1use std::cell::RefCell;
4use std::future::Future;
5use std::hash::Hash;
6use std::marker::PhantomData;
7use std::ops::Deref;
8use std::rc::Rc;
9
10use stageleft::{IntoQuotedMut, QuotedWithContext, QuotedWithContextWithProps, q, quote_type};
11#[cfg(feature = "tokio")]
12use tokio::time::Instant;
13
14use super::boundedness::{Bounded, Boundedness, IsBounded, Unbounded};
15use super::keyed_singleton::KeyedSingleton;
16use super::keyed_stream::{Generate, KeyedStream};
17use super::optional::Optional;
18use super::singleton::Singleton;
19use crate::compile::builder::{CycleId, FlowState};
20use crate::compile::ir::{
21 CollectionKind, HydroIrOpMetadata, HydroNode, HydroRoot, SharedNode, StreamOrder, StreamRetry,
22};
23#[cfg(stageleft_runtime)]
24use crate::forward_handle::{CycleCollection, CycleCollectionWithInitial, ReceiverComplete};
25use crate::forward_handle::{ForwardRef, TickCycle};
26use crate::live_collections::batch_atomic::BatchAtomic;
27use crate::live_collections::singleton::SingletonBound;
28#[cfg(stageleft_runtime)]
29use crate::location::dynamic::{DynLocation, LocationId};
30use crate::location::tick::{Atomic, DeferTick};
31use crate::location::{Location, Tick, TopLevel, check_matching_location};
32use crate::manual_expr::ManualExpr;
33use crate::nondet::{NonDet, nondet};
34use crate::prelude::manual_proof;
35use crate::properties::{
36 AggFuncAlgebra, ApplyMonotoneStream, StreamMapFuncAlgebra, ValidCommutativityFor,
37 ValidIdempotenceFor, ValidMutBorrowCommutativityFor, ValidMutBorrowIdempotenceFor,
38 ValidMutCommutativityFor, ValidMutIdempotenceFor,
39};
40
41pub mod networking;
42
43#[sealed::sealed]
45pub trait Ordering:
46 MinOrder<Self, Min = Self> + MinOrder<TotalOrder, Min = Self> + MinOrder<NoOrder, Min = NoOrder>
47{
48 const ORDERING_KIND: StreamOrder;
50}
51
52pub enum TotalOrder {}
56
57#[sealed::sealed]
58impl Ordering for TotalOrder {
59 const ORDERING_KIND: StreamOrder = StreamOrder::TotalOrder;
60}
61
62pub enum NoOrder {}
68
69#[sealed::sealed]
70impl Ordering for NoOrder {
71 const ORDERING_KIND: StreamOrder = StreamOrder::NoOrder;
72}
73
74#[sealed::sealed]
78pub trait WeakerOrderingThan<Other: ?Sized>: Ordering {}
79#[sealed::sealed]
80impl<O: Ordering, O2: Ordering> WeakerOrderingThan<O2> for O where O: MinOrder<O2, Min = O> {}
81
82#[sealed::sealed]
84pub trait MinOrder<Other: ?Sized> {
85 type Min: Ordering;
87}
88
89#[sealed::sealed]
90impl<O: Ordering> MinOrder<O> for TotalOrder {
91 type Min = O;
92}
93
94#[sealed::sealed]
95impl<O: Ordering> MinOrder<O> for NoOrder {
96 type Min = NoOrder;
97}
98
99#[sealed::sealed]
101pub trait Retries:
102 MinRetries<Self, Min = Self>
103 + MinRetries<ExactlyOnce, Min = Self>
104 + MinRetries<AtLeastOnce, Min = AtLeastOnce>
105{
106 const RETRIES_KIND: StreamRetry;
108}
109
110pub enum ExactlyOnce {}
113
114#[sealed::sealed]
115impl Retries for ExactlyOnce {
116 const RETRIES_KIND: StreamRetry = StreamRetry::ExactlyOnce;
117}
118
119pub enum AtLeastOnce {}
122
123#[sealed::sealed]
124impl Retries for AtLeastOnce {
125 const RETRIES_KIND: StreamRetry = StreamRetry::AtLeastOnce;
126}
127
128#[sealed::sealed]
132pub trait WeakerRetryThan<Other: ?Sized>: Retries {}
133#[sealed::sealed]
134impl<R: Retries, R2: Retries> WeakerRetryThan<R2> for R where R: MinRetries<R2, Min = R> {}
135
136#[sealed::sealed]
138pub trait MinRetries<Other: ?Sized> {
139 type Min: Retries + WeakerRetryThan<Self> + WeakerRetryThan<Other>;
141}
142
143#[sealed::sealed]
144impl<R: Retries> MinRetries<R> for ExactlyOnce {
145 type Min = R;
146}
147
148#[sealed::sealed]
149impl<R: Retries> MinRetries<R> for AtLeastOnce {
150 type Min = AtLeastOnce;
151}
152
153#[sealed::sealed]
154#[diagnostic::on_unimplemented(
155 message = "The input stream must be totally-ordered (`TotalOrder`), but has order `{Self}`. Strengthen the order upstream or consider a different API.",
156 label = "required here",
157 note = "To intentionally process the stream by observing a non-deterministic (shuffled) order of elements, use `.assume_ordering`. This introduces non-determinism so avoid unless necessary."
158)]
159pub trait IsOrdered: Ordering {}
161
162#[sealed::sealed]
163#[diagnostic::do_not_recommend]
164impl IsOrdered for TotalOrder {}
165
166#[sealed::sealed]
167#[diagnostic::on_unimplemented(
168 message = "The input stream must be exactly-once (`ExactlyOnce`), but has retries `{Self}`. Strengthen the retries guarantee upstream or consider a different API.",
169 label = "required here",
170 note = "To intentionally process the stream by observing non-deterministic (randomly duplicated) retries, use `.assume_retries`. This introduces non-determinism so avoid unless necessary."
171)]
172pub trait IsExactlyOnce: Retries {}
174
175#[sealed::sealed]
176#[diagnostic::do_not_recommend]
177impl IsExactlyOnce for ExactlyOnce {}
178
179pub struct Stream<
199 Type,
200 Loc,
201 Bound: Boundedness = Unbounded,
202 Order: Ordering = TotalOrder,
203 Retry: Retries = ExactlyOnce,
204> {
205 pub(crate) location: Loc,
206 pub(crate) ir_node: Rc<RefCell<HydroNode>>,
207 pub(crate) flow_state: FlowState,
208
209 _phantom: PhantomData<(Type, Loc, Bound, Order, Retry)>,
210}
211
212impl<T, L, B: Boundedness, O: Ordering, R: Retries> Drop for Stream<T, L, B, O, R> {
213 fn drop(&mut self) {
214 let ir_node = self.ir_node.replace(HydroNode::Placeholder);
215 if !matches!(ir_node, HydroNode::Placeholder) && !ir_node.is_shared_with_others() {
216 self.flow_state.borrow_mut().try_push_root(HydroRoot::Null {
217 input: Box::new(ir_node),
218 op_metadata: HydroIrOpMetadata::new(),
219 });
220 }
221 }
222}
223
224impl<'a, T, L, O: Ordering, R: Retries> From<Stream<T, L, Bounded, O, R>>
225 for Stream<T, L, Unbounded, O, R>
226where
227 L: Location<'a>,
228{
229 fn from(stream: Stream<T, L, Bounded, O, R>) -> Stream<T, L, Unbounded, O, R> {
230 let new_meta = stream
231 .location
232 .new_node_metadata(Stream::<T, L, Unbounded, O, R>::collection_kind());
233
234 let flow_state = stream.flow_state.clone();
235 Stream {
236 location: stream.location.clone(),
237 ir_node: super::tracked_ir_node(
238 &flow_state,
239 HydroNode::Cast {
240 inner: Box::new(stream.ir_node.replace(HydroNode::Placeholder)),
241 metadata: new_meta,
242 },
243 ),
244 flow_state,
245 _phantom: PhantomData,
246 }
247 }
248}
249
250impl<'a, T, L, B: Boundedness, R: Retries> From<Stream<T, L, B, TotalOrder, R>>
251 for Stream<T, L, B, NoOrder, R>
252where
253 L: Location<'a>,
254{
255 fn from(stream: Stream<T, L, B, TotalOrder, R>) -> Stream<T, L, B, NoOrder, R> {
256 stream.weaken_ordering()
257 }
258}
259
260impl<'a, T, L, B: Boundedness, O: Ordering> From<Stream<T, L, B, O, ExactlyOnce>>
261 for Stream<T, L, B, O, AtLeastOnce>
262where
263 L: Location<'a>,
264{
265 fn from(stream: Stream<T, L, B, O, ExactlyOnce>) -> Stream<T, L, B, O, AtLeastOnce> {
266 stream.weaken_retries()
267 }
268}
269
270impl<'a, T, L, O: Ordering, R: Retries> DeferTick for Stream<T, Tick<L>, Bounded, O, R>
271where
272 L: Location<'a>,
273{
274 fn defer_tick(self) -> Self {
275 Stream::defer_tick(self)
276 }
277}
278
279impl<'a, T, L, O: Ordering, R: Retries> CycleCollection<'a, TickCycle>
280 for Stream<T, Tick<L>, Bounded, O, R>
281where
282 L: Location<'a>,
283{
284 type Location = Tick<L>;
285
286 fn create_source(cycle_id: CycleId, location: Tick<L>) -> Self {
287 Stream::new(
288 location.clone(),
289 HydroNode::CycleSource {
290 cycle_id,
291 metadata: location.new_node_metadata(Self::collection_kind()),
292 },
293 )
294 }
295}
296
297impl<'a, T, L, O: Ordering, R: Retries> CycleCollectionWithInitial<'a, TickCycle>
298 for Stream<T, Tick<L>, Bounded, O, R>
299where
300 L: Location<'a>,
301{
302 type Location = Tick<L>;
303
304 fn location(&self) -> &Self::Location {
305 self.location()
306 }
307
308 fn create_source_with_initial(cycle_id: CycleId, initial: Self, location: Tick<L>) -> Self {
309 let from_previous_tick: Stream<T, Tick<L>, Bounded, O, R> = Stream::new(
310 location.clone(),
311 HydroNode::DeferTick {
312 input: Box::new(HydroNode::CycleSource {
313 cycle_id,
314 metadata: location.new_node_metadata(Self::collection_kind()),
315 }),
316 metadata: location.new_node_metadata(Self::collection_kind()),
317 },
318 );
319
320 from_previous_tick.chain(initial.filter_if(location.optional_first_tick(q!(())).is_some()))
321 }
322}
323
324impl<'a, T, L, O: Ordering, R: Retries> ReceiverComplete<'a, TickCycle>
325 for Stream<T, Tick<L>, Bounded, O, R>
326where
327 L: Location<'a>,
328{
329 fn complete(self, cycle_id: CycleId, expected_location: LocationId) {
330 assert_eq!(
331 Location::id(&self.location),
332 expected_location,
333 "locations do not match"
334 );
335 self.location
336 .flow_state()
337 .borrow_mut()
338 .push_root(HydroRoot::CycleSink {
339 cycle_id,
340 input: Box::new(self.ir_node.replace(HydroNode::Placeholder)),
341 op_metadata: HydroIrOpMetadata::new(),
342 });
343 }
344}
345
346impl<'a, T, L, B: Boundedness, O: Ordering, R: Retries> CycleCollection<'a, ForwardRef>
347 for Stream<T, L, B, O, R>
348where
349 L: Location<'a>,
350{
351 type Location = L;
352
353 fn create_source(cycle_id: CycleId, location: L) -> Self {
354 Stream::new(
355 location.clone(),
356 HydroNode::CycleSource {
357 cycle_id,
358 metadata: location.new_node_metadata(Self::collection_kind()),
359 },
360 )
361 }
362}
363
364impl<'a, T, L, B: Boundedness, O: Ordering, R: Retries> ReceiverComplete<'a, ForwardRef>
365 for Stream<T, L, B, O, R>
366where
367 L: Location<'a>,
368{
369 fn complete(self, cycle_id: CycleId, expected_location: LocationId) {
370 assert_eq!(
371 Location::id(&self.location),
372 expected_location,
373 "locations do not match"
374 );
375 self.location
376 .flow_state()
377 .borrow_mut()
378 .push_root(HydroRoot::CycleSink {
379 cycle_id,
380 input: Box::new(self.ir_node.replace(HydroNode::Placeholder)),
381 op_metadata: HydroIrOpMetadata::new(),
382 });
383 }
384}
385
386impl<'a, T, L, B: Boundedness, O: Ordering, R: Retries> Clone for Stream<T, L, B, O, R>
387where
388 T: Clone,
389 L: Location<'a>,
390{
391 fn clone(&self) -> Self {
392 if !matches!(self.ir_node.borrow().deref(), HydroNode::Tee { .. }) {
393 let orig_ir_node = self.ir_node.replace(HydroNode::Placeholder);
394 *self.ir_node.borrow_mut() = HydroNode::Tee {
395 inner: SharedNode(Rc::new(RefCell::new(orig_ir_node))),
396 metadata: self.location.new_node_metadata(Self::collection_kind()),
397 };
398 }
399
400 let HydroNode::Tee { inner, metadata } = &*self.ir_node.borrow() else {
401 unreachable!()
402 };
403 Stream {
404 location: self.location.clone(),
405 flow_state: self.flow_state.clone(),
406 ir_node: super::tracked_ir_node(
407 &self.flow_state,
408 HydroNode::Tee {
409 inner: SharedNode(inner.0.clone()),
410 metadata: metadata.clone(),
411 },
412 ),
413 _phantom: PhantomData,
414 }
415 }
416}
417
418impl<'a, T, L, B: Boundedness, O: Ordering, R: Retries> Stream<T, L, B, O, R>
419where
420 L: Location<'a>,
421{
422 pub(crate) fn new(location: L, ir_node: HydroNode) -> Self {
423 debug_assert_eq!(ir_node.metadata().location_id, Location::id(&location));
424 debug_assert_eq!(ir_node.metadata().collection_kind, Self::collection_kind());
425
426 let flow_state = location.flow_state().clone();
427 let ir_node = super::tracked_ir_node(&flow_state, ir_node);
428 Stream {
429 location,
430 flow_state,
431 ir_node,
432 _phantom: PhantomData,
433 }
434 }
435
436 pub fn location(&self) -> &L {
438 &self.location
439 }
440
441 pub fn by_ref(&self) -> crate::handoff_ref::StreamRef<'a, '_, T, L>
446 where
447 B: IsBounded,
448 {
449 crate::handoff_ref::StreamRef::new(&self.ir_node)
450 }
451
452 pub fn by_mut(&self) -> crate::handoff_ref::StreamMut<'a, '_, T, L>
455 where
456 B: IsBounded,
457 {
458 crate::handoff_ref::StreamMut::new(&self.ir_node)
459 }
460
461 pub fn weaken_consistency(self) -> Stream<T, L::DropConsistency, B, O, R>
464 where
465 L: Location<'a>,
466 {
467 if L::consistency()
468 .is_none_or(|c| c == crate::location::dynamic::ClusterConsistency::NoConsistency)
469 {
470 Stream::new(
472 self.location.drop_consistency(),
473 self.ir_node.replace(HydroNode::Placeholder),
474 )
475 } else {
476 Stream::new(
477 self.location.drop_consistency(),
478 HydroNode::Cast {
479 inner: Box::new(self.ir_node.replace(HydroNode::Placeholder)),
480 metadata: self.location.drop_consistency().new_node_metadata(Stream::<
481 T,
482 L::DropConsistency,
483 B,
484 O,
485 R,
486 >::collection_kind(
487 )),
488 },
489 )
490 }
491 }
492
493 pub fn assert_has_consistency_of<L2: Location<'a, DropConsistency = L::DropConsistency>>(
497 self,
498 _proof: impl crate::properties::ConsistencyProof,
499 ) -> Stream<T, L2, B, O, R>
500 where
501 L: Location<'a>,
502 {
503 if L::consistency() == L2::consistency() {
504 Stream::new(
505 self.location.with_consistency_of(),
506 self.ir_node.replace(HydroNode::Placeholder),
507 )
508 } else {
509 Stream::new(
510 self.location.with_consistency_of(),
511 HydroNode::AssertIsConsistent {
512 inner: Box::new(self.ir_node.replace(HydroNode::Placeholder)),
513 trusted: false,
514 metadata: self
515 .location
516 .clone()
517 .with_consistency_of::<L2>()
518 .new_node_metadata(Stream::<T, L2, B, O, R>::collection_kind()),
519 },
520 )
521 }
522 }
523
524 pub(crate) fn assert_has_consistency_of_trusted<
525 L2: Location<'a, DropConsistency = L::DropConsistency>,
526 >(
527 self,
528 _proof: impl crate::properties::ConsistencyProof,
529 ) -> Stream<T, L2, B, O, R>
530 where
531 L: Location<'a>,
532 {
533 if L::consistency() == L2::consistency() {
534 Stream::new(
535 self.location.with_consistency_of(),
536 self.ir_node.replace(HydroNode::Placeholder),
537 )
538 } else {
539 Stream::new(
540 self.location.with_consistency_of(),
541 HydroNode::AssertIsConsistent {
542 inner: Box::new(self.ir_node.replace(HydroNode::Placeholder)),
543 trusted: true,
544 metadata: self
545 .location
546 .clone()
547 .with_consistency_of::<L2>()
548 .new_node_metadata(Stream::<T, L2, B, O, R>::collection_kind()),
549 },
550 )
551 }
552 }
553
554 pub(crate) fn collection_kind() -> CollectionKind {
555 CollectionKind::Stream {
556 bound: B::BOUND_KIND,
557 order: O::ORDERING_KIND,
558 retry: R::RETRIES_KIND,
559 element_type: quote_type::<T>().into(),
560 }
561 }
562
563 pub fn map<U, F, C, I, const WAS_MUT: bool>(
583 self,
584 f: impl IntoQuotedMut<'a, F, L, StreamMapFuncAlgebra<C, I>>,
585 ) -> Stream<U, L, B, O, R>
586 where
587 F: FnMut(T) -> U + 'a,
588 C: ValidMutCommutativityFor<F, T, U, O, WAS_MUT>,
589 I: ValidMutIdempotenceFor<F, T, U, R, WAS_MUT>,
590 {
591 let f = crate::handoff_ref::with_ref_capture(|| {
592 let (expr, proof) = f.splice_fnmut1_ctx_props(&self.location);
593 proof.register_proof(&expr);
594 expr.into()
595 });
596 Stream::new(
597 self.location.clone(),
598 HydroNode::Map {
599 f,
600 input: Box::new(self.ir_node.replace(HydroNode::Placeholder)),
601 metadata: self
602 .location
603 .new_node_metadata(Stream::<U, L, B, O, R>::collection_kind()),
604 },
605 )
606 }
607
608 pub fn flat_map_ordered<U, I, F, C, Idemp, const WAS_MUT: bool>(
633 self,
634 f: impl IntoQuotedMut<'a, F, L, StreamMapFuncAlgebra<C, Idemp>>,
635 ) -> Stream<U, L, B, O, R>
636 where
637 I: IntoIterator<Item = U>,
638 F: FnMut(T) -> I + 'a,
639 C: ValidMutCommutativityFor<F, T, I, O, WAS_MUT>,
640 Idemp: ValidMutIdempotenceFor<F, T, I, R, WAS_MUT>,
641 {
642 let f = crate::handoff_ref::with_ref_capture(|| {
643 let (expr, proof) = f.splice_fnmut1_ctx_props(&self.location);
644 proof.register_proof(&expr);
645 expr.into()
646 });
647 Stream::new(
648 self.location.clone(),
649 HydroNode::FlatMap {
650 f,
651 input: Box::new(self.ir_node.replace(HydroNode::Placeholder)),
652 metadata: self
653 .location
654 .new_node_metadata(Stream::<U, L, B, O, R>::collection_kind()),
655 },
656 )
657 }
658
659 pub fn flat_map_unordered<U, I, F, C, Idemp, const WAS_MUT: bool>(
686 self,
687 f: impl IntoQuotedMut<'a, F, L, StreamMapFuncAlgebra<C, Idemp>>,
688 ) -> Stream<U, L, B, NoOrder, R>
689 where
690 I: IntoIterator<Item = U>,
691 F: FnMut(T) -> I + 'a,
692 C: ValidMutCommutativityFor<F, T, I, O, WAS_MUT>,
693 Idemp: ValidMutIdempotenceFor<F, T, I, R, WAS_MUT>,
694 {
695 let f = crate::handoff_ref::with_ref_capture(|| {
696 let (expr, proof) = f.splice_fnmut1_ctx_props(&self.location);
697 proof.register_proof(&expr);
698 expr.into()
699 });
700 Stream::new(
701 self.location.clone(),
702 HydroNode::FlatMap {
703 f,
704 input: Box::new(self.ir_node.replace(HydroNode::Placeholder)),
705 metadata: self
706 .location
707 .new_node_metadata(Stream::<U, L, B, NoOrder, R>::collection_kind()),
708 },
709 )
710 }
711
712 pub fn flatten_ordered<U>(self) -> Stream<U, L, B, O, R>
735 where
736 T: IntoIterator<Item = U>,
737 {
738 self.flat_map_ordered(q!(|d| d))
739 }
740
741 pub fn flatten_unordered<U>(self) -> Stream<U, L, B, NoOrder, R>
768 where
769 T: IntoIterator<Item = U>,
770 {
771 self.flat_map_unordered(q!(|d| d))
772 }
773
774 pub fn flat_map_stream_blocking<U, S, F, C, Idemp, const WAS_MUT: bool>(
778 self,
779 f: impl IntoQuotedMut<'a, F, L, StreamMapFuncAlgebra<C, Idemp>>,
780 ) -> Stream<U, L, B, O, R>
781 where
782 S: futures::Stream<Item = U>,
783 F: FnMut(T) -> S + 'a,
784 C: ValidMutCommutativityFor<F, T, S, O, WAS_MUT>,
785 Idemp: ValidMutIdempotenceFor<F, T, S, R, WAS_MUT>,
786 {
787 let f = crate::handoff_ref::with_ref_capture(|| {
788 let (expr, proof) = f.splice_fnmut1_ctx_props(&self.location);
789 proof.register_proof(&expr);
790 expr.into()
791 });
792 Stream::new(
793 self.location.clone(),
794 HydroNode::FlatMapStreamBlocking {
795 f,
796 input: Box::new(self.ir_node.replace(HydroNode::Placeholder)),
797 metadata: self
798 .location
799 .new_node_metadata(Stream::<U, L, B, O, R>::collection_kind()),
800 },
801 )
802 }
803
804 pub fn flatten_stream_blocking<U>(self) -> Stream<U, L, B, O, R>
808 where
809 T: futures::Stream<Item = U>,
810 {
811 self.flat_map_stream_blocking(q!(|d| d))
812 }
813
814 pub fn filter<F, C, Idemp, const WAS_MUT: bool>(
839 self,
840 f: impl IntoQuotedMut<'a, F, L, StreamMapFuncAlgebra<C, Idemp>>,
841 ) -> Self
842 where
843 F: FnMut(&T) -> bool + 'a,
844 C: ValidMutBorrowCommutativityFor<F, T, bool, O, WAS_MUT>,
845 Idemp: ValidMutBorrowIdempotenceFor<F, T, bool, R, WAS_MUT>,
846 {
847 let f = crate::handoff_ref::with_ref_capture(|| {
848 let (expr, proof) = f.splice_fnmut1_borrow_ctx_props(&self.location);
849 proof.register_proof(&expr);
850 expr.into()
851 });
852 Stream::new(
853 self.location.clone(),
854 HydroNode::Filter {
855 f,
856 input: Box::new(self.ir_node.replace(HydroNode::Placeholder)),
857 metadata: self.location.new_node_metadata(Self::collection_kind()),
858 },
859 )
860 }
861
862 pub fn partition<F, C, Idemp, const WAS_MUT: bool>(
897 self,
898 f: impl IntoQuotedMut<'a, F, L, StreamMapFuncAlgebra<C, Idemp>>,
899 ) -> (Stream<T, L, B, O, R>, Stream<T, L, B, O, R>)
900 where
901 F: FnMut(&T) -> bool + 'a,
902 C: ValidMutBorrowCommutativityFor<F, T, bool, O, WAS_MUT>,
903 Idemp: ValidMutBorrowIdempotenceFor<F, T, bool, R, WAS_MUT>,
904 {
905 let f = crate::handoff_ref::with_ref_capture(|| {
906 let (expr, proof) = f.splice_fnmut1_borrow_ctx_props(&self.location);
907 proof.register_proof(&expr);
908 expr.into()
909 });
910 let shared = SharedNode(Rc::new(RefCell::new(
911 self.ir_node.replace(HydroNode::Placeholder),
912 )));
913
914 let true_stream = Stream::new(
915 self.location.clone(),
916 HydroNode::Partition {
917 inner: SharedNode(shared.0.clone()),
918 f: f.clone(),
919 is_true: true,
920 metadata: self.location.new_node_metadata(Self::collection_kind()),
921 },
922 );
923
924 let false_stream = Stream::new(
925 self.location.clone(),
926 HydroNode::Partition {
927 inner: SharedNode(shared.0),
928 f,
929 is_true: false,
930 metadata: self.location.new_node_metadata(Self::collection_kind()),
931 },
932 );
933
934 (true_stream, false_stream)
935 }
936
937 pub fn filter_map<U, F, C, Idemp, const WAS_MUT: bool>(
957 self,
958 f: impl IntoQuotedMut<'a, F, L, StreamMapFuncAlgebra<C, Idemp>>,
959 ) -> Stream<U, L, B, O, R>
960 where
961 F: FnMut(T) -> Option<U> + 'a,
962 C: ValidMutCommutativityFor<F, T, Option<U>, O, WAS_MUT>,
963 Idemp: ValidMutIdempotenceFor<F, T, Option<U>, R, WAS_MUT>,
964 {
965 let f = crate::handoff_ref::with_ref_capture(|| {
966 let (expr, proof) = f.splice_fnmut1_ctx_props(&self.location);
967 proof.register_proof(&expr);
968 expr.into()
969 });
970 Stream::new(
971 self.location.clone(),
972 HydroNode::FilterMap {
973 f,
974 input: Box::new(self.ir_node.replace(HydroNode::Placeholder)),
975 metadata: self
976 .location
977 .new_node_metadata(Stream::<U, L, B, O, R>::collection_kind()),
978 },
979 )
980 }
981
982 pub fn cross_singleton<O2>(
1007 self,
1008 other: impl Into<Optional<O2, L, Bounded>>,
1009 ) -> Stream<(T, O2), L, B, O, R>
1010 where
1011 O2: Clone,
1012 {
1013 let other: Optional<O2, L, Bounded> = other.into();
1014 check_matching_location(&self.location, &other.location);
1015
1016 Stream::new(
1017 self.location.clone(),
1018 HydroNode::CrossSingleton {
1019 left: Box::new(self.ir_node.replace(HydroNode::Placeholder)),
1020 right: Box::new(other.ir_node.replace(HydroNode::Placeholder)),
1021 metadata: self
1022 .location
1023 .new_node_metadata(Stream::<(T, O2), L, B, O, R>::collection_kind()),
1024 },
1025 )
1026 }
1027
1028 pub fn filter_if(self, signal: Singleton<bool, L, Bounded>) -> Stream<T, L, B, O, R> {
1060 self.cross_singleton(signal.filter(q!(|b| *b)))
1061 .map(q!(|(d, _)| d))
1062 }
1063
1064 #[deprecated(note = "use `filter_if` with `Optional::is_some()` instead")]
1099 pub fn filter_if_some<U>(self, signal: Optional<U, L, Bounded>) -> Stream<T, L, B, O, R> {
1100 self.filter_if(signal.is_some())
1101 }
1102
1103 #[deprecated(note = "use `filter_if` with `!Optional::is_some()` instead")]
1138 pub fn filter_if_none<U>(self, other: Optional<U, L, Bounded>) -> Stream<T, L, B, O, R> {
1139 self.filter_if(other.is_none())
1140 }
1141
1142 pub fn cross_product<T2, B2: Boundedness, O2: Ordering, R2: Retries>(
1167 self,
1168 other: Stream<T2, L, B2, O2, R2>,
1169 ) -> Stream<(T, T2), L, B, B2::PreserveOrderIfBounded<O>, <R as MinRetries<R2>>::Min>
1170 where
1171 T: Clone,
1172 T2: Clone,
1173 R: MinRetries<R2>,
1174 {
1175 self.map(q!(|v| ((), v)))
1176 .join(other.map(q!(|v| ((), v))))
1177 .map(q!(|((), (v1, v2))| (v1, v2)))
1178 }
1179
1180 pub fn unique(self) -> Stream<T, L, B, O, ExactlyOnce>
1199 where
1200 T: Eq + Hash,
1201 {
1202 Stream::new(
1203 self.location.clone(),
1204 HydroNode::Unique {
1205 input: Box::new(self.ir_node.replace(HydroNode::Placeholder)),
1206 metadata: self
1207 .location
1208 .new_node_metadata(Stream::<T, L, B, O, ExactlyOnce>::collection_kind()),
1209 },
1210 )
1211 }
1212
1213 pub fn filter_not_in<O2: Ordering, B2>(self, other: Stream<T, L, B2, O2, R>) -> Self
1239 where
1240 T: Eq + Hash,
1241 B2: IsBounded,
1242 {
1243 check_matching_location(&self.location, &other.location);
1244
1245 Stream::new(
1246 self.location.clone(),
1247 HydroNode::Difference {
1248 pos: Box::new(self.ir_node.replace(HydroNode::Placeholder)),
1249 neg: Box::new(other.ir_node.replace(HydroNode::Placeholder)),
1250 metadata: self
1251 .location
1252 .new_node_metadata(Stream::<T, L, Bounded, O, R>::collection_kind()),
1253 },
1254 )
1255 }
1256
1257 pub fn inspect<F, C, Idemp, const WAS_MUT: bool>(
1278 self,
1279 f: impl IntoQuotedMut<'a, F, L::DropConsistency, StreamMapFuncAlgebra<C, Idemp>>,
1280 ) -> Self
1281 where
1282 F: FnMut(&T) + 'a,
1283 C: ValidMutBorrowCommutativityFor<F, T, (), O, WAS_MUT>,
1284 Idemp: ValidMutBorrowIdempotenceFor<F, T, (), R, WAS_MUT>,
1285 {
1286 let f = crate::handoff_ref::with_ref_capture(|| {
1287 let (expr, proof) = f.splice_fnmut1_borrow_ctx_props(&self.location.drop_consistency());
1288 proof.register_proof(&expr);
1289 expr.into()
1290 });
1291
1292 Stream::new(
1293 self.location.clone(),
1294 HydroNode::Inspect {
1295 f,
1296 input: Box::new(self.ir_node.replace(HydroNode::Placeholder)),
1297 metadata: self.location.new_node_metadata(Self::collection_kind()),
1298 },
1299 )
1300 }
1301
1302 pub fn for_each<F: FnMut(T) + 'a, C, I>(
1318 self,
1319 f: impl IntoQuotedMut<'a, F, L, AggFuncAlgebra<C, I>>,
1320 ) where
1321 C: ValidCommutativityFor<O>,
1322 I: ValidIdempotenceFor<R>,
1323 {
1324 let f = crate::handoff_ref::with_ref_capture(|| {
1325 let (f, proof) = f.splice_fnmut1_ctx_props(&self.location);
1326 proof.register_proof(&f);
1327 f.into()
1328 });
1329 self.location
1330 .flow_state()
1331 .borrow_mut()
1332 .push_root(HydroRoot::ForEach {
1333 input: Box::new(self.ir_node.replace(HydroNode::Placeholder)),
1334 f,
1335 op_metadata: HydroIrOpMetadata::new(),
1336 });
1337 }
1338
1339 pub fn dest_sink<S>(self, sink: impl QuotedWithContext<'a, S, L>)
1345 where
1346 O: IsOrdered,
1347 R: IsExactlyOnce,
1348 S: 'a + futures::Sink<T> + Unpin,
1349 {
1350 self.location
1351 .flow_state()
1352 .borrow_mut()
1353 .push_root(HydroRoot::DestSink {
1354 sink: sink.splice_typed_ctx(&self.location).into(),
1355 input: Box::new(self.ir_node.replace(HydroNode::Placeholder)),
1356 op_metadata: HydroIrOpMetadata::new(),
1357 });
1358 }
1359
1360 pub fn enumerate(self) -> Stream<(usize, T), L, B, O, R>
1380 where
1381 O: IsOrdered,
1382 R: IsExactlyOnce,
1383 {
1384 Stream::new(
1385 self.location.clone(),
1386 HydroNode::Enumerate {
1387 input: Box::new(self.ir_node.replace(HydroNode::Placeholder)),
1388 metadata: self.location.new_node_metadata(Stream::<
1389 (usize, T),
1390 L,
1391 B,
1392 TotalOrder,
1393 ExactlyOnce,
1394 >::collection_kind()),
1395 },
1396 )
1397 }
1398
1399 pub fn fold<A, I, F, C, Idemp, M, B2: SingletonBound>(
1423 self,
1424 init: impl IntoQuotedMut<'a, I, L>,
1425 comb: impl IntoQuotedMut<'a, F, L, AggFuncAlgebra<C, Idemp, M>>,
1426 ) -> Singleton<A, L, B2>
1427 where
1428 I: Fn() -> A + 'a,
1429 F: 'a + Fn(&mut A, T),
1430 C: ValidCommutativityFor<O>,
1431 Idemp: ValidIdempotenceFor<R>,
1432 B: ApplyMonotoneStream<M, B2>,
1433 {
1434 let init = init.splice_fn0_ctx(&self.location).into();
1435 let (comb, proof) = comb.splice_fn2_borrow_mut_ctx_props(&self.location);
1436 proof.register_proof(&comb);
1437
1438 let nondet = nondet!();
1441 let retried: Stream<T, L::DropConsistency, B, O, ExactlyOnce> = self.assume_retries(nondet);
1442
1443 let core = HydroNode::Fold {
1444 init,
1445 acc: comb.into(),
1446 input: Box::new(retried.ir_node.replace(HydroNode::Placeholder)),
1447 metadata: retried
1448 .location
1449 .new_node_metadata(Singleton::<A, L::DropConsistency, B2>::collection_kind()),
1450 };
1455
1456 Singleton::new(retried.location.clone(), core)
1457 .assert_has_consistency_of(manual_proof!())
1458 }
1459
1460 pub fn reduce<F, C, Idemp>(
1483 self,
1484 comb: impl IntoQuotedMut<'a, F, L, AggFuncAlgebra<C, Idemp>>,
1485 ) -> Optional<T, L, B>
1486 where
1487 F: Fn(&mut T, T) + 'a,
1488 C: ValidCommutativityFor<O>,
1489 Idemp: ValidIdempotenceFor<R>,
1490 {
1491 let (f, proof) = comb.splice_fn2_borrow_mut_ctx_props(&self.location);
1492 proof.register_proof(&f);
1493
1494 let nondet = nondet!();
1495 let ordered_etc: Stream<T, L::DropConsistency, B> =
1496 self.assume_retries(nondet).assume_ordering(nondet);
1497
1498 let core = HydroNode::Reduce {
1499 f: f.into(),
1500 input: Box::new(ordered_etc.ir_node.replace(HydroNode::Placeholder)),
1501 metadata: ordered_etc
1502 .location
1503 .new_node_metadata(Optional::<T, L::DropConsistency, B>::collection_kind()),
1504 };
1505
1506 Optional::new(ordered_etc.location.clone(), core)
1507 .assert_has_consistency_of(manual_proof!())
1508 }
1509
1510 pub fn max(self) -> Optional<T, L, B>
1530 where
1531 T: Ord,
1532 {
1533 self.assume_retries_trusted::<ExactlyOnce>(nondet!())
1534 .assume_ordering_trusted_bounded::<TotalOrder>(
1535 nondet!(),
1536 )
1537 .reduce(q!(|curr, new| {
1538 if new > *curr {
1539 *curr = new;
1540 }
1541 }))
1542 }
1543
1544 pub fn min(self) -> Optional<T, L, B>
1564 where
1565 T: Ord,
1566 {
1567 self.assume_retries_trusted::<ExactlyOnce>(nondet!())
1568 .assume_ordering_trusted_bounded::<TotalOrder>(
1569 nondet!(),
1570 )
1571 .reduce(q!(|curr, new| {
1572 if new < *curr {
1573 *curr = new;
1574 }
1575 }))
1576 }
1577
1578 pub fn first(self) -> Optional<T, L, B>
1601 where
1602 O: IsOrdered,
1603 {
1604 self.make_totally_ordered()
1605 .assume_retries_trusted::<ExactlyOnce>(nondet!())
1606 .generator(q!(|| ()), q!(|_, item| Generate::Return(item)))
1607 .reduce(q!(|_, _| {}))
1608 }
1609
1610 pub fn last(self) -> Optional<T, L, B>
1633 where
1634 O: IsOrdered,
1635 {
1636 self.make_totally_ordered()
1637 .assume_retries_trusted::<ExactlyOnce>(nondet!())
1638 .reduce(q!(|curr, new| *curr = new))
1639 }
1640
1641 pub fn limit(
1664 self,
1665 n: impl QuotedWithContext<'a, usize, L> + Copy + 'a,
1666 ) -> Stream<T, L, B, TotalOrder, ExactlyOnce>
1667 where
1668 O: IsOrdered,
1669 R: IsExactlyOnce,
1670 {
1671 self.generator(
1672 q!(|| 0usize),
1673 q!(move |count, item| {
1674 if *count == n {
1675 Generate::Break
1676 } else {
1677 *count += 1;
1678 if *count == n {
1679 Generate::Return(item)
1680 } else {
1681 Generate::Yield(item)
1682 }
1683 }
1684 }),
1685 )
1686 }
1687
1688 pub fn collect_vec(self) -> Singleton<Vec<T>, L, B>
1714 where
1715 O: IsOrdered,
1716 R: IsExactlyOnce,
1717 {
1718 self.make_totally_ordered().make_exactly_once().fold(
1719 q!(|| vec![]),
1720 q!(|acc, v| {
1721 acc.push(v);
1722 }),
1723 )
1724 }
1725
1726 pub fn scan<A, U, I, F>(
1790 self,
1791 init: impl IntoQuotedMut<'a, I, L>,
1792 f: impl IntoQuotedMut<'a, F, L>,
1793 ) -> Stream<U, L, B, TotalOrder, ExactlyOnce>
1794 where
1795 O: IsOrdered,
1796 R: IsExactlyOnce,
1797 I: Fn() -> A + 'a,
1798 F: Fn(&mut A, T) -> Option<U> + 'a,
1799 {
1800 let init =
1801 crate::handoff_ref::with_ref_capture(|| init.splice_fn0_ctx(&self.location).into());
1802 let f = crate::handoff_ref::with_ref_capture(|| {
1803 f.splice_fn2_borrow_mut_ctx(&self.location).into()
1804 });
1805
1806 Stream::new(
1807 self.location.clone(),
1808 HydroNode::Scan {
1809 init,
1810 acc: f,
1811 input: Box::new(self.ir_node.replace(HydroNode::Placeholder)),
1812 metadata: self.location.new_node_metadata(
1813 Stream::<U, L, B, TotalOrder, ExactlyOnce>::collection_kind(),
1814 ),
1815 },
1816 )
1817 }
1818
1819 pub fn scan_async_blocking<A, U, I, F, Fut>(
1856 self,
1857 init: impl IntoQuotedMut<'a, I, L>,
1858 f: impl IntoQuotedMut<'a, F, L>,
1859 ) -> Stream<U, L, B, TotalOrder, ExactlyOnce>
1860 where
1861 O: IsOrdered,
1862 R: IsExactlyOnce,
1863 I: Fn() -> A + 'a,
1864 F: Fn(&mut A, T) -> Fut + 'a,
1865 Fut: Future<Output = Option<U>> + 'a,
1866 {
1867 let init =
1868 crate::handoff_ref::with_ref_capture(|| init.splice_fn0_ctx(&self.location).into());
1869 let f = crate::handoff_ref::with_ref_capture(|| {
1870 f.splice_fn2_borrow_mut_ctx(&self.location).into()
1871 });
1872
1873 Stream::new(
1874 self.location.clone(),
1875 HydroNode::ScanAsyncBlocking {
1876 init,
1877 acc: f,
1878 input: Box::new(self.ir_node.replace(HydroNode::Placeholder)),
1879 metadata: self.location.new_node_metadata(
1880 Stream::<U, L, B, TotalOrder, ExactlyOnce>::collection_kind(),
1881 ),
1882 },
1883 )
1884 }
1885
1886 pub fn generator<A, U, I, F>(
1929 self,
1930 init: impl IntoQuotedMut<'a, I, L> + Copy,
1931 f: impl IntoQuotedMut<'a, F, L> + Copy,
1932 ) -> Stream<U, L, B, TotalOrder, ExactlyOnce>
1933 where
1934 O: IsOrdered,
1935 R: IsExactlyOnce,
1936 I: Fn() -> A + 'a,
1937 F: Fn(&mut A, T) -> Generate<U> + 'a,
1938 {
1939 let init: ManualExpr<I, _> = ManualExpr::new(move |ctx: &L| init.splice_fn0_ctx(ctx));
1940 let f: ManualExpr<F, _> = ManualExpr::new(move |ctx: &L| f.splice_fn2_borrow_mut_ctx(ctx));
1941
1942 let this = self.make_totally_ordered().make_exactly_once();
1943
1944 let scan_init = crate::handoff_ref::with_ref_capture(|| {
1949 q!(|| None)
1950 .splice_fn0_ctx::<Option<Option<A>>>(&this.location)
1951 .into()
1952 });
1953 let scan_f = crate::handoff_ref::with_ref_capture(|| {
1954 q!(move |state: &mut Option<Option<_>>, v| {
1955 if state.is_none() {
1956 *state = Some(Some(init()));
1957 }
1958 match state {
1959 Some(Some(state_value)) => match f(state_value, v) {
1960 Generate::Yield(out) => Some(Some(out)),
1961 Generate::Return(out) => {
1962 *state = Some(None);
1963 Some(Some(out))
1964 }
1965 Generate::Break => None,
1969 Generate::Continue => Some(None),
1970 },
1971 _ => None,
1973 }
1974 })
1975 .splice_fn2_borrow_mut_ctx::<Option<Option<A>>, T, _>(&this.location)
1976 .into()
1977 });
1978
1979 let scan_node = HydroNode::Scan {
1980 init: scan_init,
1981 acc: scan_f,
1982 input: Box::new(this.ir_node.replace(HydroNode::Placeholder)),
1983 metadata: this.location.new_node_metadata(Stream::<
1984 Option<U>,
1985 L,
1986 B,
1987 TotalOrder,
1988 ExactlyOnce,
1989 >::collection_kind()),
1990 };
1991
1992 let flatten_f = q!(|d| d)
1993 .splice_fn1_ctx::<Option<U>, _>(&this.location)
1994 .into();
1995 let flatten_node = HydroNode::FlatMap {
1996 f: flatten_f,
1997 input: Box::new(scan_node),
1998 metadata: this
1999 .location
2000 .new_node_metadata(Stream::<U, L, B, TotalOrder, ExactlyOnce>::collection_kind()),
2001 };
2002
2003 Stream::new(this.location.clone(), flatten_node)
2004 }
2005
2006 #[cfg(feature = "tokio")]
2015 pub fn sample_every(
2016 self,
2017 interval: impl QuotedWithContext<'a, std::time::Duration, L> + Copy + 'a,
2018 nondet: NonDet,
2019 ) -> Stream<T, L::DropConsistency, Unbounded, O, AtLeastOnce>
2020 where
2021 L: TopLevel<'a>,
2022 {
2023 let samples = self.location.source_interval(interval);
2024
2025 let tick = self.location.tick();
2026 self.batch(&tick, nondet)
2027 .filter_if(samples.batch(&tick, nondet).first().is_some())
2028 .all_ticks()
2029 .weaken_retries()
2030 }
2031
2032 #[cfg(feature = "tokio")]
2042 pub fn timeout(
2043 self,
2044 duration: impl QuotedWithContext<'a, std::time::Duration, Tick<L::DropConsistency>> + Copy + 'a,
2045 nondet: NonDet,
2046 ) -> Optional<(), L::DropConsistency, Unbounded>
2047 where
2048 L: TopLevel<'a>,
2049 {
2050 let tick = self.location.tick();
2051
2052 let latest_received = self.assume_retries::<ExactlyOnce>(nondet).fold(
2053 q!(|| None),
2054 q!(
2055 |latest, _| {
2056 *latest = Some(Instant::now());
2057 },
2058 commutative = manual_proof!()
2059 ),
2060 );
2061
2062 latest_received
2063 .snapshot(&tick, nondet)
2064 .filter_map(q!(move |latest_received| {
2065 if let Some(latest_received) = latest_received {
2066 if Instant::now().duration_since(latest_received) > duration {
2067 Some(())
2068 } else {
2069 None
2070 }
2071 } else {
2072 Some(())
2073 }
2074 }))
2075 .latest()
2076 }
2077
2078 pub fn atomic(self) -> Stream<T, Atomic<L>, B, O, R> {
2084 let id = self.location.flow_state().borrow_mut().next_clock_id();
2085 let out_location = Atomic {
2086 tick: Tick {
2087 id,
2088 l: self.location.clone(),
2089 },
2090 };
2091 Stream::new(
2092 out_location.clone(),
2093 HydroNode::BeginAtomic {
2094 inner: Box::new(self.ir_node.replace(HydroNode::Placeholder)),
2095 metadata: out_location
2096 .new_node_metadata(Stream::<T, Atomic<L>, B, O, R>::collection_kind()),
2097 },
2098 )
2099 }
2100
2101 pub fn batch<L2: Location<'a, DropConsistency = L::DropConsistency>>(
2109 self,
2110 tick: &Tick<L2>,
2111 _nondet: NonDet,
2112 ) -> Stream<T, Tick<L::DropConsistency>, Bounded, O, R> {
2113 assert_eq!(Location::id(tick.outer()), Location::id(&self.location));
2114 Stream::new(
2115 tick.drop_consistency(),
2116 HydroNode::Batch {
2117 inner: Box::new(self.ir_node.replace(HydroNode::Placeholder)),
2118 metadata: tick
2119 .new_node_metadata(Stream::<T, Tick<L>, Bounded, O, R>::collection_kind()),
2120 },
2121 )
2122 }
2123
2124 pub fn ir_node_named(self, name: &str) -> Stream<T, L, B, O, R> {
2127 {
2128 let mut node = self.ir_node.borrow_mut();
2129 let metadata = node.metadata_mut();
2130 metadata.tag = Some(name.to_owned());
2131 }
2132 self
2133 }
2134
2135 pub(crate) fn cast_at_most_one_element(self) -> Optional<T, L, B>
2139 where
2140 B: IsBounded,
2141 {
2142 Optional::new(
2143 self.location.clone(),
2144 HydroNode::Cast {
2145 inner: Box::new(self.ir_node.replace(HydroNode::Placeholder)),
2146 metadata: self
2147 .location
2148 .new_node_metadata(Optional::<T, L, B>::collection_kind()),
2149 },
2150 )
2151 }
2152
2153 pub(crate) fn use_ordering_type<O2: Ordering>(self) -> Stream<T, L, B, O2, R> {
2154 if O::ORDERING_KIND == O2::ORDERING_KIND {
2155 Stream::new(
2156 self.location.clone(),
2157 self.ir_node.replace(HydroNode::Placeholder),
2158 )
2159 } else {
2160 panic!(
2161 "Runtime ordering {:?} did not match requested cast {:?}.",
2162 O::ORDERING_KIND,
2163 O2::ORDERING_KIND
2164 )
2165 }
2166 }
2167
2168 pub fn assume_ordering<O2: Ordering>(
2177 self,
2178 _nondet: NonDet,
2179 ) -> Stream<T, L::DropConsistency, B, O2, R> {
2180 if O::ORDERING_KIND == O2::ORDERING_KIND {
2181 self.use_ordering_type().weaken_consistency()
2182 } else if O2::ORDERING_KIND == StreamOrder::NoOrder {
2183 let target_location = self.location().drop_consistency();
2185 Stream::new(
2186 target_location.clone(),
2187 HydroNode::Cast {
2188 inner: Box::new(self.ir_node.replace(HydroNode::Placeholder)),
2189 metadata: target_location
2190 .new_node_metadata(Stream::<T, L, B, O2, R>::collection_kind()),
2191 },
2192 )
2193 } else {
2194 let target_location = self.location().drop_consistency();
2195 Stream::new(
2196 target_location.clone(),
2197 HydroNode::ObserveNonDet {
2198 inner: Box::new(self.ir_node.replace(HydroNode::Placeholder)),
2199 trusted: false,
2200 metadata: target_location
2201 .new_node_metadata(Stream::<T, L, B, O2, R>::collection_kind()),
2202 },
2203 )
2204 }
2205 }
2206
2207 fn assume_ordering_trusted_bounded<O2: Ordering>(
2210 self,
2211 nondet: NonDet,
2212 ) -> Stream<T, L, B, O2, R> {
2213 if B::BOUNDED {
2214 self.assume_ordering_trusted(nondet)
2215 } else {
2216 let self_location = self.location.clone();
2217 let inner: Stream<T, L::DropConsistency, B, O2, R> = self.assume_ordering(nondet);
2218 Stream::new(self_location, inner.ir_node.replace(HydroNode::Placeholder))
2219 }
2220 }
2221
2222 pub(crate) fn assume_ordering_trusted<O2: Ordering>(
2225 self,
2226 _nondet: NonDet,
2227 ) -> Stream<T, L, B, O2, R> {
2228 if O::ORDERING_KIND == O2::ORDERING_KIND {
2229 self.use_ordering_type()
2230 } else if O2::ORDERING_KIND == StreamOrder::NoOrder {
2231 Stream::new(
2233 self.location.clone(),
2234 HydroNode::Cast {
2235 inner: Box::new(self.ir_node.replace(HydroNode::Placeholder)),
2236 metadata: self
2237 .location
2238 .new_node_metadata(Stream::<T, L, B, O2, R>::collection_kind()),
2239 },
2240 )
2241 } else {
2242 Stream::new(
2243 self.location.clone(),
2244 HydroNode::ObserveNonDet {
2245 inner: Box::new(self.ir_node.replace(HydroNode::Placeholder)),
2246 trusted: true,
2247 metadata: self
2248 .location
2249 .new_node_metadata(Stream::<T, L, B, O2, R>::collection_kind()),
2250 },
2251 )
2252 }
2253 }
2254
2255 #[deprecated = "use `weaken_ordering::<NoOrder>()` instead"]
2256 pub fn weakest_ordering(self) -> Stream<T, L, B, NoOrder, R> {
2259 self.weaken_ordering::<NoOrder>()
2260 }
2261
2262 pub fn weaken_ordering<O2: WeakerOrderingThan<O>>(self) -> Stream<T, L, B, O2, R> {
2265 let nondet = nondet!();
2266 self.assume_ordering_trusted::<O2>(nondet)
2267 }
2268
2269 pub fn make_totally_ordered(self) -> Stream<T, L, B, TotalOrder, R>
2272 where
2273 O: IsOrdered,
2274 {
2275 self.assume_ordering_trusted(nondet!())
2276 }
2277
2278 pub fn assume_retries<R2: Retries>(
2287 self,
2288 _nondet: NonDet,
2289 ) -> Stream<T, L::DropConsistency, B, O, R2> {
2290 if R::RETRIES_KIND == R2::RETRIES_KIND {
2291 Stream::new(
2292 self.location.drop_consistency(),
2293 self.ir_node.replace(HydroNode::Placeholder),
2294 )
2295 } else if R2::RETRIES_KIND == StreamRetry::AtLeastOnce {
2296 let target_location = self.location.drop_consistency();
2298 Stream::new(
2299 target_location.clone(),
2300 HydroNode::Cast {
2301 inner: Box::new(self.ir_node.replace(HydroNode::Placeholder)),
2302 metadata: target_location
2303 .new_node_metadata(Stream::<T, L, B, O, R2>::collection_kind()),
2304 },
2305 )
2306 } else {
2307 let target_location = self.location.drop_consistency();
2308 Stream::new(
2309 target_location.clone(),
2310 HydroNode::ObserveNonDet {
2311 inner: Box::new(self.ir_node.replace(HydroNode::Placeholder)),
2312 trusted: false,
2313 metadata: target_location
2314 .new_node_metadata(Stream::<T, L, B, O, R2>::collection_kind()),
2315 },
2316 )
2317 }
2318 }
2319
2320 fn assume_retries_trusted<R2: Retries>(self, _nondet: NonDet) -> Stream<T, L, B, O, R2> {
2323 if R::RETRIES_KIND == R2::RETRIES_KIND {
2324 Stream::new(
2325 self.location.clone(),
2326 self.ir_node.replace(HydroNode::Placeholder),
2327 )
2328 } else if R2::RETRIES_KIND == StreamRetry::AtLeastOnce {
2329 Stream::new(
2331 self.location.clone(),
2332 HydroNode::Cast {
2333 inner: Box::new(self.ir_node.replace(HydroNode::Placeholder)),
2334 metadata: self
2335 .location
2336 .new_node_metadata(Stream::<T, L, B, O, R2>::collection_kind()),
2337 },
2338 )
2339 } else {
2340 Stream::new(
2341 self.location.clone(),
2342 HydroNode::ObserveNonDet {
2343 inner: Box::new(self.ir_node.replace(HydroNode::Placeholder)),
2344 trusted: true,
2345 metadata: self
2346 .location
2347 .new_node_metadata(Stream::<T, L, B, O, R2>::collection_kind()),
2348 },
2349 )
2350 }
2351 }
2352
2353 #[deprecated = "use `weaken_retries::<AtLeastOnce>()` instead"]
2354 pub fn weakest_retries(self) -> Stream<T, L, B, O, AtLeastOnce> {
2357 self.weaken_retries::<AtLeastOnce>()
2358 }
2359
2360 pub fn weaken_retries<R2: WeakerRetryThan<R>>(self) -> Stream<T, L, B, O, R2> {
2363 let nondet = nondet!();
2364 self.assume_retries_trusted::<R2>(nondet)
2365 }
2366
2367 pub fn make_exactly_once(self) -> Stream<T, L, B, O, ExactlyOnce>
2370 where
2371 R: IsExactlyOnce,
2372 {
2373 self.assume_retries_trusted(nondet!())
2374 }
2375
2376 pub fn make_bounded(self) -> Stream<T, L, Bounded, O, R>
2379 where
2380 B: IsBounded,
2381 {
2382 self.weaken_boundedness()
2383 }
2384
2385 pub fn weaken_boundedness<B2: Boundedness>(self) -> Stream<T, L, B2, O, R> {
2388 if B::BOUNDED == B2::BOUNDED {
2389 Stream::new(
2390 self.location.clone(),
2391 self.ir_node.replace(HydroNode::Placeholder),
2392 )
2393 } else {
2394 Stream::new(
2396 self.location.clone(),
2397 HydroNode::Cast {
2398 inner: Box::new(self.ir_node.replace(HydroNode::Placeholder)),
2399 metadata: self
2400 .location
2401 .new_node_metadata(Stream::<T, L, B2, O, R>::collection_kind()),
2402 },
2403 )
2404 }
2405 }
2406}
2407
2408impl<'a, T, L, B: Boundedness, O: Ordering, R: Retries> Stream<&T, L, B, O, R>
2409where
2410 L: Location<'a>,
2411{
2412 pub fn cloned(self) -> Stream<T, L, B, O, R>
2430 where
2431 T: Clone,
2432 {
2433 self.map(q!(|d| d.clone()))
2434 }
2435}
2436
2437impl<'a, T, L, B: Boundedness, O: Ordering> Stream<T, L, B, O, ExactlyOnce>
2438where
2439 L: Location<'a>,
2440{
2441 pub fn count(self) -> Singleton<usize, L, B::StreamToMonotone> {
2460 self.assume_ordering_trusted::<TotalOrder>(nondet!(
2461 ))
2463 .fold(
2464 q!(|| 0usize),
2465 q!(
2466 |count, _| *count += 1,
2467 monotone = manual_proof!()
2468 ),
2469 )
2470 }
2471}
2472
2473impl<'a, T, L: Location<'a>, O: Ordering, R: Retries> Stream<T, L, Unbounded, O, R> {
2474 pub fn merge_unordered<O2: Ordering, R2: Retries>(
2498 self,
2499 other: Stream<T, L, Unbounded, O2, R2>,
2500 ) -> Stream<T, L, Unbounded, NoOrder, <R as MinRetries<R2>>::Min>
2501 where
2502 R: MinRetries<R2>,
2503 {
2504 Stream::new(
2505 self.location.clone(),
2506 HydroNode::Chain {
2507 first: Box::new(self.ir_node.replace(HydroNode::Placeholder)),
2508 second: Box::new(other.ir_node.replace(HydroNode::Placeholder)),
2509 metadata: self.location.new_node_metadata(Stream::<
2510 T,
2511 L,
2512 Unbounded,
2513 NoOrder,
2514 <R as MinRetries<R2>>::Min,
2515 >::collection_kind()),
2516 },
2517 )
2518 }
2519
2520 #[deprecated(note = "use `merge_unordered` instead")]
2522 pub fn interleave<O2: Ordering, R2: Retries>(
2523 self,
2524 other: Stream<T, L, Unbounded, O2, R2>,
2525 ) -> Stream<T, L, Unbounded, NoOrder, <R as MinRetries<R2>>::Min>
2526 where
2527 R: MinRetries<R2>,
2528 {
2529 self.merge_unordered(other)
2530 }
2531}
2532
2533impl<'a, T, L: Location<'a>, B: Boundedness, R: Retries> Stream<T, L, B, TotalOrder, R> {
2534 pub fn merge_ordered<R2: Retries>(
2562 self,
2563 other: Stream<T, L, B, TotalOrder, R2>,
2564 _nondet: NonDet,
2565 ) -> Stream<T, L::DropConsistency, B, TotalOrder, <R as MinRetries<R2>>::Min>
2566 where
2567 R: MinRetries<R2>,
2568 {
2569 let target_location = self.location().drop_consistency();
2570 Stream::new(
2571 target_location.clone(),
2572 HydroNode::MergeOrdered {
2573 first: Box::new(self.ir_node.replace(HydroNode::Placeholder)),
2574 second: Box::new(other.ir_node.replace(HydroNode::Placeholder)),
2575 metadata: target_location.new_node_metadata(Stream::<
2576 T,
2577 L::DropConsistency,
2578 B,
2579 TotalOrder,
2580 <R as MinRetries<R2>>::Min,
2581 >::collection_kind()),
2582 },
2583 )
2584 }
2585}
2586
2587impl<'a, T, L, B: Boundedness, O: Ordering, R: Retries> Stream<T, L, B, O, R>
2588where
2589 L: Location<'a>,
2590{
2591 pub fn sort(self) -> Stream<T, L, Bounded, TotalOrder, R>
2617 where
2618 B: IsBounded,
2619 T: Ord,
2620 {
2621 let this = self.make_bounded();
2622 Stream::new(
2623 this.location.clone(),
2624 HydroNode::Sort {
2625 input: Box::new(this.ir_node.replace(HydroNode::Placeholder)),
2626 metadata: this
2627 .location
2628 .new_node_metadata(Stream::<T, L, Bounded, TotalOrder, R>::collection_kind()),
2629 },
2630 )
2631 }
2632
2633 pub fn chain<O2: Ordering, R2: Retries, B2: Boundedness>(
2661 self,
2662 other: Stream<T, L, B2, O2, R2>,
2663 ) -> Stream<T, L, B2, <O as MinOrder<O2>>::Min, <R as MinRetries<R2>>::Min>
2664 where
2665 B: IsBounded,
2666 O: MinOrder<O2>,
2667 R: MinRetries<R2>,
2668 {
2669 check_matching_location(&self.location, &other.location);
2670
2671 Stream::new(
2672 self.location.clone(),
2673 HydroNode::Chain {
2674 first: Box::new(self.ir_node.replace(HydroNode::Placeholder)),
2675 second: Box::new(other.ir_node.replace(HydroNode::Placeholder)),
2676 metadata: self.location.new_node_metadata(Stream::<
2677 T,
2678 L,
2679 B2,
2680 <O as MinOrder<O2>>::Min,
2681 <R as MinRetries<R2>>::Min,
2682 >::collection_kind()),
2683 },
2684 )
2685 }
2686
2687 pub fn cross_product_nested_loop<T2, O2: Ordering + MinOrder<O>, R2: Retries>(
2691 self,
2692 other: Stream<T2, L, Bounded, O2, R2>,
2693 ) -> Stream<(T, T2), L, Bounded, <O2 as MinOrder<O>>::Min, <R as MinRetries<R2>>::Min>
2694 where
2695 B: IsBounded,
2696 T: Clone,
2697 T2: Clone,
2698 R: MinRetries<R2>,
2699 {
2700 let this = self.make_bounded();
2701 check_matching_location(&this.location, &other.location);
2702
2703 Stream::new(
2704 this.location.clone(),
2705 HydroNode::CrossProduct {
2706 left: Box::new(this.ir_node.replace(HydroNode::Placeholder)),
2707 right: Box::new(other.ir_node.replace(HydroNode::Placeholder)),
2708 metadata: this.location.new_node_metadata(Stream::<
2709 (T, T2),
2710 L,
2711 Bounded,
2712 <O2 as MinOrder<O>>::Min,
2713 <R as MinRetries<R2>>::Min,
2714 >::collection_kind()),
2715 },
2716 )
2717 }
2718
2719 pub fn repeat_with_keys<K, V2>(
2757 self,
2758 keys: KeyedSingleton<K, V2, L, Bounded>,
2759 ) -> KeyedStream<K, T, L, Bounded, O, R>
2760 where
2761 B: IsBounded,
2762 K: Clone,
2763 T: Clone,
2764 {
2765 keys.keys()
2766 .assume_ordering_trusted::<TotalOrder>(
2767 nondet!(),
2768 )
2769 .cross_product_nested_loop(self.make_bounded())
2770 .into_keyed()
2771 }
2772
2773 pub fn resolve_futures_blocking(self) -> Stream<T::Output, L, B, NoOrder, R>
2810 where
2811 T: Future,
2812 {
2813 Stream::new(
2814 self.location.clone(),
2815 HydroNode::ResolveFuturesBlocking {
2816 input: Box::new(self.ir_node.replace(HydroNode::Placeholder)),
2817 metadata: self
2818 .location
2819 .new_node_metadata(Stream::<T::Output, L, B, NoOrder, R>::collection_kind()),
2820 },
2821 )
2822 }
2823
2824 #[expect(clippy::wrong_self_convention, reason = "stream function naming")]
2844 pub fn is_empty(self) -> Singleton<bool, L, Bounded>
2845 where
2846 B: IsBounded,
2847 {
2848 self.make_bounded()
2849 .assume_ordering_trusted::<TotalOrder>(
2850 nondet!(),
2851 )
2852 .first()
2853 .is_none()
2854 }
2855}
2856
2857impl<'a, K, V1, L, B: Boundedness, O: Ordering, R: Retries> Stream<(K, V1), L, B, O, R>
2858where
2859 L: Location<'a>,
2860{
2861 pub fn join<V2, B2: Boundedness, O2: Ordering, R2: Retries>(
2886 self,
2887 n: Stream<(K, V2), L, B2, O2, R2>,
2888 ) -> Stream<(K, (V1, V2)), L, B, B2::PreserveOrderIfBounded<O>, <R as MinRetries<R2>>::Min>
2889 where
2890 K: Eq + Hash + Clone,
2891 R: MinRetries<R2>,
2892 V1: Clone,
2893 V2: Clone,
2894 {
2895 check_matching_location(&self.location, &n.location);
2896
2897 let ir_node = if B2::BOUNDED {
2898 HydroNode::JoinHalf {
2899 left: Box::new(self.ir_node.replace(HydroNode::Placeholder)),
2900 right: Box::new(n.ir_node.replace(HydroNode::Placeholder)),
2901 metadata: self.location.new_node_metadata(Stream::<
2902 (K, (V1, V2)),
2903 L,
2904 B,
2905 B2::PreserveOrderIfBounded<O>,
2906 <R as MinRetries<R2>>::Min,
2907 >::collection_kind()),
2908 }
2909 } else {
2910 HydroNode::Join {
2911 left: Box::new(self.ir_node.replace(HydroNode::Placeholder)),
2912 right: Box::new(n.ir_node.replace(HydroNode::Placeholder)),
2913 metadata: self.location.new_node_metadata(Stream::<
2914 (K, (V1, V2)),
2915 L,
2916 B,
2917 B2::PreserveOrderIfBounded<O>,
2918 <R as MinRetries<R2>>::Min,
2919 >::collection_kind()),
2920 }
2921 };
2922
2923 Stream::new(self.location.clone(), ir_node)
2924 }
2925
2926 pub fn anti_join<O2: Ordering, R2: Retries>(
2952 self,
2953 n: Stream<K, L, Bounded, O2, R2>,
2954 ) -> Stream<(K, V1), L, B, O, R>
2955 where
2956 K: Eq + Hash,
2957 {
2958 check_matching_location(&self.location, &n.location);
2959
2960 Stream::new(
2961 self.location.clone(),
2962 HydroNode::AntiJoin {
2963 pos: Box::new(self.ir_node.replace(HydroNode::Placeholder)),
2964 neg: Box::new(n.ir_node.replace(HydroNode::Placeholder)),
2965 metadata: self
2966 .location
2967 .new_node_metadata(Stream::<(K, V1), L, B, O, R>::collection_kind()),
2968 },
2969 )
2970 }
2971}
2972
2973impl<'a, K, V, L: Location<'a>, B: Boundedness, O: Ordering, R: Retries>
2974 Stream<(K, V), L, B, O, R>
2975{
2976 pub fn into_keyed(self) -> KeyedStream<K, V, L, B, O, R> {
3003 KeyedStream::new(
3004 self.location.clone(),
3005 HydroNode::Cast {
3006 inner: Box::new(self.ir_node.replace(HydroNode::Placeholder)),
3007 metadata: self
3008 .location
3009 .new_node_metadata(KeyedStream::<K, V, L, B, O, R>::collection_kind()),
3010 },
3011 )
3012 }
3013}
3014
3015impl<'a, K, V, L, O: Ordering, R: Retries> Stream<(K, V), Tick<L>, Bounded, O, R>
3016where
3017 K: Eq + Hash,
3018 L: Location<'a>,
3019{
3020 pub fn keys(self) -> Stream<K, Tick<L>, Bounded, NoOrder, ExactlyOnce> {
3039 self.into_keyed()
3040 .fold(
3041 q!(|| ()),
3042 q!(
3043 |_, _| {},
3044 commutative = manual_proof!(),
3045 idempotent = manual_proof!()
3046 ),
3047 )
3048 .keys()
3049 }
3050}
3051
3052impl<'a, T, L, B: Boundedness, O: Ordering, R: Retries> Stream<T, Atomic<L>, B, O, R>
3053where
3054 L: Location<'a>,
3055{
3056 pub fn batch_atomic<L2: Location<'a, DropConsistency = L::DropConsistency>>(
3063 self,
3064 tick: &Tick<L2>,
3065 _nondet: NonDet,
3066 ) -> Stream<T, Tick<L::DropConsistency>, Bounded, O, R> {
3067 Stream::new(
3068 tick.drop_consistency(),
3069 HydroNode::Batch {
3070 inner: Box::new(self.ir_node.replace(HydroNode::Placeholder)),
3071 metadata: tick
3072 .new_node_metadata(Stream::<T, Tick<L>, Bounded, O, R>::collection_kind()),
3073 },
3074 )
3075 }
3076
3077 pub fn end_atomic(self) -> Stream<T, L, B, O, R> {
3080 Stream::new(
3081 self.location.tick.l.clone(),
3082 HydroNode::EndAtomic {
3083 inner: Box::new(self.ir_node.replace(HydroNode::Placeholder)),
3084 metadata: self
3085 .location
3086 .tick
3087 .l
3088 .new_node_metadata(Stream::<T, L, B, O, R>::collection_kind()),
3089 },
3090 )
3091 }
3092}
3093
3094impl<'a, F, T, L, B: Boundedness, O: Ordering, R: Retries> Stream<F, L, B, O, R>
3095where
3096 L: TopLevel<'a>,
3097 F: Future<Output = T>,
3098{
3099 pub fn resolve_futures(self) -> Stream<T, L, Unbounded, NoOrder, R> {
3130 Stream::new(
3131 self.location.clone(),
3132 HydroNode::ResolveFutures {
3133 input: Box::new(self.ir_node.replace(HydroNode::Placeholder)),
3134 metadata: self
3135 .location
3136 .new_node_metadata(Stream::<T, L, Unbounded, NoOrder, R>::collection_kind()),
3137 },
3138 )
3139 }
3140
3141 pub fn resolve_futures_ordered(self) -> Stream<T, L, Unbounded, O, R> {
3172 Stream::new(
3173 self.location.clone(),
3174 HydroNode::ResolveFuturesOrdered {
3175 input: Box::new(self.ir_node.replace(HydroNode::Placeholder)),
3176 metadata: self
3177 .location
3178 .new_node_metadata(Stream::<T, L, Unbounded, O, R>::collection_kind()),
3179 },
3180 )
3181 }
3182}
3183
3184impl<'a, T, L, O: Ordering, R: Retries> Stream<T, Tick<L>, Bounded, O, R>
3185where
3186 L: Location<'a>,
3187{
3188 pub fn all_ticks(self) -> Stream<T, L, Unbounded, O, R> {
3191 Stream::new(
3192 self.location.outer().clone(),
3193 HydroNode::YieldConcat {
3194 inner: Box::new(self.ir_node.replace(HydroNode::Placeholder)),
3195 metadata: self
3196 .location
3197 .outer()
3198 .new_node_metadata(Stream::<T, L, Unbounded, O, R>::collection_kind()),
3199 },
3200 )
3201 }
3202
3203 pub fn all_ticks_atomic(self) -> Stream<T, Atomic<L>, Unbounded, O, R> {
3210 let out_location = Atomic {
3211 tick: self.location.clone(),
3212 };
3213
3214 Stream::new(
3215 out_location.clone(),
3216 HydroNode::YieldConcat {
3217 inner: Box::new(self.ir_node.replace(HydroNode::Placeholder)),
3218 metadata: out_location
3219 .new_node_metadata(Stream::<T, Atomic<L>, Unbounded, O, R>::collection_kind()),
3220 },
3221 )
3222 }
3223
3224 pub fn across_ticks<Out: BatchAtomic<'a>>(
3260 self,
3261 thunk: impl FnOnce(Stream<T, Atomic<L>, Unbounded, O, R>) -> Out,
3262 ) -> Out::Batched {
3263 thunk(self.all_ticks_atomic()).batched_atomic()
3264 }
3265
3266 pub fn defer_tick(self) -> Stream<T, Tick<L>, Bounded, O, R> {
3305 Stream::new(
3306 self.location.clone(),
3307 HydroNode::DeferTick {
3308 input: Box::new(self.ir_node.replace(HydroNode::Placeholder)),
3309 metadata: self
3310 .location
3311 .new_node_metadata(Stream::<T, Tick<L>, Bounded, O, R>::collection_kind()),
3312 },
3313 )
3314 }
3315}
3316
3317#[cfg(test)]
3318mod tests {
3319 #[cfg(feature = "deploy")]
3320 use futures::{SinkExt, StreamExt};
3321 #[cfg(feature = "deploy")]
3322 use hydro_deploy::Deployment;
3323 #[cfg(feature = "deploy")]
3324 use serde::{Deserialize, Serialize};
3325 #[cfg(any(feature = "deploy", feature = "sim"))]
3326 use stageleft::q;
3327
3328 #[cfg(any(feature = "deploy", feature = "sim"))]
3329 use crate::compile::builder::FlowBuilder;
3330 #[cfg(feature = "deploy")]
3331 use crate::live_collections::sliced::sliced;
3332 #[cfg(feature = "deploy")]
3333 use crate::live_collections::stream::ExactlyOnce;
3334 #[cfg(feature = "sim")]
3335 use crate::live_collections::stream::NoOrder;
3336 #[cfg(any(feature = "deploy", feature = "sim"))]
3337 use crate::live_collections::stream::TotalOrder;
3338 #[cfg(any(feature = "deploy", feature = "sim"))]
3339 use crate::location::Location;
3340 #[cfg(feature = "sim")]
3341 use crate::networking::TCP;
3342 #[cfg(any(feature = "deploy", feature = "sim"))]
3343 use crate::nondet::nondet;
3344
3345 mod backtrace_chained_ops;
3346
3347 #[cfg(feature = "deploy")]
3348 struct P1 {}
3349 #[cfg(feature = "deploy")]
3350 struct P2 {}
3351
3352 #[cfg(feature = "deploy")]
3353 #[derive(Serialize, Deserialize, Debug)]
3354 struct SendOverNetwork {
3355 n: u32,
3356 }
3357
3358 #[cfg(feature = "deploy")]
3359 #[tokio::test]
3360 async fn first_ten_distributed() {
3361 use crate::networking::TCP;
3362
3363 let mut deployment = Deployment::new();
3364
3365 let mut flow = FlowBuilder::new();
3366 let first_node = flow.process::<P1>();
3367 let second_node = flow.process::<P2>();
3368 let external = flow.external::<P2>();
3369
3370 let numbers = first_node.source_iter(q!(0..10));
3371 let out_port = numbers
3372 .map(q!(|n| SendOverNetwork { n }))
3373 .send(&second_node, TCP.fail_stop().bincode())
3374 .send_bincode_external(&external);
3375
3376 let nodes = flow
3377 .with_process(&first_node, deployment.Localhost())
3378 .with_process(&second_node, deployment.Localhost())
3379 .with_external(&external, deployment.Localhost())
3380 .deploy(&mut deployment);
3381
3382 deployment.deploy().await.unwrap();
3383
3384 let mut external_out = nodes.connect(out_port).await;
3385
3386 deployment.start().await.unwrap();
3387
3388 for i in 0..10 {
3389 assert_eq!(external_out.next().await.unwrap().n, i);
3390 }
3391 }
3392
3393 #[cfg(feature = "deploy")]
3394 #[tokio::test]
3395 async fn first_cardinality() {
3396 let mut deployment = Deployment::new();
3397
3398 let mut flow = FlowBuilder::new();
3399 let node = flow.process::<()>();
3400 let external = flow.external::<()>();
3401
3402 let node_tick = node.tick();
3403 let count = node_tick
3404 .singleton(q!([1, 2, 3]))
3405 .into_stream()
3406 .flatten_ordered()
3407 .first()
3408 .into_stream()
3409 .count()
3410 .all_ticks()
3411 .send_bincode_external(&external);
3412
3413 let nodes = flow
3414 .with_process(&node, deployment.Localhost())
3415 .with_external(&external, deployment.Localhost())
3416 .deploy(&mut deployment);
3417
3418 deployment.deploy().await.unwrap();
3419
3420 let mut external_out = nodes.connect(count).await;
3421
3422 deployment.start().await.unwrap();
3423
3424 assert_eq!(external_out.next().await.unwrap(), 1);
3425 }
3426
3427 #[cfg(feature = "deploy")]
3428 #[tokio::test]
3429 async fn unbounded_reduce_remembers_state() {
3430 let mut deployment = Deployment::new();
3431
3432 let mut flow = FlowBuilder::new();
3433 let node = flow.process::<()>();
3434 let external = flow.external::<()>();
3435
3436 let (input_port, input) = node.source_external_bincode(&external);
3437 let out = input
3438 .reduce(q!(|acc, v| *acc += v))
3439 .sample_eager(nondet!())
3440 .send_bincode_external(&external);
3441
3442 let nodes = flow
3443 .with_process(&node, deployment.Localhost())
3444 .with_external(&external, deployment.Localhost())
3445 .deploy(&mut deployment);
3446
3447 deployment.deploy().await.unwrap();
3448
3449 let mut external_in = nodes.connect(input_port).await;
3450 let mut external_out = nodes.connect(out).await;
3451
3452 deployment.start().await.unwrap();
3453
3454 external_in.send(1).await.unwrap();
3455 assert_eq!(external_out.next().await.unwrap(), 1);
3456
3457 external_in.send(2).await.unwrap();
3458 assert_eq!(external_out.next().await.unwrap(), 3);
3459 }
3460
3461 #[cfg(feature = "deploy")]
3462 #[tokio::test]
3463 async fn top_level_bounded_cross_singleton() {
3464 let mut deployment = Deployment::new();
3465
3466 let mut flow = FlowBuilder::new();
3467 let node = flow.process::<()>();
3468 let external = flow.external::<()>();
3469
3470 let (input_port, input) =
3471 node.source_external_bincode::<_, _, TotalOrder, ExactlyOnce>(&external);
3472
3473 let out = input
3474 .cross_singleton(
3475 node.source_iter(q!(vec![1, 2, 3]))
3476 .fold(q!(|| 0), q!(|acc, v| *acc += v)),
3477 )
3478 .send_bincode_external(&external);
3479
3480 let nodes = flow
3481 .with_process(&node, deployment.Localhost())
3482 .with_external(&external, deployment.Localhost())
3483 .deploy(&mut deployment);
3484
3485 deployment.deploy().await.unwrap();
3486
3487 let mut external_in = nodes.connect(input_port).await;
3488 let mut external_out = nodes.connect(out).await;
3489
3490 deployment.start().await.unwrap();
3491
3492 external_in.send(1).await.unwrap();
3493 assert_eq!(external_out.next().await.unwrap(), (1, 6));
3494
3495 external_in.send(2).await.unwrap();
3496 assert_eq!(external_out.next().await.unwrap(), (2, 6));
3497 }
3498
3499 #[cfg(feature = "deploy")]
3500 #[tokio::test]
3501 async fn top_level_bounded_reduce_cardinality() {
3502 let mut deployment = Deployment::new();
3503
3504 let mut flow = FlowBuilder::new();
3505 let node = flow.process::<()>();
3506 let external = flow.external::<()>();
3507
3508 let (input_port, input) =
3509 node.source_external_bincode::<_, _, TotalOrder, ExactlyOnce>(&external);
3510
3511 let out = sliced! {
3512 let input = use(input, nondet!());
3513 let v = use(node.source_iter(q!(vec![1, 2, 3])).reduce(q!(|acc, v| *acc += v)), nondet!());
3514 input.cross_singleton(v.into_stream().count())
3515 }
3516 .send_bincode_external(&external);
3517
3518 let nodes = flow
3519 .with_process(&node, deployment.Localhost())
3520 .with_external(&external, deployment.Localhost())
3521 .deploy(&mut deployment);
3522
3523 deployment.deploy().await.unwrap();
3524
3525 let mut external_in = nodes.connect(input_port).await;
3526 let mut external_out = nodes.connect(out).await;
3527
3528 deployment.start().await.unwrap();
3529
3530 external_in.send(1).await.unwrap();
3531 assert_eq!(external_out.next().await.unwrap(), (1, 1));
3532
3533 external_in.send(2).await.unwrap();
3534 assert_eq!(external_out.next().await.unwrap(), (2, 1));
3535 }
3536
3537 #[cfg(feature = "deploy")]
3538 #[tokio::test]
3539 async fn top_level_bounded_into_singleton_cardinality() {
3540 let mut deployment = Deployment::new();
3541
3542 let mut flow = FlowBuilder::new();
3543 let node = flow.process::<()>();
3544 let external = flow.external::<()>();
3545
3546 let (input_port, input) =
3547 node.source_external_bincode::<_, _, TotalOrder, ExactlyOnce>(&external);
3548
3549 let out = sliced! {
3550 let input = use(input, nondet!());
3551 let v = use(node.source_iter(q!(vec![1, 2, 3])).reduce(q!(|acc, v| *acc += v)).into_singleton(), nondet!());
3552 input.cross_singleton(v.into_stream().count())
3553 }
3554 .send_bincode_external(&external);
3555
3556 let nodes = flow
3557 .with_process(&node, deployment.Localhost())
3558 .with_external(&external, deployment.Localhost())
3559 .deploy(&mut deployment);
3560
3561 deployment.deploy().await.unwrap();
3562
3563 let mut external_in = nodes.connect(input_port).await;
3564 let mut external_out = nodes.connect(out).await;
3565
3566 deployment.start().await.unwrap();
3567
3568 external_in.send(1).await.unwrap();
3569 assert_eq!(external_out.next().await.unwrap(), (1, 1));
3570
3571 external_in.send(2).await.unwrap();
3572 assert_eq!(external_out.next().await.unwrap(), (2, 1));
3573 }
3574
3575 #[cfg(feature = "deploy")]
3576 #[tokio::test]
3577 async fn atomic_fold_replays_each_tick() {
3578 let mut deployment = Deployment::new();
3579
3580 let mut flow = FlowBuilder::new();
3581 let node = flow.process::<()>();
3582 let external = flow.external::<()>();
3583
3584 let (input_port, input) =
3585 node.source_external_bincode::<_, _, TotalOrder, ExactlyOnce>(&external);
3586 let tick = node.tick();
3587
3588 let out = input
3589 .batch(&tick, nondet!())
3590 .cross_singleton(
3591 node.source_iter(q!(vec![1, 2, 3]))
3592 .atomic()
3593 .fold(q!(|| 0), q!(|acc, v| *acc += v))
3594 .snapshot_atomic(&tick, nondet!()),
3595 )
3596 .all_ticks()
3597 .send_bincode_external(&external);
3598
3599 let nodes = flow
3600 .with_process(&node, deployment.Localhost())
3601 .with_external(&external, deployment.Localhost())
3602 .deploy(&mut deployment);
3603
3604 deployment.deploy().await.unwrap();
3605
3606 let mut external_in = nodes.connect(input_port).await;
3607 let mut external_out = nodes.connect(out).await;
3608
3609 deployment.start().await.unwrap();
3610
3611 external_in.send(1).await.unwrap();
3612 assert_eq!(external_out.next().await.unwrap(), (1, 6));
3613
3614 external_in.send(2).await.unwrap();
3615 assert_eq!(external_out.next().await.unwrap(), (2, 6));
3616 }
3617
3618 #[cfg(feature = "deploy")]
3619 #[tokio::test]
3620 async fn unbounded_scan_remembers_state() {
3621 let mut deployment = Deployment::new();
3622
3623 let mut flow = FlowBuilder::new();
3624 let node = flow.process::<()>();
3625 let external = flow.external::<()>();
3626
3627 let (input_port, input) = node.source_external_bincode(&external);
3628 let out = input
3629 .scan(
3630 q!(|| 0),
3631 q!(|acc, v| {
3632 *acc += v;
3633 Some(*acc)
3634 }),
3635 )
3636 .send_bincode_external(&external);
3637
3638 let nodes = flow
3639 .with_process(&node, deployment.Localhost())
3640 .with_external(&external, deployment.Localhost())
3641 .deploy(&mut deployment);
3642
3643 deployment.deploy().await.unwrap();
3644
3645 let mut external_in = nodes.connect(input_port).await;
3646 let mut external_out = nodes.connect(out).await;
3647
3648 deployment.start().await.unwrap();
3649
3650 external_in.send(1).await.unwrap();
3651 assert_eq!(external_out.next().await.unwrap(), 1);
3652
3653 external_in.send(2).await.unwrap();
3654 assert_eq!(external_out.next().await.unwrap(), 3);
3655 }
3656
3657 #[cfg(feature = "deploy")]
3658 #[tokio::test]
3659 async fn unbounded_enumerate_remembers_state() {
3660 let mut deployment = Deployment::new();
3661
3662 let mut flow = FlowBuilder::new();
3663 let node = flow.process::<()>();
3664 let external = flow.external::<()>();
3665
3666 let (input_port, input) = node.source_external_bincode(&external);
3667 let out = input.enumerate().send_bincode_external(&external);
3668
3669 let nodes = flow
3670 .with_process(&node, deployment.Localhost())
3671 .with_external(&external, deployment.Localhost())
3672 .deploy(&mut deployment);
3673
3674 deployment.deploy().await.unwrap();
3675
3676 let mut external_in = nodes.connect(input_port).await;
3677 let mut external_out = nodes.connect(out).await;
3678
3679 deployment.start().await.unwrap();
3680
3681 external_in.send(1).await.unwrap();
3682 assert_eq!(external_out.next().await.unwrap(), (0, 1));
3683
3684 external_in.send(2).await.unwrap();
3685 assert_eq!(external_out.next().await.unwrap(), (1, 2));
3686 }
3687
3688 #[cfg(feature = "deploy")]
3689 #[tokio::test]
3690 async fn unbounded_unique_remembers_state() {
3691 let mut deployment = Deployment::new();
3692
3693 let mut flow = FlowBuilder::new();
3694 let node = flow.process::<()>();
3695 let external = flow.external::<()>();
3696
3697 let (input_port, input) =
3698 node.source_external_bincode::<_, _, TotalOrder, ExactlyOnce>(&external);
3699 let out = input.unique().send_bincode_external(&external);
3700
3701 let nodes = flow
3702 .with_process(&node, deployment.Localhost())
3703 .with_external(&external, deployment.Localhost())
3704 .deploy(&mut deployment);
3705
3706 deployment.deploy().await.unwrap();
3707
3708 let mut external_in = nodes.connect(input_port).await;
3709 let mut external_out = nodes.connect(out).await;
3710
3711 deployment.start().await.unwrap();
3712
3713 external_in.send(1).await.unwrap();
3714 assert_eq!(external_out.next().await.unwrap(), 1);
3715
3716 external_in.send(2).await.unwrap();
3717 assert_eq!(external_out.next().await.unwrap(), 2);
3718
3719 external_in.send(1).await.unwrap();
3720 external_in.send(3).await.unwrap();
3721 assert_eq!(external_out.next().await.unwrap(), 3);
3722 }
3723
3724 #[cfg(feature = "sim")]
3725 #[test]
3726 #[should_panic]
3727 fn sim_batch_nondet_size() {
3728 let mut flow = FlowBuilder::new();
3729 let node = flow.process::<()>();
3730
3731 let (in_send, input) = node.sim_input::<_, TotalOrder, _>();
3732
3733 let tick = node.tick();
3734 let out_recv = input
3735 .batch(&tick, nondet!())
3736 .count()
3737 .all_ticks()
3738 .sim_output();
3739
3740 flow.sim().exhaustive(async || {
3741 in_send.send(());
3742 in_send.send(());
3743 in_send.send(());
3744
3745 assert_eq!(out_recv.next().await.unwrap(), 3); });
3747 }
3748
3749 #[cfg(feature = "sim")]
3750 #[test]
3751 fn sim_batch_preserves_order() {
3752 let mut flow = FlowBuilder::new();
3753 let node = flow.process::<()>();
3754
3755 let (in_send, input) = node.sim_input();
3756
3757 let tick = node.tick();
3758 let out_recv = input
3759 .batch(&tick, nondet!())
3760 .all_ticks()
3761 .sim_output();
3762
3763 flow.sim().exhaustive(async || {
3764 in_send.send(1);
3765 in_send.send(2);
3766 in_send.send(3);
3767
3768 out_recv.assert_yields_only([1, 2, 3]).await;
3769 });
3770 }
3771
3772 #[cfg(feature = "sim")]
3773 #[test]
3774 #[should_panic]
3775 fn sim_batch_unordered_shuffles() {
3776 let mut flow = FlowBuilder::new();
3777 let node = flow.process::<()>();
3778
3779 let (in_send, input) = node.sim_input::<_, NoOrder, _>();
3780
3781 let tick = node.tick();
3782 let batch = input.batch(&tick, nondet!());
3783 let out_recv = batch
3784 .clone()
3785 .min()
3786 .zip(batch.max())
3787 .all_ticks()
3788 .sim_output();
3789
3790 flow.sim().exhaustive(async || {
3791 in_send.send_many_unordered([1, 2, 3]);
3792
3793 if out_recv.collect::<Vec<_>>().await == vec![(1, 3), (2, 2)] {
3794 panic!("saw both (1, 3) and (2, 2), so batching must have shuffled the order");
3795 }
3796 });
3797 }
3798
3799 #[cfg(feature = "sim")]
3800 #[test]
3801 fn sim_batch_unordered_shuffles_count() {
3802 let mut flow = FlowBuilder::new();
3803 let node = flow.process::<()>();
3804
3805 let (in_send, input) = node.sim_input::<_, NoOrder, _>();
3806
3807 let tick = node.tick();
3808 let batch = input.batch(&tick, nondet!());
3809 let out_recv = batch.all_ticks().sim_output();
3810
3811 let instance_count = flow.sim().exhaustive(async || {
3812 in_send.send_many_unordered([1, 2, 3, 4]);
3813 out_recv.assert_yields_only_unordered([1, 2, 3, 4]).await;
3814 });
3815
3816 assert_eq!(
3817 instance_count,
3818 75 )
3820 }
3821
3822 #[cfg(feature = "sim")]
3823 #[test]
3824 #[should_panic]
3825 fn sim_observe_order_batched() {
3826 let mut flow = FlowBuilder::new();
3827 let node = flow.process::<()>();
3828
3829 let (in_send, input) = node.sim_input::<_, NoOrder, _>();
3830
3831 let tick = node.tick();
3832 let batch = input.batch(&tick, nondet!());
3833 let out_recv = batch
3834 .assume_ordering::<TotalOrder>(nondet!())
3835 .all_ticks()
3836 .sim_output();
3837
3838 flow.sim().exhaustive(async || {
3839 in_send.send_many_unordered([1, 2, 3, 4]);
3840 out_recv.assert_yields_only([1, 2, 3, 4]).await; });
3842 }
3843
3844 #[cfg(feature = "sim")]
3845 #[test]
3846 fn sim_observe_order_batched_count() {
3847 let mut flow = FlowBuilder::new();
3848 let node = flow.process::<()>();
3849
3850 let (in_send, input) = node.sim_input::<_, NoOrder, _>();
3851
3852 let tick = node.tick();
3853 let batch = input.batch(&tick, nondet!());
3854 let out_recv = batch
3855 .assume_ordering::<TotalOrder>(nondet!())
3856 .all_ticks()
3857 .sim_output();
3858
3859 let instance_count = flow.sim().exhaustive(async || {
3860 in_send.send_many_unordered([1, 2, 3, 4]);
3861 let _ = out_recv.collect::<Vec<_>>().await;
3862 });
3863
3864 assert_eq!(
3865 instance_count,
3866 192 )
3868 }
3869
3870 #[cfg(feature = "sim")]
3871 #[test]
3872 fn sim_unordered_count_instance_count() {
3873 let mut flow = FlowBuilder::new();
3874 let node = flow.process::<()>();
3875
3876 let (in_send, input) = node.sim_input::<_, NoOrder, _>();
3877
3878 let tick = node.tick();
3879 let out_recv = input
3880 .count()
3881 .snapshot(&tick, nondet!())
3882 .all_ticks()
3883 .sim_output();
3884
3885 let instance_count = flow.sim().exhaustive(async || {
3886 in_send.send_many_unordered([1, 2, 3, 4]);
3887 assert!(out_recv.collect::<Vec<_>>().await.last().unwrap() == &4);
3888 });
3889
3890 assert_eq!(
3891 instance_count,
3892 16 )
3894 }
3895
3896 #[cfg(feature = "sim")]
3897 #[test]
3898 fn sim_top_level_assume_ordering() {
3899 let mut flow = FlowBuilder::new();
3900 let node = flow.process::<()>();
3901
3902 let (in_send, input) = node.sim_input::<_, NoOrder, _>();
3903
3904 let out_recv = input
3905 .assume_ordering::<TotalOrder>(nondet!())
3906 .sim_output();
3907
3908 let instance_count = flow.sim().exhaustive(async || {
3909 in_send.send_many_unordered([1, 2, 3]);
3910 let mut out = out_recv.collect::<Vec<_>>().await;
3911 out.sort();
3912 assert_eq!(out, vec![1, 2, 3]);
3913 });
3914
3915 assert_eq!(instance_count, 6)
3916 }
3917
3918 #[cfg(feature = "sim")]
3919 #[test]
3920 fn sim_top_level_assume_ordering_cycle_back() {
3921 let mut flow = FlowBuilder::new();
3922 let node = flow.process::<()>();
3923 let node2 = flow.process::<()>();
3924
3925 let (in_send, input) = node.sim_input::<_, NoOrder, _>();
3926
3927 let (complete_cycle_back, cycle_back) =
3928 node.forward_ref::<super::Stream<_, _, _, NoOrder>>();
3929 let ordered = input
3930 .merge_unordered(cycle_back)
3931 .assume_ordering::<TotalOrder>(nondet!());
3932 complete_cycle_back.complete(
3933 ordered
3934 .clone()
3935 .map(q!(|v| v + 1))
3936 .filter(q!(|v| v % 2 == 1))
3937 .send(&node2, TCP.fail_stop().bincode())
3938 .send(&node, TCP.fail_stop().bincode()),
3939 );
3940
3941 let out_recv = ordered.sim_output();
3942
3943 let mut saw = false;
3944 let instance_count = flow.sim().exhaustive(async || {
3945 in_send.send_many_unordered([0, 2]);
3946 let out = out_recv.collect::<Vec<_>>().await;
3947
3948 if out.starts_with(&[0, 1, 2]) {
3949 saw = true;
3950 }
3951 });
3952
3953 assert!(saw, "did not see an instance with 0, 1, 2 in order");
3954 assert_eq!(instance_count, 6);
3955 }
3956
3957 #[cfg(feature = "sim")]
3958 #[test]
3959 fn sim_top_level_assume_ordering_cycle_back_tick() {
3960 let mut flow = FlowBuilder::new();
3961 let node = flow.process::<()>();
3962 let node2 = flow.process::<()>();
3963
3964 let (in_send, input) = node.sim_input::<_, NoOrder, _>();
3965
3966 let (complete_cycle_back, cycle_back) =
3967 node.forward_ref::<super::Stream<_, _, _, NoOrder>>();
3968 let ordered = input
3969 .merge_unordered(cycle_back)
3970 .assume_ordering::<TotalOrder>(nondet!());
3971 complete_cycle_back.complete(
3972 ordered
3973 .clone()
3974 .batch(&node.tick(), nondet!())
3975 .all_ticks()
3976 .map(q!(|v| v + 1))
3977 .filter(q!(|v| v % 2 == 1))
3978 .send(&node2, TCP.fail_stop().bincode())
3979 .send(&node, TCP.fail_stop().bincode()),
3980 );
3981
3982 let out_recv = ordered.sim_output();
3983
3984 let mut saw = false;
3985 let instance_count = flow.sim().exhaustive(async || {
3986 in_send.send_many_unordered([0, 2]);
3987 let out = out_recv.collect::<Vec<_>>().await;
3988
3989 if out.starts_with(&[0, 1, 2]) {
3990 saw = true;
3991 }
3992 });
3993
3994 assert!(saw, "did not see an instance with 0, 1, 2 in order");
3995 assert_eq!(instance_count, 58);
3996 }
3997
3998 #[cfg(feature = "sim")]
3999 #[test]
4000 fn sim_top_level_assume_ordering_multiple() {
4001 let mut flow = FlowBuilder::new();
4002 let node = flow.process::<()>();
4003 let node2 = flow.process::<()>();
4004
4005 let (in_send, input) = node.sim_input::<_, NoOrder, _>();
4006 let (_, input2) = node.sim_input::<_, NoOrder, _>();
4007
4008 let (complete_cycle_back, cycle_back) =
4009 node.forward_ref::<super::Stream<_, _, _, NoOrder>>();
4010 let input1_ordered = input
4011 .clone()
4012 .merge_unordered(cycle_back)
4013 .assume_ordering::<TotalOrder>(nondet!());
4014 let foo = input1_ordered
4015 .clone()
4016 .map(q!(|v| v + 3))
4017 .weaken_ordering::<NoOrder>()
4018 .merge_unordered(input2)
4019 .assume_ordering::<TotalOrder>(nondet!());
4020
4021 complete_cycle_back.complete(
4022 foo.filter(q!(|v| *v == 3))
4023 .send(&node2, TCP.fail_stop().bincode())
4024 .send(&node, TCP.fail_stop().bincode()),
4025 );
4026
4027 let out_recv = input1_ordered.sim_output();
4028
4029 let mut saw = false;
4030 let instance_count = flow.sim().exhaustive(async || {
4031 in_send.send_many_unordered([0, 1]);
4032 let out = out_recv.collect::<Vec<_>>().await;
4033
4034 if out.starts_with(&[0, 3, 1]) {
4035 saw = true;
4036 }
4037 });
4038
4039 assert!(saw, "did not see an instance with 0, 3, 1 in order");
4040 assert_eq!(instance_count, 24);
4041 }
4042
4043 #[cfg(feature = "sim")]
4044 #[test]
4045 fn sim_atomic_assume_ordering_cycle_back() {
4046 let mut flow = FlowBuilder::new();
4047 let node = flow.process::<()>();
4048 let node2 = flow.process::<()>();
4049
4050 let (in_send, input) = node.sim_input::<_, NoOrder, _>();
4051
4052 let (complete_cycle_back, cycle_back) =
4053 node.forward_ref::<super::Stream<_, _, _, NoOrder>>();
4054 let ordered = input
4055 .merge_unordered(cycle_back)
4056 .atomic()
4057 .assume_ordering::<TotalOrder>(nondet!())
4058 .end_atomic();
4059 complete_cycle_back.complete(
4060 ordered
4061 .clone()
4062 .map(q!(|v| v + 1))
4063 .filter(q!(|v| v % 2 == 1))
4064 .send(&node2, TCP.fail_stop().bincode())
4065 .send(&node, TCP.fail_stop().bincode()),
4066 );
4067
4068 let out_recv = ordered.sim_output();
4069
4070 let instance_count = flow.sim().exhaustive(async || {
4071 in_send.send_many_unordered([0, 2]);
4072 let out = out_recv.collect::<Vec<_>>().await;
4073 assert_eq!(out.len(), 4);
4074 });
4075 assert_eq!(instance_count, 22);
4076 }
4077
4078 #[cfg(feature = "deploy")]
4079 #[tokio::test]
4080 async fn partition_evens_odds() {
4081 let mut deployment = Deployment::new();
4082
4083 let mut flow = FlowBuilder::new();
4084 let node = flow.process::<()>();
4085 let external = flow.external::<()>();
4086
4087 let numbers = node.source_iter(q!(vec![1i32, 2, 3, 4, 5, 6]));
4088 let (evens, odds) = numbers.partition(q!(|x: &i32| x % 2 == 0));
4089 let evens_port = evens.send_bincode_external(&external);
4090 let odds_port = odds.send_bincode_external(&external);
4091
4092 let nodes = flow
4093 .with_process(&node, deployment.Localhost())
4094 .with_external(&external, deployment.Localhost())
4095 .deploy(&mut deployment);
4096
4097 deployment.deploy().await.unwrap();
4098
4099 let mut evens_out = nodes.connect(evens_port).await;
4100 let mut odds_out = nodes.connect(odds_port).await;
4101
4102 deployment.start().await.unwrap();
4103
4104 let mut even_results = Vec::new();
4105 for _ in 0..3 {
4106 even_results.push(evens_out.next().await.unwrap());
4107 }
4108 even_results.sort();
4109 assert_eq!(even_results, vec![2, 4, 6]);
4110
4111 let mut odd_results = Vec::new();
4112 for _ in 0..3 {
4113 odd_results.push(odds_out.next().await.unwrap());
4114 }
4115 odd_results.sort();
4116 assert_eq!(odd_results, vec![1, 3, 5]);
4117 }
4118
4119 #[cfg(feature = "deploy")]
4120 #[tokio::test]
4121 async fn unconsumed_inspect_still_runs() {
4122 use crate::deploy::DeployCrateWrapper;
4123
4124 let mut deployment = Deployment::new();
4125
4126 let mut flow = FlowBuilder::new();
4127 let node = flow.process::<()>();
4128
4129 node.source_iter(q!(0..5))
4132 .inspect(q!(|x| println!("inspect: {}", x)));
4133
4134 let nodes = flow
4135 .with_process(&node, deployment.Localhost())
4136 .deploy(&mut deployment);
4137
4138 deployment.deploy().await.unwrap();
4139
4140 let mut stdout = nodes.get_process(&node).stdout();
4141
4142 deployment.start().await.unwrap();
4143
4144 let mut lines = Vec::new();
4145 for _ in 0..5 {
4146 lines.push(stdout.recv().await.unwrap());
4147 }
4148 lines.sort();
4149 assert_eq!(
4150 lines,
4151 vec![
4152 "inspect: 0",
4153 "inspect: 1",
4154 "inspect: 2",
4155 "inspect: 3",
4156 "inspect: 4",
4157 ]
4158 );
4159 }
4160
4161 #[cfg(feature = "deploy")]
4162 #[tokio::test]
4163 async fn unconsumed_inspect_alive_at_deploy_still_runs() {
4164 use crate::deploy::DeployCrateWrapper;
4165
4166 let mut deployment = Deployment::new();
4167
4168 let mut flow = FlowBuilder::new();
4169 let node = flow.process::<()>();
4170
4171 let _inspected = node
4176 .source_iter(q!(0..5))
4177 .inspect(q!(|x| println!("inspect: {}", x)));
4178
4179 let nodes = flow
4180 .with_process(&node, deployment.Localhost())
4181 .deploy(&mut deployment);
4182
4183 deployment.deploy().await.unwrap();
4184
4185 let mut stdout = nodes.get_process(&node).stdout();
4186
4187 deployment.start().await.unwrap();
4188
4189 let mut lines = Vec::new();
4190 for _ in 0..5 {
4191 lines.push(stdout.recv().await.unwrap());
4192 }
4193 lines.sort();
4194 assert_eq!(
4195 lines,
4196 vec![
4197 "inspect: 0",
4198 "inspect: 1",
4199 "inspect: 2",
4200 "inspect: 3",
4201 "inspect: 4",
4202 ]
4203 );
4204 }
4205
4206 #[cfg(feature = "sim")]
4207 #[test]
4208 fn sim_limit() {
4209 let mut flow = FlowBuilder::new();
4210 let node = flow.process::<()>();
4211
4212 let (in_send, input) = node.sim_input();
4213
4214 let out_recv = input.limit(q!(3)).sim_output();
4215
4216 flow.sim().exhaustive(async || {
4217 in_send.send(1);
4218 in_send.send(2);
4219 in_send.send(3);
4220 in_send.send(4);
4221 in_send.send(5);
4222
4223 out_recv.assert_yields_only([1, 2, 3]).await;
4224 });
4225 }
4226
4227 #[cfg(feature = "sim")]
4228 #[test]
4229 fn sim_limit_zero() {
4230 let mut flow = FlowBuilder::new();
4231 let node = flow.process::<()>();
4232
4233 let (in_send, input) = node.sim_input();
4234
4235 let out_recv = input.limit(q!(0)).sim_output();
4236
4237 flow.sim().exhaustive(async || {
4238 in_send.send(1);
4239 in_send.send(2);
4240
4241 out_recv.assert_yields_only::<i32, _>([]).await;
4242 });
4243 }
4244
4245 #[cfg(feature = "sim")]
4246 #[test]
4247 fn sim_merge_ordered() {
4248 let mut flow = FlowBuilder::new();
4249 let node = flow.process::<()>();
4250
4251 let (in_send, input) = node.sim_input();
4252 let (in_send2, input2) = node.sim_input();
4253
4254 let out_recv = input
4255 .merge_ordered(input2, nondet!())
4256 .sim_output();
4257
4258 let mut saw_out_of_order = false;
4259 let instances = flow.sim().exhaustive(async || {
4260 in_send.send(1);
4261 in_send.send(2);
4262 in_send2.send(3);
4263 in_send2.send(4);
4264
4265 let out = out_recv.collect::<Vec<_>>().await;
4266
4267 if out == [1, 3, 2, 4] {
4268 saw_out_of_order = true;
4269 }
4270
4271 let mut first_elements = out.iter().filter(|v| **v <= 2).copied().collect::<Vec<_>>();
4274 let mut second_elements = out.iter().filter(|v| **v > 2).copied().collect::<Vec<_>>();
4275 assert_eq!(
4276 first_elements,
4277 vec![1, 2],
4278 "first input order violated: {:?}",
4279 out
4280 );
4281 assert_eq!(
4282 second_elements,
4283 vec![3, 4],
4284 "second input order violated: {:?}",
4285 out
4286 );
4287
4288 first_elements.append(&mut second_elements);
4289 first_elements.sort();
4290 assert_eq!(first_elements, vec![1, 2, 3, 4]);
4291 });
4292
4293 assert!(saw_out_of_order);
4294 assert_eq!(instances, 6);
4295 }
4296
4297 #[cfg(feature = "sim")]
4300 #[test]
4301 fn sim_merge_ordered_one_empty() {
4302 let mut flow = FlowBuilder::new();
4303 let node = flow.process::<()>();
4304
4305 let (in_send, input) = node.sim_input();
4306 let (_in_send2, input2) = node.sim_input();
4307
4308 let out_recv = input
4309 .merge_ordered(input2, nondet!())
4310 .sim_output();
4311
4312 let instances = flow.sim().exhaustive(async || {
4313 in_send.send(1);
4314 in_send.send(2);
4315
4316 let out = out_recv.collect::<Vec<_>>().await;
4317 assert_eq!(out, vec![1, 2]);
4318 });
4319
4320 assert_eq!(instances, 1);
4322 }
4323
4324 #[cfg(feature = "sim")]
4330 #[test]
4331 fn sim_merge_ordered_cycle_back() {
4332 let mut flow = FlowBuilder::new();
4333 let node = flow.process::<()>();
4334
4335 let (in_send, input) = node.sim_input();
4336
4337 let (complete_cycle_back, cycle_back) =
4339 node.forward_ref::<super::Stream<_, _, _, TotalOrder>>();
4340
4341 let merged = input.merge_ordered(cycle_back, nondet!());
4343
4344 complete_cycle_back.complete(merged.clone().filter(q!(|v| *v == 1)).map(q!(|v| v * 10)));
4346
4347 let out_recv = merged.sim_output();
4348
4349 let mut saw_cycle_before_second = false;
4352 flow.sim().exhaustive(async || {
4353 in_send.send(1);
4354 in_send.send(2);
4355
4356 let out = out_recv.collect::<Vec<_>>().await;
4357
4358 let pos_1 = out.iter().position(|v| *v == 1).unwrap();
4360 let pos_10 = out.iter().position(|v| *v == 10).unwrap();
4361 assert!(pos_1 < pos_10, "causal order violated: {:?}", out);
4362
4363 if out == [1, 10, 2] {
4365 saw_cycle_before_second = true;
4366 }
4367
4368 let mut sorted = out;
4369 sorted.sort();
4370 assert_eq!(sorted, vec![1, 2, 10]);
4371 });
4372
4373 assert!(
4374 saw_cycle_before_second,
4375 "never saw the cycled element arrive before the second input element"
4376 );
4377 }
4378
4379 #[cfg(feature = "sim")]
4383 #[test]
4384 fn sim_merge_ordered_delayed() {
4385 let mut flow = FlowBuilder::new();
4386 let node = flow.process::<()>();
4387
4388 let (in_send, input) = node.sim_input();
4389 let (in_send2, input2) = node.sim_input();
4390
4391 let out_recv = input
4392 .merge_ordered(input2, nondet!())
4393 .sim_output();
4394
4395 let mut saw_delayed_interleaving = false;
4396 flow.sim().exhaustive(async || {
4397 in_send.send(1);
4399 in_send2.send(3);
4400 in_send2.send(4);
4401
4402 let first_batch = out_recv.collect::<Vec<_>>().await;
4404
4405 in_send.send(2);
4407 let second_batch = out_recv.collect::<Vec<_>>().await;
4408
4409 let mut all: Vec<_> = first_batch
4410 .iter()
4411 .chain(second_batch.iter())
4412 .copied()
4413 .collect();
4414
4415 if all == [1, 3, 4, 2] {
4417 saw_delayed_interleaving = true;
4418 }
4419
4420 all.sort();
4421 assert_eq!(all, vec![1, 2, 3, 4]);
4422 });
4423
4424 assert!(saw_delayed_interleaving);
4425 }
4426
4427 #[cfg(feature = "deploy")]
4432 #[tokio::test]
4433 async fn deploy_merge_ordered_delayed() {
4434 let mut deployment = Deployment::new();
4435
4436 let mut flow = FlowBuilder::new();
4437 let node = flow.process::<()>();
4438 let external = flow.external::<()>();
4439
4440 let (input_a_port, input_a) = node.source_external_bincode(&external);
4441 let (input_b_port, input_b) = node.source_external_bincode(&external);
4442
4443 let out = input_a
4444 .assume_ordering(nondet!())
4445 .merge_ordered(
4446 input_b.assume_ordering(nondet!()),
4447 nondet!(),
4448 )
4449 .send_bincode_external(&external);
4450
4451 let nodes = flow
4452 .with_process(&node, deployment.Localhost())
4453 .with_external(&external, deployment.Localhost())
4454 .deploy(&mut deployment);
4455
4456 deployment.deploy().await.unwrap();
4457
4458 let mut ext_a = nodes.connect(input_a_port).await;
4459 let mut ext_b = nodes.connect(input_b_port).await;
4460 let mut ext_out = nodes.connect(out).await;
4461
4462 deployment.start().await.unwrap();
4463
4464 ext_a.send(1).await.unwrap();
4466 ext_b.send(3).await.unwrap();
4467 ext_b.send(4).await.unwrap();
4468
4469 let mut received = Vec::new();
4471 for _ in 0..3 {
4472 received.push(ext_out.next().await.unwrap());
4473 }
4474
4475 ext_a.send(2).await.unwrap();
4477 received.push(ext_out.next().await.unwrap());
4478
4479 received.sort();
4481 assert_eq!(received, vec![1, 2, 3, 4]);
4482 }
4483
4484 #[cfg(feature = "deploy")]
4485 #[tokio::test]
4486 async fn monotone_fold_threshold() {
4487 use crate::properties::manual_proof;
4488
4489 let mut deployment = Deployment::new();
4490
4491 let mut flow = FlowBuilder::new();
4492 let node = flow.process::<()>();
4493 let external = flow.external::<()>();
4494
4495 let in_unbounded: super::Stream<_, _> =
4496 node.source_iter(q!(vec![1i32, 2, 3, 4, 5, 6])).into();
4497 let sum = in_unbounded.fold(
4498 q!(|| 0),
4499 q!(
4500 |sum, v| {
4501 *sum += v;
4502 },
4503 monotone = manual_proof!()
4504 ),
4505 );
4506
4507 let threshold_out = sum
4508 .threshold_greater_or_equal(node.singleton(q!(7)))
4509 .send_bincode_external(&external);
4510
4511 let nodes = flow
4512 .with_process(&node, deployment.Localhost())
4513 .with_external(&external, deployment.Localhost())
4514 .deploy(&mut deployment);
4515
4516 deployment.deploy().await.unwrap();
4517
4518 let mut threshold_out = nodes.connect(threshold_out).await;
4519
4520 deployment.start().await.unwrap();
4521
4522 assert_eq!(threshold_out.next().await.unwrap(), 7);
4523 }
4524
4525 #[cfg(feature = "deploy")]
4526 #[tokio::test]
4527 async fn monotone_count_threshold() {
4528 let mut deployment = Deployment::new();
4529
4530 let mut flow = FlowBuilder::new();
4531 let node = flow.process::<()>();
4532 let external = flow.external::<()>();
4533
4534 let in_unbounded: super::Stream<_, _> =
4535 node.source_iter(q!(vec![1i32, 2, 3, 4, 5, 6])).into();
4536 let sum = in_unbounded.count();
4537
4538 let threshold_out = sum
4539 .threshold_greater_or_equal(node.singleton(q!(3)))
4540 .send_bincode_external(&external);
4541
4542 let nodes = flow
4543 .with_process(&node, deployment.Localhost())
4544 .with_external(&external, deployment.Localhost())
4545 .deploy(&mut deployment);
4546
4547 deployment.deploy().await.unwrap();
4548
4549 let mut threshold_out = nodes.connect(threshold_out).await;
4550
4551 deployment.start().await.unwrap();
4552
4553 assert_eq!(threshold_out.next().await.unwrap(), 3);
4554 }
4555
4556 #[cfg(feature = "deploy")]
4557 #[tokio::test]
4558 async fn monotone_map_order_preserving_threshold() {
4559 use crate::properties::manual_proof;
4560
4561 let mut deployment = Deployment::new();
4562
4563 let mut flow = FlowBuilder::new();
4564 let node = flow.process::<()>();
4565 let external = flow.external::<()>();
4566
4567 let in_unbounded: super::Stream<_, _> =
4568 node.source_iter(q!(vec![1i32, 2, 3, 4, 5, 6])).into();
4569 let sum = in_unbounded.fold(
4570 q!(|| 0),
4571 q!(
4572 |sum, v| {
4573 *sum += v;
4574 },
4575 monotone = manual_proof!()
4576 ),
4577 );
4578
4579 let doubled = sum.map(q!(
4581 |v| v * 2,
4582 order_preserving = manual_proof!()
4583 ));
4584
4585 let threshold_out = doubled
4586 .threshold_greater_or_equal(node.singleton(q!(14)))
4587 .send_bincode_external(&external);
4588
4589 let nodes = flow
4590 .with_process(&node, deployment.Localhost())
4591 .with_external(&external, deployment.Localhost())
4592 .deploy(&mut deployment);
4593
4594 deployment.deploy().await.unwrap();
4595
4596 let mut threshold_out = nodes.connect(threshold_out).await;
4597
4598 deployment.start().await.unwrap();
4599
4600 assert_eq!(threshold_out.next().await.unwrap(), 14);
4601 }
4602
4603 #[cfg(any(feature = "deploy", feature = "sim"))]
4606 mod join_ordering_type_tests {
4607 use crate::live_collections::boundedness::{Bounded, Unbounded};
4608 use crate::live_collections::stream::{ExactlyOnce, NoOrder, Stream, TotalOrder};
4609 use crate::location::{Location, Process};
4610
4611 #[expect(dead_code, reason = "compile-time type test")]
4612 fn join_unbounded_with_bounded_preserves_order<'a>(
4613 left: Stream<(i32, char), Process<'a>, Unbounded, TotalOrder, ExactlyOnce>,
4614 right: Stream<(i32, char), Process<'a>, Bounded, TotalOrder, ExactlyOnce>,
4615 ) -> Stream<(i32, (char, char)), Process<'a>, Unbounded, TotalOrder, ExactlyOnce> {
4616 left.join(right)
4617 }
4618
4619 #[expect(dead_code, reason = "compile-time type test")]
4620 fn join_unbounded_with_unbounded_is_no_order<'a>(
4621 left: Stream<(i32, char), Process<'a>, Unbounded, TotalOrder, ExactlyOnce>,
4622 right: Stream<(i32, char), Process<'a>, Unbounded, TotalOrder, ExactlyOnce>,
4623 ) -> Stream<(i32, (char, char)), Process<'a>, Unbounded, NoOrder, ExactlyOnce> {
4624 left.join(right)
4625 }
4626
4627 #[expect(dead_code, reason = "compile-time type test")]
4628 fn join_bounded_with_bounded_preserves_order<'a, L: Location<'a>>(
4629 left: Stream<(i32, char), L, Bounded, TotalOrder, ExactlyOnce>,
4630 right: Stream<(i32, char), L, Bounded, TotalOrder, ExactlyOnce>,
4631 ) -> Stream<(i32, (char, char)), L, Bounded, TotalOrder, ExactlyOnce> {
4632 left.join(right)
4633 }
4634
4635 #[expect(dead_code, reason = "compile-time type test")]
4636 fn join_unbounded_noorder_with_bounded<'a>(
4637 left: Stream<(i32, char), Process<'a>, Unbounded, NoOrder, ExactlyOnce>,
4638 right: Stream<(i32, char), Process<'a>, Bounded, NoOrder, ExactlyOnce>,
4639 ) -> Stream<(i32, (char, char)), Process<'a>, Unbounded, NoOrder, ExactlyOnce> {
4640 left.join(right)
4641 }
4642
4643 #[expect(dead_code, reason = "compile-time type test")]
4646 fn cross_product_unbounded_with_bounded_preserves_order<'a>(
4647 left: Stream<i32, Process<'a>, Unbounded, TotalOrder, ExactlyOnce>,
4648 right: Stream<char, Process<'a>, Bounded, TotalOrder, ExactlyOnce>,
4649 ) -> Stream<(i32, char), Process<'a>, Unbounded, TotalOrder, ExactlyOnce> {
4650 left.cross_product(right)
4651 }
4652
4653 #[expect(dead_code, reason = "compile-time type test")]
4654 fn cross_product_bounded_with_bounded_preserves_order<'a>(
4655 left: Stream<i32, Process<'a>, Bounded, TotalOrder, ExactlyOnce>,
4656 right: Stream<char, Process<'a>, Bounded, TotalOrder, ExactlyOnce>,
4657 ) -> Stream<(i32, char), Process<'a>, Bounded, TotalOrder, ExactlyOnce> {
4658 left.cross_product(right)
4659 }
4660
4661 #[expect(dead_code, reason = "compile-time type test")]
4662 fn cross_product_unbounded_with_unbounded_is_no_order<'a>(
4663 left: Stream<i32, Process<'a>, Unbounded, TotalOrder, ExactlyOnce>,
4664 right: Stream<char, Process<'a>, Unbounded, TotalOrder, ExactlyOnce>,
4665 ) -> Stream<(i32, char), Process<'a>, Unbounded, NoOrder, ExactlyOnce> {
4666 left.cross_product(right)
4667 }
4668 } #[cfg(feature = "sim")]
4673 #[test]
4674 fn cross_product_mixed_boundedness_correctness() {
4675 use stageleft::q;
4676
4677 use crate::compile::builder::FlowBuilder;
4678 use crate::nondet::nondet;
4679
4680 let mut flow = FlowBuilder::new();
4681 let process = flow.process::<()>();
4682 let tick = process.tick();
4683
4684 let left = process.source_iter(q!(vec![1, 2]));
4685 let right = process
4686 .source_iter(q!(vec!['a', 'b']))
4687 .batch(&tick, nondet!())
4688 .all_ticks();
4689
4690 let out = left.cross_product(right).sim_output();
4691
4692 flow.sim().exhaustive(async || {
4693 out.assert_yields_only_unordered(vec![(1, 'a'), (1, 'b'), (2, 'a'), (2, 'b')])
4694 .await;
4695 });
4696 }
4697
4698 #[cfg(feature = "sim")]
4699 #[test]
4700 fn join_mixed_boundedness_correctness() {
4701 use stageleft::q;
4702
4703 use crate::compile::builder::FlowBuilder;
4704 use crate::nondet::nondet;
4705
4706 let mut flow = FlowBuilder::new();
4707 let process = flow.process::<()>();
4708 let tick = process.tick();
4709
4710 let left = process.source_iter(q!(vec![(1, 'a'), (2, 'b')]));
4711 let right = process
4712 .source_iter(q!(vec![(1, 'x'), (2, 'y')]))
4713 .batch(&tick, nondet!())
4714 .all_ticks();
4715
4716 let out = left.join(right).sim_output();
4717
4718 flow.sim().exhaustive(async || {
4719 out.assert_yields_only_unordered(vec![(1, ('a', 'x')), (2, ('b', 'y'))])
4720 .await;
4721 });
4722 }
4723
4724 #[cfg(feature = "sim")]
4725 #[test]
4726 fn sim_merge_unordered_independent_atomics() {
4727 let mut flow = FlowBuilder::new();
4728 let node = flow.process::<()>();
4729
4730 let (in1_send, input1) = node.sim_input::<_, TotalOrder, _>();
4731 let (in2_send, input2) = node.sim_input::<_, TotalOrder, _>();
4732
4733 let out = input1
4734 .atomic()
4735 .merge_unordered(input2.atomic())
4736 .end_atomic()
4737 .sim_output();
4738
4739 flow.sim().exhaustive(async || {
4740 in1_send.send(1);
4741 in2_send.send(2);
4742
4743 out.assert_yields_only_unordered(vec![1, 2]).await;
4744 });
4745 }
4746
4747 #[cfg(feature = "deploy")]
4748 #[tokio::test]
4749 async fn test_stream_ref() {
4750 let mut deployment = Deployment::new();
4751
4752 let mut flow = FlowBuilder::new();
4753 let external = flow.external::<()>();
4754 let p1 = flow.process::<()>();
4755
4756 let my_stream = p1.source_iter(q!(1..=5i32));
4758
4759 let stream_ref = my_stream.by_ref();
4760
4761 let out_port = p1
4763 .source_iter(q!([()]))
4764 .map(q!(|_| stream_ref.len() as i32))
4765 .send_bincode_external(&external);
4766
4767 my_stream.for_each(q!(|_| {}));
4769
4770 let nodes = flow
4771 .with_default_optimize()
4772 .with_process(&p1, deployment.Localhost())
4773 .with_external(&external, deployment.Localhost())
4774 .deploy(&mut deployment);
4775
4776 deployment.deploy().await.unwrap();
4777
4778 let mut out_recv = nodes.connect(out_port).await;
4779
4780 deployment.start().await.unwrap();
4781
4782 let result = out_recv.next().await.unwrap();
4783 assert_eq!(result, 5);
4785 }
4786
4787 #[cfg(feature = "deploy")]
4788 #[tokio::test]
4789 async fn test_stream_ref_contents() {
4790 let mut deployment = Deployment::new();
4791
4792 let mut flow = FlowBuilder::new();
4793 let external = flow.external::<()>();
4794 let p1 = flow.process::<()>();
4795
4796 let my_stream = p1.source_iter(q!(1..=3i32));
4798
4799 let stream_ref = my_stream.by_ref();
4800
4801 let out_port = p1
4803 .source_iter(q!([()]))
4804 .map(q!(|_| stream_ref.iter().sum::<i32>()))
4805 .send_bincode_external(&external);
4806
4807 my_stream.for_each(q!(|_| {}));
4808
4809 let nodes = flow
4810 .with_default_optimize()
4811 .with_process(&p1, deployment.Localhost())
4812 .with_external(&external, deployment.Localhost())
4813 .deploy(&mut deployment);
4814
4815 deployment.deploy().await.unwrap();
4816
4817 let mut out_recv = nodes.connect(out_port).await;
4818
4819 deployment.start().await.unwrap();
4820
4821 let result = out_recv.next().await.unwrap();
4822 assert_eq!(result, 6);
4824 }
4825
4826 #[cfg(feature = "deploy")]
4827 #[tokio::test]
4828 async fn test_stream_ref_no_consumer() {
4829 let mut deployment = Deployment::new();
4830
4831 let mut flow = FlowBuilder::new();
4832 let external = flow.external::<()>();
4833 let p1 = flow.process::<()>();
4834
4835 let my_stream = p1.source_iter(q!(1..=4i32));
4837
4838 let stream_ref = my_stream.by_ref();
4839
4840 let out_port = p1
4841 .source_iter(q!([()]))
4842 .map(q!(|_| stream_ref.len() as i32))
4843 .send_bincode_external(&external);
4844
4845 let nodes = flow
4846 .with_default_optimize()
4847 .with_process(&p1, deployment.Localhost())
4848 .with_external(&external, deployment.Localhost())
4849 .deploy(&mut deployment);
4850
4851 deployment.deploy().await.unwrap();
4852
4853 let mut out_recv = nodes.connect(out_port).await;
4854
4855 deployment.start().await.unwrap();
4856
4857 let result = out_recv.next().await.unwrap();
4858 assert_eq!(result, 4);
4859 }
4860
4861 #[cfg(feature = "deploy")]
4862 #[tokio::test]
4863 async fn test_stream_mut() {
4864 let mut deployment = Deployment::new();
4865
4866 let mut flow = FlowBuilder::new();
4867 let external = flow.external::<()>();
4868 let p1 = flow.process::<()>();
4869
4870 let my_stream = p1.source_iter(q!(1..=5i32));
4872
4873 let stream_mut = my_stream.by_mut();
4874
4875 let out_port = p1
4877 .source_iter(q!([()]))
4878 .map(q!(|_| {
4879 stream_mut.retain(|x| *x > 3);
4880 stream_mut.len() as i32
4881 }))
4882 .send_bincode_external(&external);
4883
4884 my_stream.for_each(q!(|_| {}));
4885
4886 let nodes = flow
4887 .with_default_optimize()
4888 .with_process(&p1, deployment.Localhost())
4889 .with_external(&external, deployment.Localhost())
4890 .deploy(&mut deployment);
4891
4892 deployment.deploy().await.unwrap();
4893
4894 let mut out_recv = nodes.connect(out_port).await;
4895
4896 deployment.start().await.unwrap();
4897
4898 let result = out_recv.next().await.unwrap();
4899 assert_eq!(result, 2);
4901 }
4902
4903 #[cfg(feature = "sim")]
4907 #[test]
4908 fn sim_map_with_mut_on_unordered_explores_multiple_states() {
4909 use crate::live_collections::sliced::sliced;
4910 use crate::live_collections::stream::ExactlyOnce;
4911 use crate::properties::manual_proof;
4912
4913 let mut flow = FlowBuilder::new();
4914 let node = flow.process::<()>();
4915
4916 let (trigger_send, trigger) = node.sim_input::<i32, TotalOrder, ExactlyOnce>();
4917
4918 let out_recv = sliced! {
4919 let batch = use(trigger, nondet!());
4920 let counter = batch.location().source_iter(q!(vec![0i32]))
4921 .fold(q!(|| 0i32), q!(|acc, v| *acc += v));
4922 let counter_mut = counter.by_mut();
4923 let items = batch.location().source_iter(q!(vec![1i32, 2])).weaken_ordering::<NoOrder>();
4924 items.map(q!(
4925 |x| {
4926 *counter_mut += x;
4927 *counter_mut
4928 },
4929 commutative = manual_proof!()
4930 ))
4931 }
4932 .sim_output();
4933
4934 let count = flow.sim().exhaustive(async || {
4935 trigger_send.send(1);
4936 let _all: Vec<i32> = out_recv.collect_sorted().await;
4937 });
4938
4939 assert_eq!(
4940 count, 2,
4941 "Expected 2 simulation instances due to mut on unordered input, got {}",
4942 count
4943 );
4944 }
4945
4946 #[cfg(feature = "sim")]
4950 #[test]
4951 fn sim_scan_with_ref_capture() {
4952 use crate::live_collections::sliced::sliced;
4953 use crate::live_collections::stream::ExactlyOnce;
4954
4955 let mut flow = FlowBuilder::new();
4956 let node = flow.process::<()>();
4957
4958 let (trigger_send, trigger) = node.sim_input::<i32, TotalOrder, ExactlyOnce>();
4959
4960 let out_recv = sliced! {
4961 let batch = use(trigger, nondet!());
4962 let offset = batch
4963 .location()
4964 .source_iter(q!(vec![10i32]))
4965 .fold(q!(|| 0i32), q!(|acc, v| *acc += v));
4966 let offset_ref = offset.by_ref();
4967 batch
4968 .location()
4969 .source_iter(q!(vec![1i32, 2, 3]))
4970 .scan(
4971 q!(|| 0i32),
4972 q!(move |acc: &mut i32, x| {
4973 *acc += x + *offset_ref;
4974 Some(*acc)
4975 }),
4976 )
4977 }
4978 .sim_output();
4979
4980 let count = flow.sim().exhaustive(async || {
4981 trigger_send.send(1);
4982 let all: Vec<i32> = out_recv.collect().await;
4983 assert_eq!(all, vec![11, 23, 36]);
4988 });
4989
4990 assert_eq!(
4991 count, 1,
4992 "Expected a single simulation instance for a totally-ordered scan, got {}",
4993 count
4994 );
4995 }
4996
4997 #[cfg(feature = "sim")]
5001 #[test]
5002 #[ignore = "observe_nondet not yet supported for top-level bounded inputs (https://github.com/hydro-project/hydro/issues/2950)"]
5003 fn sim_map_with_mut_on_unordered_top_level() {
5004 use crate::properties::manual_proof;
5005
5006 let mut flow = FlowBuilder::new();
5007 let node = flow.process::<()>();
5008
5009 let counter = node
5010 .source_iter(q!(vec![0i32]))
5011 .fold(q!(|| 0i32), q!(|acc, v| *acc += v));
5012 let counter_mut = counter.by_mut();
5013
5014 let out_recv = node
5015 .source_iter(q!(vec![1i32, 2]))
5016 .weaken_ordering::<NoOrder>()
5017 .map(q!(
5018 |x| {
5019 *counter_mut += x;
5020 *counter_mut
5021 },
5022 commutative = manual_proof!()
5023 ))
5024 .assume_ordering::<TotalOrder>(nondet!())
5025 .sim_output();
5026
5027 counter.into_stream().for_each(q!(|_| {}));
5028
5029 let count = flow.sim().exhaustive(async || {
5030 let _all: Vec<i32> = out_recv.collect().await;
5031 });
5032
5033 assert_eq!(
5034 count, 2,
5035 "Expected 2 simulation instances due to mut on unordered input, got {}",
5036 count
5037 );
5038 }
5039}