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: 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 Stream {
235 location: stream.location.clone(),
236 flow_state: stream.flow_state.clone(),
237 ir_node: RefCell::new(HydroNode::Cast {
238 inner: Box::new(stream.ir_node.replace(HydroNode::Placeholder)),
239 metadata: new_meta,
240 }),
241 _phantom: PhantomData,
242 }
243 }
244}
245
246impl<'a, T, L, B: Boundedness, R: Retries> From<Stream<T, L, B, TotalOrder, R>>
247 for Stream<T, L, B, NoOrder, R>
248where
249 L: Location<'a>,
250{
251 fn from(stream: Stream<T, L, B, TotalOrder, R>) -> Stream<T, L, B, NoOrder, R> {
252 stream.weaken_ordering()
253 }
254}
255
256impl<'a, T, L, B: Boundedness, O: Ordering> From<Stream<T, L, B, O, ExactlyOnce>>
257 for Stream<T, L, B, O, AtLeastOnce>
258where
259 L: Location<'a>,
260{
261 fn from(stream: Stream<T, L, B, O, ExactlyOnce>) -> Stream<T, L, B, O, AtLeastOnce> {
262 stream.weaken_retries()
263 }
264}
265
266impl<'a, T, L, O: Ordering, R: Retries> DeferTick for Stream<T, Tick<L>, Bounded, O, R>
267where
268 L: Location<'a>,
269{
270 fn defer_tick(self) -> Self {
271 Stream::defer_tick(self)
272 }
273}
274
275impl<'a, T, L, O: Ordering, R: Retries> CycleCollection<'a, TickCycle>
276 for Stream<T, Tick<L>, Bounded, O, R>
277where
278 L: Location<'a>,
279{
280 type Location = Tick<L>;
281
282 fn create_source(cycle_id: CycleId, location: Tick<L>) -> Self {
283 Stream::new(
284 location.clone(),
285 HydroNode::CycleSource {
286 cycle_id,
287 metadata: location.new_node_metadata(Self::collection_kind()),
288 },
289 )
290 }
291}
292
293impl<'a, T, L, O: Ordering, R: Retries> CycleCollectionWithInitial<'a, TickCycle>
294 for Stream<T, Tick<L>, Bounded, O, R>
295where
296 L: Location<'a>,
297{
298 type Location = Tick<L>;
299
300 fn location(&self) -> &Self::Location {
301 self.location()
302 }
303
304 fn create_source_with_initial(cycle_id: CycleId, initial: Self, location: Tick<L>) -> Self {
305 let from_previous_tick: Stream<T, Tick<L>, Bounded, O, R> = Stream::new(
306 location.clone(),
307 HydroNode::DeferTick {
308 input: Box::new(HydroNode::CycleSource {
309 cycle_id,
310 metadata: location.new_node_metadata(Self::collection_kind()),
311 }),
312 metadata: location.new_node_metadata(Self::collection_kind()),
313 },
314 );
315
316 from_previous_tick.chain(initial.filter_if(location.optional_first_tick(q!(())).is_some()))
317 }
318}
319
320impl<'a, T, L, O: Ordering, R: Retries> ReceiverComplete<'a, TickCycle>
321 for Stream<T, Tick<L>, Bounded, O, R>
322where
323 L: Location<'a>,
324{
325 fn complete(self, cycle_id: CycleId, expected_location: LocationId) {
326 assert_eq!(
327 Location::id(&self.location),
328 expected_location,
329 "locations do not match"
330 );
331 self.location
332 .flow_state()
333 .borrow_mut()
334 .push_root(HydroRoot::CycleSink {
335 cycle_id,
336 input: Box::new(self.ir_node.replace(HydroNode::Placeholder)),
337 op_metadata: HydroIrOpMetadata::new(),
338 });
339 }
340}
341
342impl<'a, T, L, B: Boundedness, O: Ordering, R: Retries> CycleCollection<'a, ForwardRef>
343 for Stream<T, L, B, O, R>
344where
345 L: Location<'a>,
346{
347 type Location = L;
348
349 fn create_source(cycle_id: CycleId, location: L) -> Self {
350 Stream::new(
351 location.clone(),
352 HydroNode::CycleSource {
353 cycle_id,
354 metadata: location.new_node_metadata(Self::collection_kind()),
355 },
356 )
357 }
358}
359
360impl<'a, T, L, B: Boundedness, O: Ordering, R: Retries> ReceiverComplete<'a, ForwardRef>
361 for Stream<T, L, B, O, R>
362where
363 L: Location<'a>,
364{
365 fn complete(self, cycle_id: CycleId, expected_location: LocationId) {
366 assert_eq!(
367 Location::id(&self.location),
368 expected_location,
369 "locations do not match"
370 );
371 self.location
372 .flow_state()
373 .borrow_mut()
374 .push_root(HydroRoot::CycleSink {
375 cycle_id,
376 input: Box::new(self.ir_node.replace(HydroNode::Placeholder)),
377 op_metadata: HydroIrOpMetadata::new(),
378 });
379 }
380}
381
382impl<'a, T, L, B: Boundedness, O: Ordering, R: Retries> Clone for Stream<T, L, B, O, R>
383where
384 T: Clone,
385 L: Location<'a>,
386{
387 fn clone(&self) -> Self {
388 if !matches!(self.ir_node.borrow().deref(), HydroNode::Tee { .. }) {
389 let orig_ir_node = self.ir_node.replace(HydroNode::Placeholder);
390 *self.ir_node.borrow_mut() = HydroNode::Tee {
391 inner: SharedNode(Rc::new(RefCell::new(orig_ir_node))),
392 metadata: self.location.new_node_metadata(Self::collection_kind()),
393 };
394 }
395
396 let HydroNode::Tee { inner, metadata } = &*self.ir_node.borrow() else {
397 unreachable!()
398 };
399 Stream {
400 location: self.location.clone(),
401 flow_state: self.flow_state.clone(),
402 ir_node: HydroNode::Tee {
403 inner: SharedNode(inner.0.clone()),
404 metadata: metadata.clone(),
405 }
406 .into(),
407 _phantom: PhantomData,
408 }
409 }
410}
411
412impl<'a, T, L, B: Boundedness, O: Ordering, R: Retries> Stream<T, L, B, O, R>
413where
414 L: Location<'a>,
415{
416 pub(crate) fn new(location: L, ir_node: HydroNode) -> Self {
417 debug_assert_eq!(ir_node.metadata().location_id, Location::id(&location));
418 debug_assert_eq!(ir_node.metadata().collection_kind, Self::collection_kind());
419
420 let flow_state = location.flow_state().clone();
421 Stream {
422 location,
423 flow_state,
424 ir_node: RefCell::new(ir_node),
425 _phantom: PhantomData,
426 }
427 }
428
429 pub fn location(&self) -> &L {
431 &self.location
432 }
433
434 pub fn by_ref(&self) -> crate::handoff_ref::StreamRef<'a, '_, T, L>
439 where
440 B: IsBounded,
441 {
442 crate::handoff_ref::StreamRef::new(&self.ir_node)
443 }
444
445 pub fn by_mut(&self) -> crate::handoff_ref::StreamMut<'a, '_, T, L>
448 where
449 B: IsBounded,
450 {
451 crate::handoff_ref::StreamMut::new(&self.ir_node)
452 }
453
454 pub fn weaken_consistency(self) -> Stream<T, L::DropConsistency, B, O, R>
457 where
458 L: Location<'a>,
459 {
460 if L::consistency()
461 .is_none_or(|c| c == crate::location::dynamic::ClusterConsistency::NoConsistency)
462 {
463 Stream::new(
465 self.location.drop_consistency(),
466 self.ir_node.replace(HydroNode::Placeholder),
467 )
468 } else {
469 Stream::new(
470 self.location.drop_consistency(),
471 HydroNode::Cast {
472 inner: Box::new(self.ir_node.replace(HydroNode::Placeholder)),
473 metadata: self.location.drop_consistency().new_node_metadata(Stream::<
474 T,
475 L::DropConsistency,
476 B,
477 O,
478 R,
479 >::collection_kind(
480 )),
481 },
482 )
483 }
484 }
485
486 pub fn assert_has_consistency_of<L2: Location<'a, DropConsistency = L::DropConsistency>>(
490 self,
491 _proof: impl crate::properties::ConsistencyProof,
492 ) -> Stream<T, L2, B, O, R>
493 where
494 L: Location<'a>,
495 {
496 if L::consistency() == L2::consistency() {
497 Stream::new(
498 self.location.with_consistency_of(),
499 self.ir_node.replace(HydroNode::Placeholder),
500 )
501 } else {
502 Stream::new(
503 self.location.with_consistency_of(),
504 HydroNode::AssertIsConsistent {
505 inner: Box::new(self.ir_node.replace(HydroNode::Placeholder)),
506 trusted: false,
507 metadata: self
508 .location
509 .clone()
510 .with_consistency_of::<L2>()
511 .new_node_metadata(Stream::<T, L2, B, O, R>::collection_kind()),
512 },
513 )
514 }
515 }
516
517 pub(crate) fn assert_has_consistency_of_trusted<
518 L2: Location<'a, DropConsistency = L::DropConsistency>,
519 >(
520 self,
521 _proof: impl crate::properties::ConsistencyProof,
522 ) -> Stream<T, L2, B, O, R>
523 where
524 L: Location<'a>,
525 {
526 if L::consistency() == L2::consistency() {
527 Stream::new(
528 self.location.with_consistency_of(),
529 self.ir_node.replace(HydroNode::Placeholder),
530 )
531 } else {
532 Stream::new(
533 self.location.with_consistency_of(),
534 HydroNode::AssertIsConsistent {
535 inner: Box::new(self.ir_node.replace(HydroNode::Placeholder)),
536 trusted: true,
537 metadata: self
538 .location
539 .clone()
540 .with_consistency_of::<L2>()
541 .new_node_metadata(Stream::<T, L2, B, O, R>::collection_kind()),
542 },
543 )
544 }
545 }
546
547 pub(crate) fn collection_kind() -> CollectionKind {
548 CollectionKind::Stream {
549 bound: B::BOUND_KIND,
550 order: O::ORDERING_KIND,
551 retry: R::RETRIES_KIND,
552 element_type: quote_type::<T>().into(),
553 }
554 }
555
556 pub fn map<U, F, C, I, const WAS_MUT: bool>(
576 self,
577 f: impl IntoQuotedMut<'a, F, L, StreamMapFuncAlgebra<C, I>>,
578 ) -> Stream<U, L, B, O, R>
579 where
580 F: FnMut(T) -> U + 'a,
581 C: ValidMutCommutativityFor<F, T, U, O, WAS_MUT>,
582 I: ValidMutIdempotenceFor<F, T, U, R, WAS_MUT>,
583 {
584 let f = crate::handoff_ref::with_ref_capture(|| {
585 let (expr, proof) = f.splice_fnmut1_ctx_props(&self.location);
586 proof.register_proof(&expr);
587 expr.into()
588 });
589 Stream::new(
590 self.location.clone(),
591 HydroNode::Map {
592 f,
593 input: Box::new(self.ir_node.replace(HydroNode::Placeholder)),
594 metadata: self
595 .location
596 .new_node_metadata(Stream::<U, L, B, O, R>::collection_kind()),
597 },
598 )
599 }
600
601 pub fn flat_map_ordered<U, I, F, C, Idemp, const WAS_MUT: bool>(
626 self,
627 f: impl IntoQuotedMut<'a, F, L, StreamMapFuncAlgebra<C, Idemp>>,
628 ) -> Stream<U, L, B, O, R>
629 where
630 I: IntoIterator<Item = U>,
631 F: FnMut(T) -> I + 'a,
632 C: ValidMutCommutativityFor<F, T, I, O, WAS_MUT>,
633 Idemp: ValidMutIdempotenceFor<F, T, I, R, WAS_MUT>,
634 {
635 let f = crate::handoff_ref::with_ref_capture(|| {
636 let (expr, proof) = f.splice_fnmut1_ctx_props(&self.location);
637 proof.register_proof(&expr);
638 expr.into()
639 });
640 Stream::new(
641 self.location.clone(),
642 HydroNode::FlatMap {
643 f,
644 input: Box::new(self.ir_node.replace(HydroNode::Placeholder)),
645 metadata: self
646 .location
647 .new_node_metadata(Stream::<U, L, B, O, R>::collection_kind()),
648 },
649 )
650 }
651
652 pub fn flat_map_unordered<U, I, F, C, Idemp, const WAS_MUT: bool>(
679 self,
680 f: impl IntoQuotedMut<'a, F, L, StreamMapFuncAlgebra<C, Idemp>>,
681 ) -> Stream<U, L, B, NoOrder, R>
682 where
683 I: IntoIterator<Item = U>,
684 F: FnMut(T) -> I + 'a,
685 C: ValidMutCommutativityFor<F, T, I, O, WAS_MUT>,
686 Idemp: ValidMutIdempotenceFor<F, T, I, R, WAS_MUT>,
687 {
688 let f = crate::handoff_ref::with_ref_capture(|| {
689 let (expr, proof) = f.splice_fnmut1_ctx_props(&self.location);
690 proof.register_proof(&expr);
691 expr.into()
692 });
693 Stream::new(
694 self.location.clone(),
695 HydroNode::FlatMap {
696 f,
697 input: Box::new(self.ir_node.replace(HydroNode::Placeholder)),
698 metadata: self
699 .location
700 .new_node_metadata(Stream::<U, L, B, NoOrder, R>::collection_kind()),
701 },
702 )
703 }
704
705 pub fn flatten_ordered<U>(self) -> Stream<U, L, B, O, R>
728 where
729 T: IntoIterator<Item = U>,
730 {
731 self.flat_map_ordered(q!(|d| d))
732 }
733
734 pub fn flatten_unordered<U>(self) -> Stream<U, L, B, NoOrder, R>
761 where
762 T: IntoIterator<Item = U>,
763 {
764 self.flat_map_unordered(q!(|d| d))
765 }
766
767 pub fn flat_map_stream_blocking<U, S, F, C, Idemp, const WAS_MUT: bool>(
771 self,
772 f: impl IntoQuotedMut<'a, F, L, StreamMapFuncAlgebra<C, Idemp>>,
773 ) -> Stream<U, L, B, O, R>
774 where
775 S: futures::Stream<Item = U>,
776 F: FnMut(T) -> S + 'a,
777 C: ValidMutCommutativityFor<F, T, S, O, WAS_MUT>,
778 Idemp: ValidMutIdempotenceFor<F, T, S, R, WAS_MUT>,
779 {
780 let f = crate::handoff_ref::with_ref_capture(|| {
781 let (expr, proof) = f.splice_fnmut1_ctx_props(&self.location);
782 proof.register_proof(&expr);
783 expr.into()
784 });
785 Stream::new(
786 self.location.clone(),
787 HydroNode::FlatMapStreamBlocking {
788 f,
789 input: Box::new(self.ir_node.replace(HydroNode::Placeholder)),
790 metadata: self
791 .location
792 .new_node_metadata(Stream::<U, L, B, O, R>::collection_kind()),
793 },
794 )
795 }
796
797 pub fn flatten_stream_blocking<U>(self) -> Stream<U, L, B, O, R>
801 where
802 T: futures::Stream<Item = U>,
803 {
804 self.flat_map_stream_blocking(q!(|d| d))
805 }
806
807 pub fn filter<F, C, Idemp, const WAS_MUT: bool>(
832 self,
833 f: impl IntoQuotedMut<'a, F, L, StreamMapFuncAlgebra<C, Idemp>>,
834 ) -> Self
835 where
836 F: FnMut(&T) -> bool + 'a,
837 C: ValidMutBorrowCommutativityFor<F, T, bool, O, WAS_MUT>,
838 Idemp: ValidMutBorrowIdempotenceFor<F, T, bool, R, WAS_MUT>,
839 {
840 let f = crate::handoff_ref::with_ref_capture(|| {
841 let (expr, proof) = f.splice_fnmut1_borrow_ctx_props(&self.location);
842 proof.register_proof(&expr);
843 expr.into()
844 });
845 Stream::new(
846 self.location.clone(),
847 HydroNode::Filter {
848 f,
849 input: Box::new(self.ir_node.replace(HydroNode::Placeholder)),
850 metadata: self.location.new_node_metadata(Self::collection_kind()),
851 },
852 )
853 }
854
855 pub fn partition<F, C, Idemp, const WAS_MUT: bool>(
890 self,
891 f: impl IntoQuotedMut<'a, F, L, StreamMapFuncAlgebra<C, Idemp>>,
892 ) -> (Stream<T, L, B, O, R>, Stream<T, L, B, O, R>)
893 where
894 F: FnMut(&T) -> bool + 'a,
895 C: ValidMutBorrowCommutativityFor<F, T, bool, O, WAS_MUT>,
896 Idemp: ValidMutBorrowIdempotenceFor<F, T, bool, R, WAS_MUT>,
897 {
898 let f = crate::handoff_ref::with_ref_capture(|| {
899 let (expr, proof) = f.splice_fnmut1_borrow_ctx_props(&self.location);
900 proof.register_proof(&expr);
901 expr.into()
902 });
903 let shared = SharedNode(Rc::new(RefCell::new(
904 self.ir_node.replace(HydroNode::Placeholder),
905 )));
906
907 let true_stream = Stream::new(
908 self.location.clone(),
909 HydroNode::Partition {
910 inner: SharedNode(shared.0.clone()),
911 f: f.clone(),
912 is_true: true,
913 metadata: self.location.new_node_metadata(Self::collection_kind()),
914 },
915 );
916
917 let false_stream = Stream::new(
918 self.location.clone(),
919 HydroNode::Partition {
920 inner: SharedNode(shared.0),
921 f,
922 is_true: false,
923 metadata: self.location.new_node_metadata(Self::collection_kind()),
924 },
925 );
926
927 (true_stream, false_stream)
928 }
929
930 pub fn filter_map<U, F, C, Idemp, const WAS_MUT: bool>(
950 self,
951 f: impl IntoQuotedMut<'a, F, L, StreamMapFuncAlgebra<C, Idemp>>,
952 ) -> Stream<U, L, B, O, R>
953 where
954 F: FnMut(T) -> Option<U> + 'a,
955 C: ValidMutCommutativityFor<F, T, Option<U>, O, WAS_MUT>,
956 Idemp: ValidMutIdempotenceFor<F, T, Option<U>, R, WAS_MUT>,
957 {
958 let f = crate::handoff_ref::with_ref_capture(|| {
959 let (expr, proof) = f.splice_fnmut1_ctx_props(&self.location);
960 proof.register_proof(&expr);
961 expr.into()
962 });
963 Stream::new(
964 self.location.clone(),
965 HydroNode::FilterMap {
966 f,
967 input: Box::new(self.ir_node.replace(HydroNode::Placeholder)),
968 metadata: self
969 .location
970 .new_node_metadata(Stream::<U, L, B, O, R>::collection_kind()),
971 },
972 )
973 }
974
975 pub fn cross_singleton<O2>(
1000 self,
1001 other: impl Into<Optional<O2, L, Bounded>>,
1002 ) -> Stream<(T, O2), L, B, O, R>
1003 where
1004 O2: Clone,
1005 {
1006 let other: Optional<O2, L, Bounded> = other.into();
1007 check_matching_location(&self.location, &other.location);
1008
1009 Stream::new(
1010 self.location.clone(),
1011 HydroNode::CrossSingleton {
1012 left: Box::new(self.ir_node.replace(HydroNode::Placeholder)),
1013 right: Box::new(other.ir_node.replace(HydroNode::Placeholder)),
1014 metadata: self
1015 .location
1016 .new_node_metadata(Stream::<(T, O2), L, B, O, R>::collection_kind()),
1017 },
1018 )
1019 }
1020
1021 pub fn filter_if(self, signal: Singleton<bool, L, Bounded>) -> Stream<T, L, B, O, R> {
1053 self.cross_singleton(signal.filter(q!(|b| *b)))
1054 .map(q!(|(d, _)| d))
1055 }
1056
1057 #[deprecated(note = "use `filter_if` with `Optional::is_some()` instead")]
1092 pub fn filter_if_some<U>(self, signal: Optional<U, L, Bounded>) -> Stream<T, L, B, O, R> {
1093 self.filter_if(signal.is_some())
1094 }
1095
1096 #[deprecated(note = "use `filter_if` with `!Optional::is_some()` instead")]
1131 pub fn filter_if_none<U>(self, other: Optional<U, L, Bounded>) -> Stream<T, L, B, O, R> {
1132 self.filter_if(other.is_none())
1133 }
1134
1135 pub fn cross_product<T2, B2: Boundedness, O2: Ordering, R2: Retries>(
1160 self,
1161 other: Stream<T2, L, B2, O2, R2>,
1162 ) -> Stream<(T, T2), L, B, B2::PreserveOrderIfBounded<O>, <R as MinRetries<R2>>::Min>
1163 where
1164 T: Clone,
1165 T2: Clone,
1166 R: MinRetries<R2>,
1167 {
1168 self.map(q!(|v| ((), v)))
1169 .join(other.map(q!(|v| ((), v))))
1170 .map(q!(|((), (v1, v2))| (v1, v2)))
1171 }
1172
1173 pub fn unique(self) -> Stream<T, L, B, O, ExactlyOnce>
1192 where
1193 T: Eq + Hash,
1194 {
1195 Stream::new(
1196 self.location.clone(),
1197 HydroNode::Unique {
1198 input: Box::new(self.ir_node.replace(HydroNode::Placeholder)),
1199 metadata: self
1200 .location
1201 .new_node_metadata(Stream::<T, L, B, O, ExactlyOnce>::collection_kind()),
1202 },
1203 )
1204 }
1205
1206 pub fn filter_not_in<O2: Ordering, B2>(self, other: Stream<T, L, B2, O2, R>) -> Self
1232 where
1233 T: Eq + Hash,
1234 B2: IsBounded,
1235 {
1236 check_matching_location(&self.location, &other.location);
1237
1238 Stream::new(
1239 self.location.clone(),
1240 HydroNode::Difference {
1241 pos: Box::new(self.ir_node.replace(HydroNode::Placeholder)),
1242 neg: Box::new(other.ir_node.replace(HydroNode::Placeholder)),
1243 metadata: self
1244 .location
1245 .new_node_metadata(Stream::<T, L, Bounded, O, R>::collection_kind()),
1246 },
1247 )
1248 }
1249
1250 pub fn inspect<F, C, Idemp, const WAS_MUT: bool>(
1271 self,
1272 f: impl IntoQuotedMut<'a, F, L::DropConsistency, StreamMapFuncAlgebra<C, Idemp>>,
1273 ) -> Self
1274 where
1275 F: FnMut(&T) + 'a,
1276 C: ValidMutBorrowCommutativityFor<F, T, (), O, WAS_MUT>,
1277 Idemp: ValidMutBorrowIdempotenceFor<F, T, (), R, WAS_MUT>,
1278 {
1279 let f = crate::handoff_ref::with_ref_capture(|| {
1280 let (expr, proof) = f.splice_fnmut1_borrow_ctx_props(&self.location.drop_consistency());
1281 proof.register_proof(&expr);
1282 expr.into()
1283 });
1284
1285 Stream::new(
1286 self.location.clone(),
1287 HydroNode::Inspect {
1288 f,
1289 input: Box::new(self.ir_node.replace(HydroNode::Placeholder)),
1290 metadata: self.location.new_node_metadata(Self::collection_kind()),
1291 },
1292 )
1293 }
1294
1295 pub fn for_each<F: FnMut(T) + 'a, C, I>(
1311 self,
1312 f: impl IntoQuotedMut<'a, F, L, AggFuncAlgebra<C, I>>,
1313 ) where
1314 C: ValidCommutativityFor<O>,
1315 I: ValidIdempotenceFor<R>,
1316 {
1317 let f = crate::handoff_ref::with_ref_capture(|| {
1318 let (f, proof) = f.splice_fnmut1_ctx_props(&self.location);
1319 proof.register_proof(&f);
1320 f.into()
1321 });
1322 self.location
1323 .flow_state()
1324 .borrow_mut()
1325 .push_root(HydroRoot::ForEach {
1326 input: Box::new(self.ir_node.replace(HydroNode::Placeholder)),
1327 f,
1328 op_metadata: HydroIrOpMetadata::new(),
1329 });
1330 }
1331
1332 pub fn dest_sink<S>(self, sink: impl QuotedWithContext<'a, S, L>)
1338 where
1339 O: IsOrdered,
1340 R: IsExactlyOnce,
1341 S: 'a + futures::Sink<T> + Unpin,
1342 {
1343 self.location
1344 .flow_state()
1345 .borrow_mut()
1346 .push_root(HydroRoot::DestSink {
1347 sink: sink.splice_typed_ctx(&self.location).into(),
1348 input: Box::new(self.ir_node.replace(HydroNode::Placeholder)),
1349 op_metadata: HydroIrOpMetadata::new(),
1350 });
1351 }
1352
1353 pub fn enumerate(self) -> Stream<(usize, T), L, B, O, R>
1373 where
1374 O: IsOrdered,
1375 R: IsExactlyOnce,
1376 {
1377 Stream::new(
1378 self.location.clone(),
1379 HydroNode::Enumerate {
1380 input: Box::new(self.ir_node.replace(HydroNode::Placeholder)),
1381 metadata: self.location.new_node_metadata(Stream::<
1382 (usize, T),
1383 L,
1384 B,
1385 TotalOrder,
1386 ExactlyOnce,
1387 >::collection_kind()),
1388 },
1389 )
1390 }
1391
1392 pub fn fold<A, I, F, C, Idemp, M, B2: SingletonBound>(
1416 self,
1417 init: impl IntoQuotedMut<'a, I, L>,
1418 comb: impl IntoQuotedMut<'a, F, L, AggFuncAlgebra<C, Idemp, M>>,
1419 ) -> Singleton<A, L, B2>
1420 where
1421 I: Fn() -> A + 'a,
1422 F: 'a + Fn(&mut A, T),
1423 C: ValidCommutativityFor<O>,
1424 Idemp: ValidIdempotenceFor<R>,
1425 B: ApplyMonotoneStream<M, B2>,
1426 {
1427 let init = init.splice_fn0_ctx(&self.location).into();
1428 let (comb, proof) = comb.splice_fn2_borrow_mut_ctx_props(&self.location);
1429 proof.register_proof(&comb);
1430
1431 let nondet = nondet!();
1434 let retried: Stream<T, L::DropConsistency, B, O, ExactlyOnce> = self.assume_retries(nondet);
1435
1436 let core = HydroNode::Fold {
1437 init,
1438 acc: comb.into(),
1439 input: Box::new(retried.ir_node.replace(HydroNode::Placeholder)),
1440 metadata: retried
1441 .location
1442 .new_node_metadata(Singleton::<A, L::DropConsistency, B2>::collection_kind()),
1443 };
1448
1449 Singleton::new(retried.location.clone(), core)
1450 .assert_has_consistency_of(manual_proof!())
1451 }
1452
1453 pub fn reduce<F, C, Idemp>(
1476 self,
1477 comb: impl IntoQuotedMut<'a, F, L, AggFuncAlgebra<C, Idemp>>,
1478 ) -> Optional<T, L, B>
1479 where
1480 F: Fn(&mut T, T) + 'a,
1481 C: ValidCommutativityFor<O>,
1482 Idemp: ValidIdempotenceFor<R>,
1483 {
1484 let (f, proof) = comb.splice_fn2_borrow_mut_ctx_props(&self.location);
1485 proof.register_proof(&f);
1486
1487 let nondet = nondet!();
1488 let ordered_etc: Stream<T, L::DropConsistency, B> =
1489 self.assume_retries(nondet).assume_ordering(nondet);
1490
1491 let core = HydroNode::Reduce {
1492 f: f.into(),
1493 input: Box::new(ordered_etc.ir_node.replace(HydroNode::Placeholder)),
1494 metadata: ordered_etc
1495 .location
1496 .new_node_metadata(Optional::<T, L::DropConsistency, B>::collection_kind()),
1497 };
1498
1499 Optional::new(ordered_etc.location.clone(), core)
1500 .assert_has_consistency_of(manual_proof!())
1501 }
1502
1503 pub fn max(self) -> Optional<T, L, B>
1523 where
1524 T: Ord,
1525 {
1526 self.assume_retries_trusted::<ExactlyOnce>(nondet!())
1527 .assume_ordering_trusted_bounded::<TotalOrder>(
1528 nondet!(),
1529 )
1530 .reduce(q!(|curr, new| {
1531 if new > *curr {
1532 *curr = new;
1533 }
1534 }))
1535 }
1536
1537 pub fn min(self) -> Optional<T, L, B>
1557 where
1558 T: Ord,
1559 {
1560 self.assume_retries_trusted::<ExactlyOnce>(nondet!())
1561 .assume_ordering_trusted_bounded::<TotalOrder>(
1562 nondet!(),
1563 )
1564 .reduce(q!(|curr, new| {
1565 if new < *curr {
1566 *curr = new;
1567 }
1568 }))
1569 }
1570
1571 pub fn first(self) -> Optional<T, L, B>
1594 where
1595 O: IsOrdered,
1596 {
1597 self.make_totally_ordered()
1598 .assume_retries_trusted::<ExactlyOnce>(nondet!())
1599 .generator(q!(|| ()), q!(|_, item| Generate::Return(item)))
1600 .reduce(q!(|_, _| {}))
1601 }
1602
1603 pub fn last(self) -> Optional<T, L, B>
1626 where
1627 O: IsOrdered,
1628 {
1629 self.make_totally_ordered()
1630 .assume_retries_trusted::<ExactlyOnce>(nondet!())
1631 .reduce(q!(|curr, new| *curr = new))
1632 }
1633
1634 pub fn limit(
1657 self,
1658 n: impl QuotedWithContext<'a, usize, L> + Copy + 'a,
1659 ) -> Stream<T, L, B, TotalOrder, ExactlyOnce>
1660 where
1661 O: IsOrdered,
1662 R: IsExactlyOnce,
1663 {
1664 self.generator(
1665 q!(|| 0usize),
1666 q!(move |count, item| {
1667 if *count == n {
1668 Generate::Break
1669 } else {
1670 *count += 1;
1671 if *count == n {
1672 Generate::Return(item)
1673 } else {
1674 Generate::Yield(item)
1675 }
1676 }
1677 }),
1678 )
1679 }
1680
1681 pub fn collect_vec(self) -> Singleton<Vec<T>, L, B>
1707 where
1708 O: IsOrdered,
1709 R: IsExactlyOnce,
1710 {
1711 self.make_totally_ordered().make_exactly_once().fold(
1712 q!(|| vec![]),
1713 q!(|acc, v| {
1714 acc.push(v);
1715 }),
1716 )
1717 }
1718
1719 pub fn scan<A, U, I, F>(
1783 self,
1784 init: impl IntoQuotedMut<'a, I, L>,
1785 f: impl IntoQuotedMut<'a, F, L>,
1786 ) -> Stream<U, L, B, TotalOrder, ExactlyOnce>
1787 where
1788 O: IsOrdered,
1789 R: IsExactlyOnce,
1790 I: Fn() -> A + 'a,
1791 F: Fn(&mut A, T) -> Option<U> + 'a,
1792 {
1793 let init =
1794 crate::handoff_ref::with_ref_capture(|| init.splice_fn0_ctx(&self.location).into());
1795 let f = crate::handoff_ref::with_ref_capture(|| {
1796 f.splice_fn2_borrow_mut_ctx(&self.location).into()
1797 });
1798
1799 Stream::new(
1800 self.location.clone(),
1801 HydroNode::Scan {
1802 init,
1803 acc: f,
1804 input: Box::new(self.ir_node.replace(HydroNode::Placeholder)),
1805 metadata: self.location.new_node_metadata(
1806 Stream::<U, L, B, TotalOrder, ExactlyOnce>::collection_kind(),
1807 ),
1808 },
1809 )
1810 }
1811
1812 pub fn scan_async_blocking<A, U, I, F, Fut>(
1849 self,
1850 init: impl IntoQuotedMut<'a, I, L>,
1851 f: impl IntoQuotedMut<'a, F, L>,
1852 ) -> Stream<U, L, B, TotalOrder, ExactlyOnce>
1853 where
1854 O: IsOrdered,
1855 R: IsExactlyOnce,
1856 I: Fn() -> A + 'a,
1857 F: Fn(&mut A, T) -> Fut + 'a,
1858 Fut: Future<Output = Option<U>> + 'a,
1859 {
1860 let init =
1861 crate::handoff_ref::with_ref_capture(|| init.splice_fn0_ctx(&self.location).into());
1862 let f = crate::handoff_ref::with_ref_capture(|| {
1863 f.splice_fn2_borrow_mut_ctx(&self.location).into()
1864 });
1865
1866 Stream::new(
1867 self.location.clone(),
1868 HydroNode::ScanAsyncBlocking {
1869 init,
1870 acc: f,
1871 input: Box::new(self.ir_node.replace(HydroNode::Placeholder)),
1872 metadata: self.location.new_node_metadata(
1873 Stream::<U, L, B, TotalOrder, ExactlyOnce>::collection_kind(),
1874 ),
1875 },
1876 )
1877 }
1878
1879 pub fn generator<A, U, I, F>(
1922 self,
1923 init: impl IntoQuotedMut<'a, I, L> + Copy,
1924 f: impl IntoQuotedMut<'a, F, L> + Copy,
1925 ) -> Stream<U, L, B, TotalOrder, ExactlyOnce>
1926 where
1927 O: IsOrdered,
1928 R: IsExactlyOnce,
1929 I: Fn() -> A + 'a,
1930 F: Fn(&mut A, T) -> Generate<U> + 'a,
1931 {
1932 let init: ManualExpr<I, _> = ManualExpr::new(move |ctx: &L| init.splice_fn0_ctx(ctx));
1933 let f: ManualExpr<F, _> = ManualExpr::new(move |ctx: &L| f.splice_fn2_borrow_mut_ctx(ctx));
1934
1935 let this = self.make_totally_ordered().make_exactly_once();
1936
1937 let scan_init = crate::handoff_ref::with_ref_capture(|| {
1942 q!(|| None)
1943 .splice_fn0_ctx::<Option<Option<A>>>(&this.location)
1944 .into()
1945 });
1946 let scan_f = crate::handoff_ref::with_ref_capture(|| {
1947 q!(move |state: &mut Option<Option<_>>, v| {
1948 if state.is_none() {
1949 *state = Some(Some(init()));
1950 }
1951 match state {
1952 Some(Some(state_value)) => match f(state_value, v) {
1953 Generate::Yield(out) => Some(Some(out)),
1954 Generate::Return(out) => {
1955 *state = Some(None);
1956 Some(Some(out))
1957 }
1958 Generate::Break => None,
1962 Generate::Continue => Some(None),
1963 },
1964 _ => None,
1966 }
1967 })
1968 .splice_fn2_borrow_mut_ctx::<Option<Option<A>>, T, _>(&this.location)
1969 .into()
1970 });
1971
1972 let scan_node = HydroNode::Scan {
1973 init: scan_init,
1974 acc: scan_f,
1975 input: Box::new(this.ir_node.replace(HydroNode::Placeholder)),
1976 metadata: this.location.new_node_metadata(Stream::<
1977 Option<U>,
1978 L,
1979 B,
1980 TotalOrder,
1981 ExactlyOnce,
1982 >::collection_kind()),
1983 };
1984
1985 let flatten_f = q!(|d| d)
1986 .splice_fn1_ctx::<Option<U>, _>(&this.location)
1987 .into();
1988 let flatten_node = HydroNode::FlatMap {
1989 f: flatten_f,
1990 input: Box::new(scan_node),
1991 metadata: this
1992 .location
1993 .new_node_metadata(Stream::<U, L, B, TotalOrder, ExactlyOnce>::collection_kind()),
1994 };
1995
1996 Stream::new(this.location.clone(), flatten_node)
1997 }
1998
1999 #[cfg(feature = "tokio")]
2008 pub fn sample_every(
2009 self,
2010 interval: impl QuotedWithContext<'a, std::time::Duration, L> + Copy + 'a,
2011 nondet: NonDet,
2012 ) -> Stream<T, L::DropConsistency, Unbounded, O, AtLeastOnce>
2013 where
2014 L: TopLevel<'a>,
2015 {
2016 let samples = self.location.source_interval(interval);
2017
2018 let tick = self.location.tick();
2019 self.batch(&tick, nondet)
2020 .filter_if(samples.batch(&tick, nondet).first().is_some())
2021 .all_ticks()
2022 .weaken_retries()
2023 }
2024
2025 #[cfg(feature = "tokio")]
2035 pub fn timeout(
2036 self,
2037 duration: impl QuotedWithContext<'a, std::time::Duration, Tick<L::DropConsistency>> + Copy + 'a,
2038 nondet: NonDet,
2039 ) -> Optional<(), L::DropConsistency, Unbounded>
2040 where
2041 L: TopLevel<'a>,
2042 {
2043 let tick = self.location.tick();
2044
2045 let latest_received = self.assume_retries::<ExactlyOnce>(nondet).fold(
2046 q!(|| None),
2047 q!(
2048 |latest, _| {
2049 *latest = Some(Instant::now());
2050 },
2051 commutative = manual_proof!()
2052 ),
2053 );
2054
2055 latest_received
2056 .snapshot(&tick, nondet)
2057 .filter_map(q!(move |latest_received| {
2058 if let Some(latest_received) = latest_received {
2059 if Instant::now().duration_since(latest_received) > duration {
2060 Some(())
2061 } else {
2062 None
2063 }
2064 } else {
2065 Some(())
2066 }
2067 }))
2068 .latest()
2069 }
2070
2071 pub fn atomic(self) -> Stream<T, Atomic<L>, B, O, R> {
2077 let id = self.location.flow_state().borrow_mut().next_clock_id();
2078 let out_location = Atomic {
2079 tick: Tick {
2080 id,
2081 l: self.location.clone(),
2082 },
2083 };
2084 Stream::new(
2085 out_location.clone(),
2086 HydroNode::BeginAtomic {
2087 inner: Box::new(self.ir_node.replace(HydroNode::Placeholder)),
2088 metadata: out_location
2089 .new_node_metadata(Stream::<T, Atomic<L>, B, O, R>::collection_kind()),
2090 },
2091 )
2092 }
2093
2094 pub fn batch<L2: Location<'a, DropConsistency = L::DropConsistency>>(
2102 self,
2103 tick: &Tick<L2>,
2104 _nondet: NonDet,
2105 ) -> Stream<T, Tick<L::DropConsistency>, Bounded, O, R> {
2106 assert_eq!(Location::id(tick.outer()), Location::id(&self.location));
2107 Stream::new(
2108 tick.drop_consistency(),
2109 HydroNode::Batch {
2110 inner: Box::new(self.ir_node.replace(HydroNode::Placeholder)),
2111 metadata: tick
2112 .new_node_metadata(Stream::<T, Tick<L>, Bounded, O, R>::collection_kind()),
2113 },
2114 )
2115 }
2116
2117 pub fn ir_node_named(self, name: &str) -> Stream<T, L, B, O, R> {
2120 {
2121 let mut node = self.ir_node.borrow_mut();
2122 let metadata = node.metadata_mut();
2123 metadata.tag = Some(name.to_owned());
2124 }
2125 self
2126 }
2127
2128 pub(crate) fn cast_at_most_one_element(self) -> Optional<T, L, B>
2132 where
2133 B: IsBounded,
2134 {
2135 Optional::new(
2136 self.location.clone(),
2137 HydroNode::Cast {
2138 inner: Box::new(self.ir_node.replace(HydroNode::Placeholder)),
2139 metadata: self
2140 .location
2141 .new_node_metadata(Optional::<T, L, B>::collection_kind()),
2142 },
2143 )
2144 }
2145
2146 pub(crate) fn use_ordering_type<O2: Ordering>(self) -> Stream<T, L, B, O2, R> {
2147 if O::ORDERING_KIND == O2::ORDERING_KIND {
2148 Stream::new(
2149 self.location.clone(),
2150 self.ir_node.replace(HydroNode::Placeholder),
2151 )
2152 } else {
2153 panic!(
2154 "Runtime ordering {:?} did not match requested cast {:?}.",
2155 O::ORDERING_KIND,
2156 O2::ORDERING_KIND
2157 )
2158 }
2159 }
2160
2161 pub fn assume_ordering<O2: Ordering>(
2170 self,
2171 _nondet: NonDet,
2172 ) -> Stream<T, L::DropConsistency, B, O2, R> {
2173 if O::ORDERING_KIND == O2::ORDERING_KIND {
2174 self.use_ordering_type().weaken_consistency()
2175 } else if O2::ORDERING_KIND == StreamOrder::NoOrder {
2176 let target_location = self.location().drop_consistency();
2178 Stream::new(
2179 target_location.clone(),
2180 HydroNode::Cast {
2181 inner: Box::new(self.ir_node.replace(HydroNode::Placeholder)),
2182 metadata: target_location
2183 .new_node_metadata(Stream::<T, L, B, O2, R>::collection_kind()),
2184 },
2185 )
2186 } else {
2187 let target_location = self.location().drop_consistency();
2188 Stream::new(
2189 target_location.clone(),
2190 HydroNode::ObserveNonDet {
2191 inner: Box::new(self.ir_node.replace(HydroNode::Placeholder)),
2192 trusted: false,
2193 metadata: target_location
2194 .new_node_metadata(Stream::<T, L, B, O2, R>::collection_kind()),
2195 },
2196 )
2197 }
2198 }
2199
2200 fn assume_ordering_trusted_bounded<O2: Ordering>(
2203 self,
2204 nondet: NonDet,
2205 ) -> Stream<T, L, B, O2, R> {
2206 if B::BOUNDED {
2207 self.assume_ordering_trusted(nondet)
2208 } else {
2209 let self_location = self.location.clone();
2210 let inner: Stream<T, L::DropConsistency, B, O2, R> = self.assume_ordering(nondet);
2211 Stream::new(self_location, inner.ir_node.replace(HydroNode::Placeholder))
2212 }
2213 }
2214
2215 pub(crate) fn assume_ordering_trusted<O2: Ordering>(
2218 self,
2219 _nondet: NonDet,
2220 ) -> Stream<T, L, B, O2, R> {
2221 if O::ORDERING_KIND == O2::ORDERING_KIND {
2222 self.use_ordering_type()
2223 } else if O2::ORDERING_KIND == StreamOrder::NoOrder {
2224 Stream::new(
2226 self.location.clone(),
2227 HydroNode::Cast {
2228 inner: Box::new(self.ir_node.replace(HydroNode::Placeholder)),
2229 metadata: self
2230 .location
2231 .new_node_metadata(Stream::<T, L, B, O2, R>::collection_kind()),
2232 },
2233 )
2234 } else {
2235 Stream::new(
2236 self.location.clone(),
2237 HydroNode::ObserveNonDet {
2238 inner: Box::new(self.ir_node.replace(HydroNode::Placeholder)),
2239 trusted: true,
2240 metadata: self
2241 .location
2242 .new_node_metadata(Stream::<T, L, B, O2, R>::collection_kind()),
2243 },
2244 )
2245 }
2246 }
2247
2248 #[deprecated = "use `weaken_ordering::<NoOrder>()` instead"]
2249 pub fn weakest_ordering(self) -> Stream<T, L, B, NoOrder, R> {
2252 self.weaken_ordering::<NoOrder>()
2253 }
2254
2255 pub fn weaken_ordering<O2: WeakerOrderingThan<O>>(self) -> Stream<T, L, B, O2, R> {
2258 let nondet = nondet!();
2259 self.assume_ordering_trusted::<O2>(nondet)
2260 }
2261
2262 pub fn make_totally_ordered(self) -> Stream<T, L, B, TotalOrder, R>
2265 where
2266 O: IsOrdered,
2267 {
2268 self.assume_ordering_trusted(nondet!())
2269 }
2270
2271 pub fn assume_retries<R2: Retries>(
2280 self,
2281 _nondet: NonDet,
2282 ) -> Stream<T, L::DropConsistency, B, O, R2> {
2283 if R::RETRIES_KIND == R2::RETRIES_KIND {
2284 Stream::new(
2285 self.location.drop_consistency(),
2286 self.ir_node.replace(HydroNode::Placeholder),
2287 )
2288 } else if R2::RETRIES_KIND == StreamRetry::AtLeastOnce {
2289 let target_location = self.location.drop_consistency();
2291 Stream::new(
2292 target_location.clone(),
2293 HydroNode::Cast {
2294 inner: Box::new(self.ir_node.replace(HydroNode::Placeholder)),
2295 metadata: target_location
2296 .new_node_metadata(Stream::<T, L, B, O, R2>::collection_kind()),
2297 },
2298 )
2299 } else {
2300 let target_location = self.location.drop_consistency();
2301 Stream::new(
2302 target_location.clone(),
2303 HydroNode::ObserveNonDet {
2304 inner: Box::new(self.ir_node.replace(HydroNode::Placeholder)),
2305 trusted: false,
2306 metadata: target_location
2307 .new_node_metadata(Stream::<T, L, B, O, R2>::collection_kind()),
2308 },
2309 )
2310 }
2311 }
2312
2313 fn assume_retries_trusted<R2: Retries>(self, _nondet: NonDet) -> Stream<T, L, B, O, R2> {
2316 if R::RETRIES_KIND == R2::RETRIES_KIND {
2317 Stream::new(
2318 self.location.clone(),
2319 self.ir_node.replace(HydroNode::Placeholder),
2320 )
2321 } else if R2::RETRIES_KIND == StreamRetry::AtLeastOnce {
2322 Stream::new(
2324 self.location.clone(),
2325 HydroNode::Cast {
2326 inner: Box::new(self.ir_node.replace(HydroNode::Placeholder)),
2327 metadata: self
2328 .location
2329 .new_node_metadata(Stream::<T, L, B, O, R2>::collection_kind()),
2330 },
2331 )
2332 } else {
2333 Stream::new(
2334 self.location.clone(),
2335 HydroNode::ObserveNonDet {
2336 inner: Box::new(self.ir_node.replace(HydroNode::Placeholder)),
2337 trusted: true,
2338 metadata: self
2339 .location
2340 .new_node_metadata(Stream::<T, L, B, O, R2>::collection_kind()),
2341 },
2342 )
2343 }
2344 }
2345
2346 #[deprecated = "use `weaken_retries::<AtLeastOnce>()` instead"]
2347 pub fn weakest_retries(self) -> Stream<T, L, B, O, AtLeastOnce> {
2350 self.weaken_retries::<AtLeastOnce>()
2351 }
2352
2353 pub fn weaken_retries<R2: WeakerRetryThan<R>>(self) -> Stream<T, L, B, O, R2> {
2356 let nondet = nondet!();
2357 self.assume_retries_trusted::<R2>(nondet)
2358 }
2359
2360 pub fn make_exactly_once(self) -> Stream<T, L, B, O, ExactlyOnce>
2363 where
2364 R: IsExactlyOnce,
2365 {
2366 self.assume_retries_trusted(nondet!())
2367 }
2368
2369 pub fn make_bounded(self) -> Stream<T, L, Bounded, O, R>
2372 where
2373 B: IsBounded,
2374 {
2375 self.weaken_boundedness()
2376 }
2377
2378 pub fn weaken_boundedness<B2: Boundedness>(self) -> Stream<T, L, B2, O, R> {
2381 if B::BOUNDED == B2::BOUNDED {
2382 Stream::new(
2383 self.location.clone(),
2384 self.ir_node.replace(HydroNode::Placeholder),
2385 )
2386 } else {
2387 Stream::new(
2389 self.location.clone(),
2390 HydroNode::Cast {
2391 inner: Box::new(self.ir_node.replace(HydroNode::Placeholder)),
2392 metadata: self
2393 .location
2394 .new_node_metadata(Stream::<T, L, B2, O, R>::collection_kind()),
2395 },
2396 )
2397 }
2398 }
2399}
2400
2401impl<'a, T, L, B: Boundedness, O: Ordering, R: Retries> Stream<&T, L, B, O, R>
2402where
2403 L: Location<'a>,
2404{
2405 pub fn cloned(self) -> Stream<T, L, B, O, R>
2423 where
2424 T: Clone,
2425 {
2426 self.map(q!(|d| d.clone()))
2427 }
2428}
2429
2430impl<'a, T, L, B: Boundedness, O: Ordering> Stream<T, L, B, O, ExactlyOnce>
2431where
2432 L: Location<'a>,
2433{
2434 pub fn count(self) -> Singleton<usize, L, B::StreamToMonotone> {
2453 self.assume_ordering_trusted::<TotalOrder>(nondet!(
2454 ))
2456 .fold(
2457 q!(|| 0usize),
2458 q!(
2459 |count, _| *count += 1,
2460 monotone = manual_proof!()
2461 ),
2462 )
2463 }
2464}
2465
2466impl<'a, T, L: Location<'a>, O: Ordering, R: Retries> Stream<T, L, Unbounded, O, R> {
2467 pub fn merge_unordered<O2: Ordering, R2: Retries>(
2491 self,
2492 other: Stream<T, L, Unbounded, O2, R2>,
2493 ) -> Stream<T, L, Unbounded, NoOrder, <R as MinRetries<R2>>::Min>
2494 where
2495 R: MinRetries<R2>,
2496 {
2497 Stream::new(
2498 self.location.clone(),
2499 HydroNode::Chain {
2500 first: Box::new(self.ir_node.replace(HydroNode::Placeholder)),
2501 second: Box::new(other.ir_node.replace(HydroNode::Placeholder)),
2502 metadata: self.location.new_node_metadata(Stream::<
2503 T,
2504 L,
2505 Unbounded,
2506 NoOrder,
2507 <R as MinRetries<R2>>::Min,
2508 >::collection_kind()),
2509 },
2510 )
2511 }
2512
2513 #[deprecated(note = "use `merge_unordered` instead")]
2515 pub fn interleave<O2: Ordering, R2: Retries>(
2516 self,
2517 other: Stream<T, L, Unbounded, O2, R2>,
2518 ) -> Stream<T, L, Unbounded, NoOrder, <R as MinRetries<R2>>::Min>
2519 where
2520 R: MinRetries<R2>,
2521 {
2522 self.merge_unordered(other)
2523 }
2524}
2525
2526impl<'a, T, L: Location<'a>, B: Boundedness, R: Retries> Stream<T, L, B, TotalOrder, R> {
2527 pub fn merge_ordered<R2: Retries>(
2555 self,
2556 other: Stream<T, L, B, TotalOrder, R2>,
2557 _nondet: NonDet,
2558 ) -> Stream<T, L::DropConsistency, B, TotalOrder, <R as MinRetries<R2>>::Min>
2559 where
2560 R: MinRetries<R2>,
2561 {
2562 let target_location = self.location().drop_consistency();
2563 Stream::new(
2564 target_location.clone(),
2565 HydroNode::MergeOrdered {
2566 first: Box::new(self.ir_node.replace(HydroNode::Placeholder)),
2567 second: Box::new(other.ir_node.replace(HydroNode::Placeholder)),
2568 metadata: target_location.new_node_metadata(Stream::<
2569 T,
2570 L::DropConsistency,
2571 B,
2572 TotalOrder,
2573 <R as MinRetries<R2>>::Min,
2574 >::collection_kind()),
2575 },
2576 )
2577 }
2578}
2579
2580impl<'a, T, L, B: Boundedness, O: Ordering, R: Retries> Stream<T, L, B, O, R>
2581where
2582 L: Location<'a>,
2583{
2584 pub fn sort(self) -> Stream<T, L, Bounded, TotalOrder, R>
2610 where
2611 B: IsBounded,
2612 T: Ord,
2613 {
2614 let this = self.make_bounded();
2615 Stream::new(
2616 this.location.clone(),
2617 HydroNode::Sort {
2618 input: Box::new(this.ir_node.replace(HydroNode::Placeholder)),
2619 metadata: this
2620 .location
2621 .new_node_metadata(Stream::<T, L, Bounded, TotalOrder, R>::collection_kind()),
2622 },
2623 )
2624 }
2625
2626 pub fn chain<O2: Ordering, R2: Retries, B2: Boundedness>(
2654 self,
2655 other: Stream<T, L, B2, O2, R2>,
2656 ) -> Stream<T, L, B2, <O as MinOrder<O2>>::Min, <R as MinRetries<R2>>::Min>
2657 where
2658 B: IsBounded,
2659 O: MinOrder<O2>,
2660 R: MinRetries<R2>,
2661 {
2662 check_matching_location(&self.location, &other.location);
2663
2664 Stream::new(
2665 self.location.clone(),
2666 HydroNode::Chain {
2667 first: Box::new(self.ir_node.replace(HydroNode::Placeholder)),
2668 second: Box::new(other.ir_node.replace(HydroNode::Placeholder)),
2669 metadata: self.location.new_node_metadata(Stream::<
2670 T,
2671 L,
2672 B2,
2673 <O as MinOrder<O2>>::Min,
2674 <R as MinRetries<R2>>::Min,
2675 >::collection_kind()),
2676 },
2677 )
2678 }
2679
2680 pub fn cross_product_nested_loop<T2, O2: Ordering + MinOrder<O>, R2: Retries>(
2684 self,
2685 other: Stream<T2, L, Bounded, O2, R2>,
2686 ) -> Stream<(T, T2), L, Bounded, <O2 as MinOrder<O>>::Min, <R as MinRetries<R2>>::Min>
2687 where
2688 B: IsBounded,
2689 T: Clone,
2690 T2: Clone,
2691 R: MinRetries<R2>,
2692 {
2693 let this = self.make_bounded();
2694 check_matching_location(&this.location, &other.location);
2695
2696 Stream::new(
2697 this.location.clone(),
2698 HydroNode::CrossProduct {
2699 left: Box::new(this.ir_node.replace(HydroNode::Placeholder)),
2700 right: Box::new(other.ir_node.replace(HydroNode::Placeholder)),
2701 metadata: this.location.new_node_metadata(Stream::<
2702 (T, T2),
2703 L,
2704 Bounded,
2705 <O2 as MinOrder<O>>::Min,
2706 <R as MinRetries<R2>>::Min,
2707 >::collection_kind()),
2708 },
2709 )
2710 }
2711
2712 pub fn repeat_with_keys<K, V2>(
2750 self,
2751 keys: KeyedSingleton<K, V2, L, Bounded>,
2752 ) -> KeyedStream<K, T, L, Bounded, O, R>
2753 where
2754 B: IsBounded,
2755 K: Clone,
2756 T: Clone,
2757 {
2758 keys.keys()
2759 .assume_ordering_trusted::<TotalOrder>(
2760 nondet!(),
2761 )
2762 .cross_product_nested_loop(self.make_bounded())
2763 .into_keyed()
2764 }
2765
2766 pub fn resolve_futures_blocking(self) -> Stream<T::Output, L, B, NoOrder, R>
2803 where
2804 T: Future,
2805 {
2806 Stream::new(
2807 self.location.clone(),
2808 HydroNode::ResolveFuturesBlocking {
2809 input: Box::new(self.ir_node.replace(HydroNode::Placeholder)),
2810 metadata: self
2811 .location
2812 .new_node_metadata(Stream::<T::Output, L, B, NoOrder, R>::collection_kind()),
2813 },
2814 )
2815 }
2816
2817 #[expect(clippy::wrong_self_convention, reason = "stream function naming")]
2837 pub fn is_empty(self) -> Singleton<bool, L, Bounded>
2838 where
2839 B: IsBounded,
2840 {
2841 self.make_bounded()
2842 .assume_ordering_trusted::<TotalOrder>(
2843 nondet!(),
2844 )
2845 .first()
2846 .is_none()
2847 }
2848}
2849
2850impl<'a, K, V1, L, B: Boundedness, O: Ordering, R: Retries> Stream<(K, V1), L, B, O, R>
2851where
2852 L: Location<'a>,
2853{
2854 pub fn join<V2, B2: Boundedness, O2: Ordering, R2: Retries>(
2879 self,
2880 n: Stream<(K, V2), L, B2, O2, R2>,
2881 ) -> Stream<(K, (V1, V2)), L, B, B2::PreserveOrderIfBounded<O>, <R as MinRetries<R2>>::Min>
2882 where
2883 K: Eq + Hash + Clone,
2884 R: MinRetries<R2>,
2885 V1: Clone,
2886 V2: Clone,
2887 {
2888 check_matching_location(&self.location, &n.location);
2889
2890 let ir_node = if B2::BOUNDED {
2891 HydroNode::JoinHalf {
2892 left: Box::new(self.ir_node.replace(HydroNode::Placeholder)),
2893 right: Box::new(n.ir_node.replace(HydroNode::Placeholder)),
2894 metadata: self.location.new_node_metadata(Stream::<
2895 (K, (V1, V2)),
2896 L,
2897 B,
2898 B2::PreserveOrderIfBounded<O>,
2899 <R as MinRetries<R2>>::Min,
2900 >::collection_kind()),
2901 }
2902 } else {
2903 HydroNode::Join {
2904 left: Box::new(self.ir_node.replace(HydroNode::Placeholder)),
2905 right: Box::new(n.ir_node.replace(HydroNode::Placeholder)),
2906 metadata: self.location.new_node_metadata(Stream::<
2907 (K, (V1, V2)),
2908 L,
2909 B,
2910 B2::PreserveOrderIfBounded<O>,
2911 <R as MinRetries<R2>>::Min,
2912 >::collection_kind()),
2913 }
2914 };
2915
2916 Stream::new(self.location.clone(), ir_node)
2917 }
2918
2919 pub fn anti_join<O2: Ordering, R2: Retries>(
2945 self,
2946 n: Stream<K, L, Bounded, O2, R2>,
2947 ) -> Stream<(K, V1), L, B, O, R>
2948 where
2949 K: Eq + Hash,
2950 {
2951 check_matching_location(&self.location, &n.location);
2952
2953 Stream::new(
2954 self.location.clone(),
2955 HydroNode::AntiJoin {
2956 pos: Box::new(self.ir_node.replace(HydroNode::Placeholder)),
2957 neg: Box::new(n.ir_node.replace(HydroNode::Placeholder)),
2958 metadata: self
2959 .location
2960 .new_node_metadata(Stream::<(K, V1), L, B, O, R>::collection_kind()),
2961 },
2962 )
2963 }
2964}
2965
2966impl<'a, K, V, L: Location<'a>, B: Boundedness, O: Ordering, R: Retries>
2967 Stream<(K, V), L, B, O, R>
2968{
2969 pub fn into_keyed(self) -> KeyedStream<K, V, L, B, O, R> {
2996 KeyedStream::new(
2997 self.location.clone(),
2998 HydroNode::Cast {
2999 inner: Box::new(self.ir_node.replace(HydroNode::Placeholder)),
3000 metadata: self
3001 .location
3002 .new_node_metadata(KeyedStream::<K, V, L, B, O, R>::collection_kind()),
3003 },
3004 )
3005 }
3006}
3007
3008impl<'a, K, V, L, O: Ordering, R: Retries> Stream<(K, V), Tick<L>, Bounded, O, R>
3009where
3010 K: Eq + Hash,
3011 L: Location<'a>,
3012{
3013 pub fn keys(self) -> Stream<K, Tick<L>, Bounded, NoOrder, ExactlyOnce> {
3032 self.into_keyed()
3033 .fold(
3034 q!(|| ()),
3035 q!(
3036 |_, _| {},
3037 commutative = manual_proof!(),
3038 idempotent = manual_proof!()
3039 ),
3040 )
3041 .keys()
3042 }
3043}
3044
3045impl<'a, T, L, B: Boundedness, O: Ordering, R: Retries> Stream<T, Atomic<L>, B, O, R>
3046where
3047 L: Location<'a>,
3048{
3049 pub fn batch_atomic<L2: Location<'a, DropConsistency = L::DropConsistency>>(
3056 self,
3057 tick: &Tick<L2>,
3058 _nondet: NonDet,
3059 ) -> Stream<T, Tick<L::DropConsistency>, Bounded, O, R> {
3060 Stream::new(
3061 tick.drop_consistency(),
3062 HydroNode::Batch {
3063 inner: Box::new(self.ir_node.replace(HydroNode::Placeholder)),
3064 metadata: tick
3065 .new_node_metadata(Stream::<T, Tick<L>, Bounded, O, R>::collection_kind()),
3066 },
3067 )
3068 }
3069
3070 pub fn end_atomic(self) -> Stream<T, L, B, O, R> {
3073 Stream::new(
3074 self.location.tick.l.clone(),
3075 HydroNode::EndAtomic {
3076 inner: Box::new(self.ir_node.replace(HydroNode::Placeholder)),
3077 metadata: self
3078 .location
3079 .tick
3080 .l
3081 .new_node_metadata(Stream::<T, L, B, O, R>::collection_kind()),
3082 },
3083 )
3084 }
3085}
3086
3087impl<'a, F, T, L, B: Boundedness, O: Ordering, R: Retries> Stream<F, L, B, O, R>
3088where
3089 L: TopLevel<'a>,
3090 F: Future<Output = T>,
3091{
3092 pub fn resolve_futures(self) -> Stream<T, L, Unbounded, NoOrder, R> {
3123 Stream::new(
3124 self.location.clone(),
3125 HydroNode::ResolveFutures {
3126 input: Box::new(self.ir_node.replace(HydroNode::Placeholder)),
3127 metadata: self
3128 .location
3129 .new_node_metadata(Stream::<T, L, Unbounded, NoOrder, R>::collection_kind()),
3130 },
3131 )
3132 }
3133
3134 pub fn resolve_futures_ordered(self) -> Stream<T, L, Unbounded, O, R> {
3165 Stream::new(
3166 self.location.clone(),
3167 HydroNode::ResolveFuturesOrdered {
3168 input: Box::new(self.ir_node.replace(HydroNode::Placeholder)),
3169 metadata: self
3170 .location
3171 .new_node_metadata(Stream::<T, L, Unbounded, O, R>::collection_kind()),
3172 },
3173 )
3174 }
3175}
3176
3177impl<'a, T, L, O: Ordering, R: Retries> Stream<T, Tick<L>, Bounded, O, R>
3178where
3179 L: Location<'a>,
3180{
3181 pub fn all_ticks(self) -> Stream<T, L, Unbounded, O, R> {
3184 Stream::new(
3185 self.location.outer().clone(),
3186 HydroNode::YieldConcat {
3187 inner: Box::new(self.ir_node.replace(HydroNode::Placeholder)),
3188 metadata: self
3189 .location
3190 .outer()
3191 .new_node_metadata(Stream::<T, L, Unbounded, O, R>::collection_kind()),
3192 },
3193 )
3194 }
3195
3196 pub fn all_ticks_atomic(self) -> Stream<T, Atomic<L>, Unbounded, O, R> {
3203 let out_location = Atomic {
3204 tick: self.location.clone(),
3205 };
3206
3207 Stream::new(
3208 out_location.clone(),
3209 HydroNode::YieldConcat {
3210 inner: Box::new(self.ir_node.replace(HydroNode::Placeholder)),
3211 metadata: out_location
3212 .new_node_metadata(Stream::<T, Atomic<L>, Unbounded, O, R>::collection_kind()),
3213 },
3214 )
3215 }
3216
3217 pub fn across_ticks<Out: BatchAtomic<'a>>(
3253 self,
3254 thunk: impl FnOnce(Stream<T, Atomic<L>, Unbounded, O, R>) -> Out,
3255 ) -> Out::Batched {
3256 thunk(self.all_ticks_atomic()).batched_atomic()
3257 }
3258
3259 pub fn defer_tick(self) -> Stream<T, Tick<L>, Bounded, O, R> {
3298 Stream::new(
3299 self.location.clone(),
3300 HydroNode::DeferTick {
3301 input: Box::new(self.ir_node.replace(HydroNode::Placeholder)),
3302 metadata: self
3303 .location
3304 .new_node_metadata(Stream::<T, Tick<L>, Bounded, O, R>::collection_kind()),
3305 },
3306 )
3307 }
3308}
3309
3310#[cfg(test)]
3311mod tests {
3312 #[cfg(feature = "deploy")]
3313 use futures::{SinkExt, StreamExt};
3314 #[cfg(feature = "deploy")]
3315 use hydro_deploy::Deployment;
3316 #[cfg(feature = "deploy")]
3317 use serde::{Deserialize, Serialize};
3318 #[cfg(any(feature = "deploy", feature = "sim"))]
3319 use stageleft::q;
3320
3321 #[cfg(any(feature = "deploy", feature = "sim"))]
3322 use crate::compile::builder::FlowBuilder;
3323 #[cfg(feature = "deploy")]
3324 use crate::live_collections::sliced::sliced;
3325 #[cfg(feature = "deploy")]
3326 use crate::live_collections::stream::ExactlyOnce;
3327 #[cfg(feature = "sim")]
3328 use crate::live_collections::stream::NoOrder;
3329 #[cfg(any(feature = "deploy", feature = "sim"))]
3330 use crate::live_collections::stream::TotalOrder;
3331 #[cfg(any(feature = "deploy", feature = "sim"))]
3332 use crate::location::Location;
3333 #[cfg(feature = "sim")]
3334 use crate::networking::TCP;
3335 #[cfg(any(feature = "deploy", feature = "sim"))]
3336 use crate::nondet::nondet;
3337
3338 mod backtrace_chained_ops;
3339
3340 #[cfg(feature = "deploy")]
3341 struct P1 {}
3342 #[cfg(feature = "deploy")]
3343 struct P2 {}
3344
3345 #[cfg(feature = "deploy")]
3346 #[derive(Serialize, Deserialize, Debug)]
3347 struct SendOverNetwork {
3348 n: u32,
3349 }
3350
3351 #[cfg(feature = "deploy")]
3352 #[tokio::test]
3353 async fn first_ten_distributed() {
3354 use crate::networking::TCP;
3355
3356 let mut deployment = Deployment::new();
3357
3358 let mut flow = FlowBuilder::new();
3359 let first_node = flow.process::<P1>();
3360 let second_node = flow.process::<P2>();
3361 let external = flow.external::<P2>();
3362
3363 let numbers = first_node.source_iter(q!(0..10));
3364 let out_port = numbers
3365 .map(q!(|n| SendOverNetwork { n }))
3366 .send(&second_node, TCP.fail_stop().bincode())
3367 .send_bincode_external(&external);
3368
3369 let nodes = flow
3370 .with_process(&first_node, deployment.Localhost())
3371 .with_process(&second_node, deployment.Localhost())
3372 .with_external(&external, deployment.Localhost())
3373 .deploy(&mut deployment);
3374
3375 deployment.deploy().await.unwrap();
3376
3377 let mut external_out = nodes.connect(out_port).await;
3378
3379 deployment.start().await.unwrap();
3380
3381 for i in 0..10 {
3382 assert_eq!(external_out.next().await.unwrap().n, i);
3383 }
3384 }
3385
3386 #[cfg(feature = "deploy")]
3387 #[tokio::test]
3388 async fn first_cardinality() {
3389 let mut deployment = Deployment::new();
3390
3391 let mut flow = FlowBuilder::new();
3392 let node = flow.process::<()>();
3393 let external = flow.external::<()>();
3394
3395 let node_tick = node.tick();
3396 let count = node_tick
3397 .singleton(q!([1, 2, 3]))
3398 .into_stream()
3399 .flatten_ordered()
3400 .first()
3401 .into_stream()
3402 .count()
3403 .all_ticks()
3404 .send_bincode_external(&external);
3405
3406 let nodes = flow
3407 .with_process(&node, deployment.Localhost())
3408 .with_external(&external, deployment.Localhost())
3409 .deploy(&mut deployment);
3410
3411 deployment.deploy().await.unwrap();
3412
3413 let mut external_out = nodes.connect(count).await;
3414
3415 deployment.start().await.unwrap();
3416
3417 assert_eq!(external_out.next().await.unwrap(), 1);
3418 }
3419
3420 #[cfg(feature = "deploy")]
3421 #[tokio::test]
3422 async fn unbounded_reduce_remembers_state() {
3423 let mut deployment = Deployment::new();
3424
3425 let mut flow = FlowBuilder::new();
3426 let node = flow.process::<()>();
3427 let external = flow.external::<()>();
3428
3429 let (input_port, input) = node.source_external_bincode(&external);
3430 let out = input
3431 .reduce(q!(|acc, v| *acc += v))
3432 .sample_eager(nondet!())
3433 .send_bincode_external(&external);
3434
3435 let nodes = flow
3436 .with_process(&node, deployment.Localhost())
3437 .with_external(&external, deployment.Localhost())
3438 .deploy(&mut deployment);
3439
3440 deployment.deploy().await.unwrap();
3441
3442 let mut external_in = nodes.connect(input_port).await;
3443 let mut external_out = nodes.connect(out).await;
3444
3445 deployment.start().await.unwrap();
3446
3447 external_in.send(1).await.unwrap();
3448 assert_eq!(external_out.next().await.unwrap(), 1);
3449
3450 external_in.send(2).await.unwrap();
3451 assert_eq!(external_out.next().await.unwrap(), 3);
3452 }
3453
3454 #[cfg(feature = "deploy")]
3455 #[tokio::test]
3456 async fn top_level_bounded_cross_singleton() {
3457 let mut deployment = Deployment::new();
3458
3459 let mut flow = FlowBuilder::new();
3460 let node = flow.process::<()>();
3461 let external = flow.external::<()>();
3462
3463 let (input_port, input) =
3464 node.source_external_bincode::<_, _, TotalOrder, ExactlyOnce>(&external);
3465
3466 let out = input
3467 .cross_singleton(
3468 node.source_iter(q!(vec![1, 2, 3]))
3469 .fold(q!(|| 0), q!(|acc, v| *acc += v)),
3470 )
3471 .send_bincode_external(&external);
3472
3473 let nodes = flow
3474 .with_process(&node, deployment.Localhost())
3475 .with_external(&external, deployment.Localhost())
3476 .deploy(&mut deployment);
3477
3478 deployment.deploy().await.unwrap();
3479
3480 let mut external_in = nodes.connect(input_port).await;
3481 let mut external_out = nodes.connect(out).await;
3482
3483 deployment.start().await.unwrap();
3484
3485 external_in.send(1).await.unwrap();
3486 assert_eq!(external_out.next().await.unwrap(), (1, 6));
3487
3488 external_in.send(2).await.unwrap();
3489 assert_eq!(external_out.next().await.unwrap(), (2, 6));
3490 }
3491
3492 #[cfg(feature = "deploy")]
3493 #[tokio::test]
3494 async fn top_level_bounded_reduce_cardinality() {
3495 let mut deployment = Deployment::new();
3496
3497 let mut flow = FlowBuilder::new();
3498 let node = flow.process::<()>();
3499 let external = flow.external::<()>();
3500
3501 let (input_port, input) =
3502 node.source_external_bincode::<_, _, TotalOrder, ExactlyOnce>(&external);
3503
3504 let out = sliced! {
3505 let input = use(input, nondet!());
3506 let v = use(node.source_iter(q!(vec![1, 2, 3])).reduce(q!(|acc, v| *acc += v)), nondet!());
3507 input.cross_singleton(v.into_stream().count())
3508 }
3509 .send_bincode_external(&external);
3510
3511 let nodes = flow
3512 .with_process(&node, deployment.Localhost())
3513 .with_external(&external, deployment.Localhost())
3514 .deploy(&mut deployment);
3515
3516 deployment.deploy().await.unwrap();
3517
3518 let mut external_in = nodes.connect(input_port).await;
3519 let mut external_out = nodes.connect(out).await;
3520
3521 deployment.start().await.unwrap();
3522
3523 external_in.send(1).await.unwrap();
3524 assert_eq!(external_out.next().await.unwrap(), (1, 1));
3525
3526 external_in.send(2).await.unwrap();
3527 assert_eq!(external_out.next().await.unwrap(), (2, 1));
3528 }
3529
3530 #[cfg(feature = "deploy")]
3531 #[tokio::test]
3532 async fn top_level_bounded_into_singleton_cardinality() {
3533 let mut deployment = Deployment::new();
3534
3535 let mut flow = FlowBuilder::new();
3536 let node = flow.process::<()>();
3537 let external = flow.external::<()>();
3538
3539 let (input_port, input) =
3540 node.source_external_bincode::<_, _, TotalOrder, ExactlyOnce>(&external);
3541
3542 let out = sliced! {
3543 let input = use(input, nondet!());
3544 let v = use(node.source_iter(q!(vec![1, 2, 3])).reduce(q!(|acc, v| *acc += v)).into_singleton(), nondet!());
3545 input.cross_singleton(v.into_stream().count())
3546 }
3547 .send_bincode_external(&external);
3548
3549 let nodes = flow
3550 .with_process(&node, deployment.Localhost())
3551 .with_external(&external, deployment.Localhost())
3552 .deploy(&mut deployment);
3553
3554 deployment.deploy().await.unwrap();
3555
3556 let mut external_in = nodes.connect(input_port).await;
3557 let mut external_out = nodes.connect(out).await;
3558
3559 deployment.start().await.unwrap();
3560
3561 external_in.send(1).await.unwrap();
3562 assert_eq!(external_out.next().await.unwrap(), (1, 1));
3563
3564 external_in.send(2).await.unwrap();
3565 assert_eq!(external_out.next().await.unwrap(), (2, 1));
3566 }
3567
3568 #[cfg(feature = "deploy")]
3569 #[tokio::test]
3570 async fn atomic_fold_replays_each_tick() {
3571 let mut deployment = Deployment::new();
3572
3573 let mut flow = FlowBuilder::new();
3574 let node = flow.process::<()>();
3575 let external = flow.external::<()>();
3576
3577 let (input_port, input) =
3578 node.source_external_bincode::<_, _, TotalOrder, ExactlyOnce>(&external);
3579 let tick = node.tick();
3580
3581 let out = input
3582 .batch(&tick, nondet!())
3583 .cross_singleton(
3584 node.source_iter(q!(vec![1, 2, 3]))
3585 .atomic()
3586 .fold(q!(|| 0), q!(|acc, v| *acc += v))
3587 .snapshot_atomic(&tick, nondet!()),
3588 )
3589 .all_ticks()
3590 .send_bincode_external(&external);
3591
3592 let nodes = flow
3593 .with_process(&node, deployment.Localhost())
3594 .with_external(&external, deployment.Localhost())
3595 .deploy(&mut deployment);
3596
3597 deployment.deploy().await.unwrap();
3598
3599 let mut external_in = nodes.connect(input_port).await;
3600 let mut external_out = nodes.connect(out).await;
3601
3602 deployment.start().await.unwrap();
3603
3604 external_in.send(1).await.unwrap();
3605 assert_eq!(external_out.next().await.unwrap(), (1, 6));
3606
3607 external_in.send(2).await.unwrap();
3608 assert_eq!(external_out.next().await.unwrap(), (2, 6));
3609 }
3610
3611 #[cfg(feature = "deploy")]
3612 #[tokio::test]
3613 async fn unbounded_scan_remembers_state() {
3614 let mut deployment = Deployment::new();
3615
3616 let mut flow = FlowBuilder::new();
3617 let node = flow.process::<()>();
3618 let external = flow.external::<()>();
3619
3620 let (input_port, input) = node.source_external_bincode(&external);
3621 let out = input
3622 .scan(
3623 q!(|| 0),
3624 q!(|acc, v| {
3625 *acc += v;
3626 Some(*acc)
3627 }),
3628 )
3629 .send_bincode_external(&external);
3630
3631 let nodes = flow
3632 .with_process(&node, deployment.Localhost())
3633 .with_external(&external, deployment.Localhost())
3634 .deploy(&mut deployment);
3635
3636 deployment.deploy().await.unwrap();
3637
3638 let mut external_in = nodes.connect(input_port).await;
3639 let mut external_out = nodes.connect(out).await;
3640
3641 deployment.start().await.unwrap();
3642
3643 external_in.send(1).await.unwrap();
3644 assert_eq!(external_out.next().await.unwrap(), 1);
3645
3646 external_in.send(2).await.unwrap();
3647 assert_eq!(external_out.next().await.unwrap(), 3);
3648 }
3649
3650 #[cfg(feature = "deploy")]
3651 #[tokio::test]
3652 async fn unbounded_enumerate_remembers_state() {
3653 let mut deployment = Deployment::new();
3654
3655 let mut flow = FlowBuilder::new();
3656 let node = flow.process::<()>();
3657 let external = flow.external::<()>();
3658
3659 let (input_port, input) = node.source_external_bincode(&external);
3660 let out = input.enumerate().send_bincode_external(&external);
3661
3662 let nodes = flow
3663 .with_process(&node, deployment.Localhost())
3664 .with_external(&external, deployment.Localhost())
3665 .deploy(&mut deployment);
3666
3667 deployment.deploy().await.unwrap();
3668
3669 let mut external_in = nodes.connect(input_port).await;
3670 let mut external_out = nodes.connect(out).await;
3671
3672 deployment.start().await.unwrap();
3673
3674 external_in.send(1).await.unwrap();
3675 assert_eq!(external_out.next().await.unwrap(), (0, 1));
3676
3677 external_in.send(2).await.unwrap();
3678 assert_eq!(external_out.next().await.unwrap(), (1, 2));
3679 }
3680
3681 #[cfg(feature = "deploy")]
3682 #[tokio::test]
3683 async fn unbounded_unique_remembers_state() {
3684 let mut deployment = Deployment::new();
3685
3686 let mut flow = FlowBuilder::new();
3687 let node = flow.process::<()>();
3688 let external = flow.external::<()>();
3689
3690 let (input_port, input) =
3691 node.source_external_bincode::<_, _, TotalOrder, ExactlyOnce>(&external);
3692 let out = input.unique().send_bincode_external(&external);
3693
3694 let nodes = flow
3695 .with_process(&node, deployment.Localhost())
3696 .with_external(&external, deployment.Localhost())
3697 .deploy(&mut deployment);
3698
3699 deployment.deploy().await.unwrap();
3700
3701 let mut external_in = nodes.connect(input_port).await;
3702 let mut external_out = nodes.connect(out).await;
3703
3704 deployment.start().await.unwrap();
3705
3706 external_in.send(1).await.unwrap();
3707 assert_eq!(external_out.next().await.unwrap(), 1);
3708
3709 external_in.send(2).await.unwrap();
3710 assert_eq!(external_out.next().await.unwrap(), 2);
3711
3712 external_in.send(1).await.unwrap();
3713 external_in.send(3).await.unwrap();
3714 assert_eq!(external_out.next().await.unwrap(), 3);
3715 }
3716
3717 #[cfg(feature = "sim")]
3718 #[test]
3719 #[should_panic]
3720 fn sim_batch_nondet_size() {
3721 let mut flow = FlowBuilder::new();
3722 let node = flow.process::<()>();
3723
3724 let (in_send, input) = node.sim_input::<_, TotalOrder, _>();
3725
3726 let tick = node.tick();
3727 let out_recv = input
3728 .batch(&tick, nondet!())
3729 .count()
3730 .all_ticks()
3731 .sim_output();
3732
3733 flow.sim().exhaustive(async || {
3734 in_send.send(());
3735 in_send.send(());
3736 in_send.send(());
3737
3738 assert_eq!(out_recv.next().await.unwrap(), 3); });
3740 }
3741
3742 #[cfg(feature = "sim")]
3743 #[test]
3744 fn sim_batch_preserves_order() {
3745 let mut flow = FlowBuilder::new();
3746 let node = flow.process::<()>();
3747
3748 let (in_send, input) = node.sim_input();
3749
3750 let tick = node.tick();
3751 let out_recv = input
3752 .batch(&tick, nondet!())
3753 .all_ticks()
3754 .sim_output();
3755
3756 flow.sim().exhaustive(async || {
3757 in_send.send(1);
3758 in_send.send(2);
3759 in_send.send(3);
3760
3761 out_recv.assert_yields_only([1, 2, 3]).await;
3762 });
3763 }
3764
3765 #[cfg(feature = "sim")]
3766 #[test]
3767 #[should_panic]
3768 fn sim_batch_unordered_shuffles() {
3769 let mut flow = FlowBuilder::new();
3770 let node = flow.process::<()>();
3771
3772 let (in_send, input) = node.sim_input::<_, NoOrder, _>();
3773
3774 let tick = node.tick();
3775 let batch = input.batch(&tick, nondet!());
3776 let out_recv = batch
3777 .clone()
3778 .min()
3779 .zip(batch.max())
3780 .all_ticks()
3781 .sim_output();
3782
3783 flow.sim().exhaustive(async || {
3784 in_send.send_many_unordered([1, 2, 3]);
3785
3786 if out_recv.collect::<Vec<_>>().await == vec![(1, 3), (2, 2)] {
3787 panic!("saw both (1, 3) and (2, 2), so batching must have shuffled the order");
3788 }
3789 });
3790 }
3791
3792 #[cfg(feature = "sim")]
3793 #[test]
3794 fn sim_batch_unordered_shuffles_count() {
3795 let mut flow = FlowBuilder::new();
3796 let node = flow.process::<()>();
3797
3798 let (in_send, input) = node.sim_input::<_, NoOrder, _>();
3799
3800 let tick = node.tick();
3801 let batch = input.batch(&tick, nondet!());
3802 let out_recv = batch.all_ticks().sim_output();
3803
3804 let instance_count = flow.sim().exhaustive(async || {
3805 in_send.send_many_unordered([1, 2, 3, 4]);
3806 out_recv.assert_yields_only_unordered([1, 2, 3, 4]).await;
3807 });
3808
3809 assert_eq!(
3810 instance_count,
3811 75 )
3813 }
3814
3815 #[cfg(feature = "sim")]
3816 #[test]
3817 #[should_panic]
3818 fn sim_observe_order_batched() {
3819 let mut flow = FlowBuilder::new();
3820 let node = flow.process::<()>();
3821
3822 let (in_send, input) = node.sim_input::<_, NoOrder, _>();
3823
3824 let tick = node.tick();
3825 let batch = input.batch(&tick, nondet!());
3826 let out_recv = batch
3827 .assume_ordering::<TotalOrder>(nondet!())
3828 .all_ticks()
3829 .sim_output();
3830
3831 flow.sim().exhaustive(async || {
3832 in_send.send_many_unordered([1, 2, 3, 4]);
3833 out_recv.assert_yields_only([1, 2, 3, 4]).await; });
3835 }
3836
3837 #[cfg(feature = "sim")]
3838 #[test]
3839 fn sim_observe_order_batched_count() {
3840 let mut flow = FlowBuilder::new();
3841 let node = flow.process::<()>();
3842
3843 let (in_send, input) = node.sim_input::<_, NoOrder, _>();
3844
3845 let tick = node.tick();
3846 let batch = input.batch(&tick, nondet!());
3847 let out_recv = batch
3848 .assume_ordering::<TotalOrder>(nondet!())
3849 .all_ticks()
3850 .sim_output();
3851
3852 let instance_count = flow.sim().exhaustive(async || {
3853 in_send.send_many_unordered([1, 2, 3, 4]);
3854 let _ = out_recv.collect::<Vec<_>>().await;
3855 });
3856
3857 assert_eq!(
3858 instance_count,
3859 192 )
3861 }
3862
3863 #[cfg(feature = "sim")]
3864 #[test]
3865 fn sim_unordered_count_instance_count() {
3866 let mut flow = FlowBuilder::new();
3867 let node = flow.process::<()>();
3868
3869 let (in_send, input) = node.sim_input::<_, NoOrder, _>();
3870
3871 let tick = node.tick();
3872 let out_recv = input
3873 .count()
3874 .snapshot(&tick, nondet!())
3875 .all_ticks()
3876 .sim_output();
3877
3878 let instance_count = flow.sim().exhaustive(async || {
3879 in_send.send_many_unordered([1, 2, 3, 4]);
3880 assert!(out_recv.collect::<Vec<_>>().await.last().unwrap() == &4);
3881 });
3882
3883 assert_eq!(
3884 instance_count,
3885 16 )
3887 }
3888
3889 #[cfg(feature = "sim")]
3890 #[test]
3891 fn sim_top_level_assume_ordering() {
3892 let mut flow = FlowBuilder::new();
3893 let node = flow.process::<()>();
3894
3895 let (in_send, input) = node.sim_input::<_, NoOrder, _>();
3896
3897 let out_recv = input
3898 .assume_ordering::<TotalOrder>(nondet!())
3899 .sim_output();
3900
3901 let instance_count = flow.sim().exhaustive(async || {
3902 in_send.send_many_unordered([1, 2, 3]);
3903 let mut out = out_recv.collect::<Vec<_>>().await;
3904 out.sort();
3905 assert_eq!(out, vec![1, 2, 3]);
3906 });
3907
3908 assert_eq!(instance_count, 6)
3909 }
3910
3911 #[cfg(feature = "sim")]
3912 #[test]
3913 fn sim_top_level_assume_ordering_cycle_back() {
3914 let mut flow = FlowBuilder::new();
3915 let node = flow.process::<()>();
3916 let node2 = flow.process::<()>();
3917
3918 let (in_send, input) = node.sim_input::<_, NoOrder, _>();
3919
3920 let (complete_cycle_back, cycle_back) =
3921 node.forward_ref::<super::Stream<_, _, _, NoOrder>>();
3922 let ordered = input
3923 .merge_unordered(cycle_back)
3924 .assume_ordering::<TotalOrder>(nondet!());
3925 complete_cycle_back.complete(
3926 ordered
3927 .clone()
3928 .map(q!(|v| v + 1))
3929 .filter(q!(|v| v % 2 == 1))
3930 .send(&node2, TCP.fail_stop().bincode())
3931 .send(&node, TCP.fail_stop().bincode()),
3932 );
3933
3934 let out_recv = ordered.sim_output();
3935
3936 let mut saw = false;
3937 let instance_count = flow.sim().exhaustive(async || {
3938 in_send.send_many_unordered([0, 2]);
3939 let out = out_recv.collect::<Vec<_>>().await;
3940
3941 if out.starts_with(&[0, 1, 2]) {
3942 saw = true;
3943 }
3944 });
3945
3946 assert!(saw, "did not see an instance with 0, 1, 2 in order");
3947 assert_eq!(instance_count, 6);
3948 }
3949
3950 #[cfg(feature = "sim")]
3951 #[test]
3952 fn sim_top_level_assume_ordering_cycle_back_tick() {
3953 let mut flow = FlowBuilder::new();
3954 let node = flow.process::<()>();
3955 let node2 = flow.process::<()>();
3956
3957 let (in_send, input) = node.sim_input::<_, NoOrder, _>();
3958
3959 let (complete_cycle_back, cycle_back) =
3960 node.forward_ref::<super::Stream<_, _, _, NoOrder>>();
3961 let ordered = input
3962 .merge_unordered(cycle_back)
3963 .assume_ordering::<TotalOrder>(nondet!());
3964 complete_cycle_back.complete(
3965 ordered
3966 .clone()
3967 .batch(&node.tick(), nondet!())
3968 .all_ticks()
3969 .map(q!(|v| v + 1))
3970 .filter(q!(|v| v % 2 == 1))
3971 .send(&node2, TCP.fail_stop().bincode())
3972 .send(&node, TCP.fail_stop().bincode()),
3973 );
3974
3975 let out_recv = ordered.sim_output();
3976
3977 let mut saw = false;
3978 let instance_count = flow.sim().exhaustive(async || {
3979 in_send.send_many_unordered([0, 2]);
3980 let out = out_recv.collect::<Vec<_>>().await;
3981
3982 if out.starts_with(&[0, 1, 2]) {
3983 saw = true;
3984 }
3985 });
3986
3987 assert!(saw, "did not see an instance with 0, 1, 2 in order");
3988 assert_eq!(instance_count, 58);
3989 }
3990
3991 #[cfg(feature = "sim")]
3992 #[test]
3993 fn sim_top_level_assume_ordering_multiple() {
3994 let mut flow = FlowBuilder::new();
3995 let node = flow.process::<()>();
3996 let node2 = flow.process::<()>();
3997
3998 let (in_send, input) = node.sim_input::<_, NoOrder, _>();
3999 let (_, input2) = node.sim_input::<_, NoOrder, _>();
4000
4001 let (complete_cycle_back, cycle_back) =
4002 node.forward_ref::<super::Stream<_, _, _, NoOrder>>();
4003 let input1_ordered = input
4004 .clone()
4005 .merge_unordered(cycle_back)
4006 .assume_ordering::<TotalOrder>(nondet!());
4007 let foo = input1_ordered
4008 .clone()
4009 .map(q!(|v| v + 3))
4010 .weaken_ordering::<NoOrder>()
4011 .merge_unordered(input2)
4012 .assume_ordering::<TotalOrder>(nondet!());
4013
4014 complete_cycle_back.complete(
4015 foo.filter(q!(|v| *v == 3))
4016 .send(&node2, TCP.fail_stop().bincode())
4017 .send(&node, TCP.fail_stop().bincode()),
4018 );
4019
4020 let out_recv = input1_ordered.sim_output();
4021
4022 let mut saw = false;
4023 let instance_count = flow.sim().exhaustive(async || {
4024 in_send.send_many_unordered([0, 1]);
4025 let out = out_recv.collect::<Vec<_>>().await;
4026
4027 if out.starts_with(&[0, 3, 1]) {
4028 saw = true;
4029 }
4030 });
4031
4032 assert!(saw, "did not see an instance with 0, 3, 1 in order");
4033 assert_eq!(instance_count, 24);
4034 }
4035
4036 #[cfg(feature = "sim")]
4037 #[test]
4038 fn sim_atomic_assume_ordering_cycle_back() {
4039 let mut flow = FlowBuilder::new();
4040 let node = flow.process::<()>();
4041 let node2 = flow.process::<()>();
4042
4043 let (in_send, input) = node.sim_input::<_, NoOrder, _>();
4044
4045 let (complete_cycle_back, cycle_back) =
4046 node.forward_ref::<super::Stream<_, _, _, NoOrder>>();
4047 let ordered = input
4048 .merge_unordered(cycle_back)
4049 .atomic()
4050 .assume_ordering::<TotalOrder>(nondet!())
4051 .end_atomic();
4052 complete_cycle_back.complete(
4053 ordered
4054 .clone()
4055 .map(q!(|v| v + 1))
4056 .filter(q!(|v| v % 2 == 1))
4057 .send(&node2, TCP.fail_stop().bincode())
4058 .send(&node, TCP.fail_stop().bincode()),
4059 );
4060
4061 let out_recv = ordered.sim_output();
4062
4063 let instance_count = flow.sim().exhaustive(async || {
4064 in_send.send_many_unordered([0, 2]);
4065 let out = out_recv.collect::<Vec<_>>().await;
4066 assert_eq!(out.len(), 4);
4067 });
4068 assert_eq!(instance_count, 22);
4069 }
4070
4071 #[cfg(feature = "deploy")]
4072 #[tokio::test]
4073 async fn partition_evens_odds() {
4074 let mut deployment = Deployment::new();
4075
4076 let mut flow = FlowBuilder::new();
4077 let node = flow.process::<()>();
4078 let external = flow.external::<()>();
4079
4080 let numbers = node.source_iter(q!(vec![1i32, 2, 3, 4, 5, 6]));
4081 let (evens, odds) = numbers.partition(q!(|x: &i32| x % 2 == 0));
4082 let evens_port = evens.send_bincode_external(&external);
4083 let odds_port = odds.send_bincode_external(&external);
4084
4085 let nodes = flow
4086 .with_process(&node, deployment.Localhost())
4087 .with_external(&external, deployment.Localhost())
4088 .deploy(&mut deployment);
4089
4090 deployment.deploy().await.unwrap();
4091
4092 let mut evens_out = nodes.connect(evens_port).await;
4093 let mut odds_out = nodes.connect(odds_port).await;
4094
4095 deployment.start().await.unwrap();
4096
4097 let mut even_results = Vec::new();
4098 for _ in 0..3 {
4099 even_results.push(evens_out.next().await.unwrap());
4100 }
4101 even_results.sort();
4102 assert_eq!(even_results, vec![2, 4, 6]);
4103
4104 let mut odd_results = Vec::new();
4105 for _ in 0..3 {
4106 odd_results.push(odds_out.next().await.unwrap());
4107 }
4108 odd_results.sort();
4109 assert_eq!(odd_results, vec![1, 3, 5]);
4110 }
4111
4112 #[cfg(feature = "deploy")]
4113 #[tokio::test]
4114 async fn unconsumed_inspect_still_runs() {
4115 use crate::deploy::DeployCrateWrapper;
4116
4117 let mut deployment = Deployment::new();
4118
4119 let mut flow = FlowBuilder::new();
4120 let node = flow.process::<()>();
4121
4122 node.source_iter(q!(0..5))
4125 .inspect(q!(|x| println!("inspect: {}", x)));
4126
4127 let nodes = flow
4128 .with_process(&node, deployment.Localhost())
4129 .deploy(&mut deployment);
4130
4131 deployment.deploy().await.unwrap();
4132
4133 let mut stdout = nodes.get_process(&node).stdout();
4134
4135 deployment.start().await.unwrap();
4136
4137 let mut lines = Vec::new();
4138 for _ in 0..5 {
4139 lines.push(stdout.recv().await.unwrap());
4140 }
4141 lines.sort();
4142 assert_eq!(
4143 lines,
4144 vec![
4145 "inspect: 0",
4146 "inspect: 1",
4147 "inspect: 2",
4148 "inspect: 3",
4149 "inspect: 4",
4150 ]
4151 );
4152 }
4153
4154 #[cfg(feature = "sim")]
4155 #[test]
4156 fn sim_limit() {
4157 let mut flow = FlowBuilder::new();
4158 let node = flow.process::<()>();
4159
4160 let (in_send, input) = node.sim_input();
4161
4162 let out_recv = input.limit(q!(3)).sim_output();
4163
4164 flow.sim().exhaustive(async || {
4165 in_send.send(1);
4166 in_send.send(2);
4167 in_send.send(3);
4168 in_send.send(4);
4169 in_send.send(5);
4170
4171 out_recv.assert_yields_only([1, 2, 3]).await;
4172 });
4173 }
4174
4175 #[cfg(feature = "sim")]
4176 #[test]
4177 fn sim_limit_zero() {
4178 let mut flow = FlowBuilder::new();
4179 let node = flow.process::<()>();
4180
4181 let (in_send, input) = node.sim_input();
4182
4183 let out_recv = input.limit(q!(0)).sim_output();
4184
4185 flow.sim().exhaustive(async || {
4186 in_send.send(1);
4187 in_send.send(2);
4188
4189 out_recv.assert_yields_only::<i32, _>([]).await;
4190 });
4191 }
4192
4193 #[cfg(feature = "sim")]
4194 #[test]
4195 fn sim_merge_ordered() {
4196 let mut flow = FlowBuilder::new();
4197 let node = flow.process::<()>();
4198
4199 let (in_send, input) = node.sim_input();
4200 let (in_send2, input2) = node.sim_input();
4201
4202 let out_recv = input
4203 .merge_ordered(input2, nondet!())
4204 .sim_output();
4205
4206 let mut saw_out_of_order = false;
4207 let instances = flow.sim().exhaustive(async || {
4208 in_send.send(1);
4209 in_send.send(2);
4210 in_send2.send(3);
4211 in_send2.send(4);
4212
4213 let out = out_recv.collect::<Vec<_>>().await;
4214
4215 if out == [1, 3, 2, 4] {
4216 saw_out_of_order = true;
4217 }
4218
4219 let mut first_elements = out.iter().filter(|v| **v <= 2).copied().collect::<Vec<_>>();
4222 let mut second_elements = out.iter().filter(|v| **v > 2).copied().collect::<Vec<_>>();
4223 assert_eq!(
4224 first_elements,
4225 vec![1, 2],
4226 "first input order violated: {:?}",
4227 out
4228 );
4229 assert_eq!(
4230 second_elements,
4231 vec![3, 4],
4232 "second input order violated: {:?}",
4233 out
4234 );
4235
4236 first_elements.append(&mut second_elements);
4237 first_elements.sort();
4238 assert_eq!(first_elements, vec![1, 2, 3, 4]);
4239 });
4240
4241 assert!(saw_out_of_order);
4242 assert_eq!(instances, 6);
4243 }
4244
4245 #[cfg(feature = "sim")]
4248 #[test]
4249 fn sim_merge_ordered_one_empty() {
4250 let mut flow = FlowBuilder::new();
4251 let node = flow.process::<()>();
4252
4253 let (in_send, input) = node.sim_input();
4254 let (_in_send2, input2) = node.sim_input();
4255
4256 let out_recv = input
4257 .merge_ordered(input2, nondet!())
4258 .sim_output();
4259
4260 let instances = flow.sim().exhaustive(async || {
4261 in_send.send(1);
4262 in_send.send(2);
4263
4264 let out = out_recv.collect::<Vec<_>>().await;
4265 assert_eq!(out, vec![1, 2]);
4266 });
4267
4268 assert_eq!(instances, 1);
4270 }
4271
4272 #[cfg(feature = "sim")]
4278 #[test]
4279 fn sim_merge_ordered_cycle_back() {
4280 let mut flow = FlowBuilder::new();
4281 let node = flow.process::<()>();
4282
4283 let (in_send, input) = node.sim_input();
4284
4285 let (complete_cycle_back, cycle_back) =
4287 node.forward_ref::<super::Stream<_, _, _, TotalOrder>>();
4288
4289 let merged = input.merge_ordered(cycle_back, nondet!());
4291
4292 complete_cycle_back.complete(merged.clone().filter(q!(|v| *v == 1)).map(q!(|v| v * 10)));
4294
4295 let out_recv = merged.sim_output();
4296
4297 let mut saw_cycle_before_second = false;
4300 flow.sim().exhaustive(async || {
4301 in_send.send(1);
4302 in_send.send(2);
4303
4304 let out = out_recv.collect::<Vec<_>>().await;
4305
4306 let pos_1 = out.iter().position(|v| *v == 1).unwrap();
4308 let pos_10 = out.iter().position(|v| *v == 10).unwrap();
4309 assert!(pos_1 < pos_10, "causal order violated: {:?}", out);
4310
4311 if out == [1, 10, 2] {
4313 saw_cycle_before_second = true;
4314 }
4315
4316 let mut sorted = out;
4317 sorted.sort();
4318 assert_eq!(sorted, vec![1, 2, 10]);
4319 });
4320
4321 assert!(
4322 saw_cycle_before_second,
4323 "never saw the cycled element arrive before the second input element"
4324 );
4325 }
4326
4327 #[cfg(feature = "sim")]
4331 #[test]
4332 fn sim_merge_ordered_delayed() {
4333 let mut flow = FlowBuilder::new();
4334 let node = flow.process::<()>();
4335
4336 let (in_send, input) = node.sim_input();
4337 let (in_send2, input2) = node.sim_input();
4338
4339 let out_recv = input
4340 .merge_ordered(input2, nondet!())
4341 .sim_output();
4342
4343 let mut saw_delayed_interleaving = false;
4344 flow.sim().exhaustive(async || {
4345 in_send.send(1);
4347 in_send2.send(3);
4348 in_send2.send(4);
4349
4350 let first_batch = out_recv.collect::<Vec<_>>().await;
4352
4353 in_send.send(2);
4355 let second_batch = out_recv.collect::<Vec<_>>().await;
4356
4357 let mut all: Vec<_> = first_batch
4358 .iter()
4359 .chain(second_batch.iter())
4360 .copied()
4361 .collect();
4362
4363 if all == [1, 3, 4, 2] {
4365 saw_delayed_interleaving = true;
4366 }
4367
4368 all.sort();
4369 assert_eq!(all, vec![1, 2, 3, 4]);
4370 });
4371
4372 assert!(saw_delayed_interleaving);
4373 }
4374
4375 #[cfg(feature = "deploy")]
4380 #[tokio::test]
4381 async fn deploy_merge_ordered_delayed() {
4382 let mut deployment = Deployment::new();
4383
4384 let mut flow = FlowBuilder::new();
4385 let node = flow.process::<()>();
4386 let external = flow.external::<()>();
4387
4388 let (input_a_port, input_a) = node.source_external_bincode(&external);
4389 let (input_b_port, input_b) = node.source_external_bincode(&external);
4390
4391 let out = input_a
4392 .assume_ordering(nondet!())
4393 .merge_ordered(
4394 input_b.assume_ordering(nondet!()),
4395 nondet!(),
4396 )
4397 .send_bincode_external(&external);
4398
4399 let nodes = flow
4400 .with_process(&node, deployment.Localhost())
4401 .with_external(&external, deployment.Localhost())
4402 .deploy(&mut deployment);
4403
4404 deployment.deploy().await.unwrap();
4405
4406 let mut ext_a = nodes.connect(input_a_port).await;
4407 let mut ext_b = nodes.connect(input_b_port).await;
4408 let mut ext_out = nodes.connect(out).await;
4409
4410 deployment.start().await.unwrap();
4411
4412 ext_a.send(1).await.unwrap();
4414 ext_b.send(3).await.unwrap();
4415 ext_b.send(4).await.unwrap();
4416
4417 let mut received = Vec::new();
4419 for _ in 0..3 {
4420 received.push(ext_out.next().await.unwrap());
4421 }
4422
4423 ext_a.send(2).await.unwrap();
4425 received.push(ext_out.next().await.unwrap());
4426
4427 received.sort();
4429 assert_eq!(received, vec![1, 2, 3, 4]);
4430 }
4431
4432 #[cfg(feature = "deploy")]
4433 #[tokio::test]
4434 async fn monotone_fold_threshold() {
4435 use crate::properties::manual_proof;
4436
4437 let mut deployment = Deployment::new();
4438
4439 let mut flow = FlowBuilder::new();
4440 let node = flow.process::<()>();
4441 let external = flow.external::<()>();
4442
4443 let in_unbounded: super::Stream<_, _> =
4444 node.source_iter(q!(vec![1i32, 2, 3, 4, 5, 6])).into();
4445 let sum = in_unbounded.fold(
4446 q!(|| 0),
4447 q!(
4448 |sum, v| {
4449 *sum += v;
4450 },
4451 monotone = manual_proof!()
4452 ),
4453 );
4454
4455 let threshold_out = sum
4456 .threshold_greater_or_equal(node.singleton(q!(7)))
4457 .send_bincode_external(&external);
4458
4459 let nodes = flow
4460 .with_process(&node, deployment.Localhost())
4461 .with_external(&external, deployment.Localhost())
4462 .deploy(&mut deployment);
4463
4464 deployment.deploy().await.unwrap();
4465
4466 let mut threshold_out = nodes.connect(threshold_out).await;
4467
4468 deployment.start().await.unwrap();
4469
4470 assert_eq!(threshold_out.next().await.unwrap(), 7);
4471 }
4472
4473 #[cfg(feature = "deploy")]
4474 #[tokio::test]
4475 async fn monotone_count_threshold() {
4476 let mut deployment = Deployment::new();
4477
4478 let mut flow = FlowBuilder::new();
4479 let node = flow.process::<()>();
4480 let external = flow.external::<()>();
4481
4482 let in_unbounded: super::Stream<_, _> =
4483 node.source_iter(q!(vec![1i32, 2, 3, 4, 5, 6])).into();
4484 let sum = in_unbounded.count();
4485
4486 let threshold_out = sum
4487 .threshold_greater_or_equal(node.singleton(q!(3)))
4488 .send_bincode_external(&external);
4489
4490 let nodes = flow
4491 .with_process(&node, deployment.Localhost())
4492 .with_external(&external, deployment.Localhost())
4493 .deploy(&mut deployment);
4494
4495 deployment.deploy().await.unwrap();
4496
4497 let mut threshold_out = nodes.connect(threshold_out).await;
4498
4499 deployment.start().await.unwrap();
4500
4501 assert_eq!(threshold_out.next().await.unwrap(), 3);
4502 }
4503
4504 #[cfg(feature = "deploy")]
4505 #[tokio::test]
4506 async fn monotone_map_order_preserving_threshold() {
4507 use crate::properties::manual_proof;
4508
4509 let mut deployment = Deployment::new();
4510
4511 let mut flow = FlowBuilder::new();
4512 let node = flow.process::<()>();
4513 let external = flow.external::<()>();
4514
4515 let in_unbounded: super::Stream<_, _> =
4516 node.source_iter(q!(vec![1i32, 2, 3, 4, 5, 6])).into();
4517 let sum = in_unbounded.fold(
4518 q!(|| 0),
4519 q!(
4520 |sum, v| {
4521 *sum += v;
4522 },
4523 monotone = manual_proof!()
4524 ),
4525 );
4526
4527 let doubled = sum.map(q!(
4529 |v| v * 2,
4530 order_preserving = manual_proof!()
4531 ));
4532
4533 let threshold_out = doubled
4534 .threshold_greater_or_equal(node.singleton(q!(14)))
4535 .send_bincode_external(&external);
4536
4537 let nodes = flow
4538 .with_process(&node, deployment.Localhost())
4539 .with_external(&external, deployment.Localhost())
4540 .deploy(&mut deployment);
4541
4542 deployment.deploy().await.unwrap();
4543
4544 let mut threshold_out = nodes.connect(threshold_out).await;
4545
4546 deployment.start().await.unwrap();
4547
4548 assert_eq!(threshold_out.next().await.unwrap(), 14);
4549 }
4550
4551 #[cfg(any(feature = "deploy", feature = "sim"))]
4554 mod join_ordering_type_tests {
4555 use crate::live_collections::boundedness::{Bounded, Unbounded};
4556 use crate::live_collections::stream::{ExactlyOnce, NoOrder, Stream, TotalOrder};
4557 use crate::location::{Location, Process};
4558
4559 #[expect(dead_code, reason = "compile-time type test")]
4560 fn join_unbounded_with_bounded_preserves_order<'a>(
4561 left: Stream<(i32, char), Process<'a>, Unbounded, TotalOrder, ExactlyOnce>,
4562 right: Stream<(i32, char), Process<'a>, Bounded, TotalOrder, ExactlyOnce>,
4563 ) -> Stream<(i32, (char, char)), Process<'a>, Unbounded, TotalOrder, ExactlyOnce> {
4564 left.join(right)
4565 }
4566
4567 #[expect(dead_code, reason = "compile-time type test")]
4568 fn join_unbounded_with_unbounded_is_no_order<'a>(
4569 left: Stream<(i32, char), Process<'a>, Unbounded, TotalOrder, ExactlyOnce>,
4570 right: Stream<(i32, char), Process<'a>, Unbounded, TotalOrder, ExactlyOnce>,
4571 ) -> Stream<(i32, (char, char)), Process<'a>, Unbounded, NoOrder, ExactlyOnce> {
4572 left.join(right)
4573 }
4574
4575 #[expect(dead_code, reason = "compile-time type test")]
4576 fn join_bounded_with_bounded_preserves_order<'a, L: Location<'a>>(
4577 left: Stream<(i32, char), L, Bounded, TotalOrder, ExactlyOnce>,
4578 right: Stream<(i32, char), L, Bounded, TotalOrder, ExactlyOnce>,
4579 ) -> Stream<(i32, (char, char)), L, Bounded, TotalOrder, ExactlyOnce> {
4580 left.join(right)
4581 }
4582
4583 #[expect(dead_code, reason = "compile-time type test")]
4584 fn join_unbounded_noorder_with_bounded<'a>(
4585 left: Stream<(i32, char), Process<'a>, Unbounded, NoOrder, ExactlyOnce>,
4586 right: Stream<(i32, char), Process<'a>, Bounded, NoOrder, ExactlyOnce>,
4587 ) -> Stream<(i32, (char, char)), Process<'a>, Unbounded, NoOrder, ExactlyOnce> {
4588 left.join(right)
4589 }
4590
4591 #[expect(dead_code, reason = "compile-time type test")]
4594 fn cross_product_unbounded_with_bounded_preserves_order<'a>(
4595 left: Stream<i32, Process<'a>, Unbounded, TotalOrder, ExactlyOnce>,
4596 right: Stream<char, Process<'a>, Bounded, TotalOrder, ExactlyOnce>,
4597 ) -> Stream<(i32, char), Process<'a>, Unbounded, TotalOrder, ExactlyOnce> {
4598 left.cross_product(right)
4599 }
4600
4601 #[expect(dead_code, reason = "compile-time type test")]
4602 fn cross_product_bounded_with_bounded_preserves_order<'a>(
4603 left: Stream<i32, Process<'a>, Bounded, TotalOrder, ExactlyOnce>,
4604 right: Stream<char, Process<'a>, Bounded, TotalOrder, ExactlyOnce>,
4605 ) -> Stream<(i32, char), Process<'a>, Bounded, TotalOrder, ExactlyOnce> {
4606 left.cross_product(right)
4607 }
4608
4609 #[expect(dead_code, reason = "compile-time type test")]
4610 fn cross_product_unbounded_with_unbounded_is_no_order<'a>(
4611 left: Stream<i32, Process<'a>, Unbounded, TotalOrder, ExactlyOnce>,
4612 right: Stream<char, Process<'a>, Unbounded, TotalOrder, ExactlyOnce>,
4613 ) -> Stream<(i32, char), Process<'a>, Unbounded, NoOrder, ExactlyOnce> {
4614 left.cross_product(right)
4615 }
4616 } #[cfg(feature = "sim")]
4621 #[test]
4622 fn cross_product_mixed_boundedness_correctness() {
4623 use stageleft::q;
4624
4625 use crate::compile::builder::FlowBuilder;
4626 use crate::nondet::nondet;
4627
4628 let mut flow = FlowBuilder::new();
4629 let process = flow.process::<()>();
4630 let tick = process.tick();
4631
4632 let left = process.source_iter(q!(vec![1, 2]));
4633 let right = process
4634 .source_iter(q!(vec!['a', 'b']))
4635 .batch(&tick, nondet!())
4636 .all_ticks();
4637
4638 let out = left.cross_product(right).sim_output();
4639
4640 flow.sim().exhaustive(async || {
4641 out.assert_yields_only_unordered(vec![(1, 'a'), (1, 'b'), (2, 'a'), (2, 'b')])
4642 .await;
4643 });
4644 }
4645
4646 #[cfg(feature = "sim")]
4647 #[test]
4648 fn join_mixed_boundedness_correctness() {
4649 use stageleft::q;
4650
4651 use crate::compile::builder::FlowBuilder;
4652 use crate::nondet::nondet;
4653
4654 let mut flow = FlowBuilder::new();
4655 let process = flow.process::<()>();
4656 let tick = process.tick();
4657
4658 let left = process.source_iter(q!(vec![(1, 'a'), (2, 'b')]));
4659 let right = process
4660 .source_iter(q!(vec![(1, 'x'), (2, 'y')]))
4661 .batch(&tick, nondet!())
4662 .all_ticks();
4663
4664 let out = left.join(right).sim_output();
4665
4666 flow.sim().exhaustive(async || {
4667 out.assert_yields_only_unordered(vec![(1, ('a', 'x')), (2, ('b', 'y'))])
4668 .await;
4669 });
4670 }
4671
4672 #[cfg(feature = "sim")]
4673 #[test]
4674 fn sim_merge_unordered_independent_atomics() {
4675 let mut flow = FlowBuilder::new();
4676 let node = flow.process::<()>();
4677
4678 let (in1_send, input1) = node.sim_input::<_, TotalOrder, _>();
4679 let (in2_send, input2) = node.sim_input::<_, TotalOrder, _>();
4680
4681 let out = input1
4682 .atomic()
4683 .merge_unordered(input2.atomic())
4684 .end_atomic()
4685 .sim_output();
4686
4687 flow.sim().exhaustive(async || {
4688 in1_send.send(1);
4689 in2_send.send(2);
4690
4691 out.assert_yields_only_unordered(vec![1, 2]).await;
4692 });
4693 }
4694
4695 #[cfg(feature = "deploy")]
4696 #[tokio::test]
4697 async fn test_stream_ref() {
4698 let mut deployment = Deployment::new();
4699
4700 let mut flow = FlowBuilder::new();
4701 let external = flow.external::<()>();
4702 let p1 = flow.process::<()>();
4703
4704 let my_stream = p1.source_iter(q!(1..=5i32));
4706
4707 let stream_ref = my_stream.by_ref();
4708
4709 let out_port = p1
4711 .source_iter(q!([()]))
4712 .map(q!(|_| stream_ref.len() as i32))
4713 .send_bincode_external(&external);
4714
4715 my_stream.for_each(q!(|_| {}));
4717
4718 let nodes = flow
4719 .with_default_optimize()
4720 .with_process(&p1, deployment.Localhost())
4721 .with_external(&external, deployment.Localhost())
4722 .deploy(&mut deployment);
4723
4724 deployment.deploy().await.unwrap();
4725
4726 let mut out_recv = nodes.connect(out_port).await;
4727
4728 deployment.start().await.unwrap();
4729
4730 let result = out_recv.next().await.unwrap();
4731 assert_eq!(result, 5);
4733 }
4734
4735 #[cfg(feature = "deploy")]
4736 #[tokio::test]
4737 async fn test_stream_ref_contents() {
4738 let mut deployment = Deployment::new();
4739
4740 let mut flow = FlowBuilder::new();
4741 let external = flow.external::<()>();
4742 let p1 = flow.process::<()>();
4743
4744 let my_stream = p1.source_iter(q!(1..=3i32));
4746
4747 let stream_ref = my_stream.by_ref();
4748
4749 let out_port = p1
4751 .source_iter(q!([()]))
4752 .map(q!(|_| stream_ref.iter().sum::<i32>()))
4753 .send_bincode_external(&external);
4754
4755 my_stream.for_each(q!(|_| {}));
4756
4757 let nodes = flow
4758 .with_default_optimize()
4759 .with_process(&p1, deployment.Localhost())
4760 .with_external(&external, deployment.Localhost())
4761 .deploy(&mut deployment);
4762
4763 deployment.deploy().await.unwrap();
4764
4765 let mut out_recv = nodes.connect(out_port).await;
4766
4767 deployment.start().await.unwrap();
4768
4769 let result = out_recv.next().await.unwrap();
4770 assert_eq!(result, 6);
4772 }
4773
4774 #[cfg(feature = "deploy")]
4775 #[tokio::test]
4776 async fn test_stream_ref_no_consumer() {
4777 let mut deployment = Deployment::new();
4778
4779 let mut flow = FlowBuilder::new();
4780 let external = flow.external::<()>();
4781 let p1 = flow.process::<()>();
4782
4783 let my_stream = p1.source_iter(q!(1..=4i32));
4785
4786 let stream_ref = my_stream.by_ref();
4787
4788 let out_port = p1
4789 .source_iter(q!([()]))
4790 .map(q!(|_| stream_ref.len() as i32))
4791 .send_bincode_external(&external);
4792
4793 let nodes = flow
4794 .with_default_optimize()
4795 .with_process(&p1, deployment.Localhost())
4796 .with_external(&external, deployment.Localhost())
4797 .deploy(&mut deployment);
4798
4799 deployment.deploy().await.unwrap();
4800
4801 let mut out_recv = nodes.connect(out_port).await;
4802
4803 deployment.start().await.unwrap();
4804
4805 let result = out_recv.next().await.unwrap();
4806 assert_eq!(result, 4);
4807 }
4808
4809 #[cfg(feature = "deploy")]
4810 #[tokio::test]
4811 async fn test_stream_mut() {
4812 let mut deployment = Deployment::new();
4813
4814 let mut flow = FlowBuilder::new();
4815 let external = flow.external::<()>();
4816 let p1 = flow.process::<()>();
4817
4818 let my_stream = p1.source_iter(q!(1..=5i32));
4820
4821 let stream_mut = my_stream.by_mut();
4822
4823 let out_port = p1
4825 .source_iter(q!([()]))
4826 .map(q!(|_| {
4827 stream_mut.retain(|x| *x > 3);
4828 stream_mut.len() as i32
4829 }))
4830 .send_bincode_external(&external);
4831
4832 my_stream.for_each(q!(|_| {}));
4833
4834 let nodes = flow
4835 .with_default_optimize()
4836 .with_process(&p1, deployment.Localhost())
4837 .with_external(&external, deployment.Localhost())
4838 .deploy(&mut deployment);
4839
4840 deployment.deploy().await.unwrap();
4841
4842 let mut out_recv = nodes.connect(out_port).await;
4843
4844 deployment.start().await.unwrap();
4845
4846 let result = out_recv.next().await.unwrap();
4847 assert_eq!(result, 2);
4849 }
4850
4851 #[cfg(feature = "sim")]
4855 #[test]
4856 fn sim_map_with_mut_on_unordered_explores_multiple_states() {
4857 use crate::live_collections::sliced::sliced;
4858 use crate::live_collections::stream::ExactlyOnce;
4859 use crate::properties::manual_proof;
4860
4861 let mut flow = FlowBuilder::new();
4862 let node = flow.process::<()>();
4863
4864 let (trigger_send, trigger) = node.sim_input::<i32, TotalOrder, ExactlyOnce>();
4865
4866 let out_recv = sliced! {
4867 let batch = use(trigger, nondet!());
4868 let counter = batch.location().source_iter(q!(vec![0i32]))
4869 .fold(q!(|| 0i32), q!(|acc, v| *acc += v));
4870 let counter_mut = counter.by_mut();
4871 let items = batch.location().source_iter(q!(vec![1i32, 2])).weaken_ordering::<NoOrder>();
4872 items.map(q!(
4873 |x| {
4874 *counter_mut += x;
4875 *counter_mut
4876 },
4877 commutative = manual_proof!()
4878 ))
4879 }
4880 .sim_output();
4881
4882 let count = flow.sim().exhaustive(async || {
4883 trigger_send.send(1);
4884 let _all: Vec<i32> = out_recv.collect_sorted().await;
4885 });
4886
4887 assert_eq!(
4888 count, 2,
4889 "Expected 2 simulation instances due to mut on unordered input, got {}",
4890 count
4891 );
4892 }
4893
4894 #[cfg(feature = "sim")]
4898 #[test]
4899 fn sim_scan_with_ref_capture() {
4900 use crate::live_collections::sliced::sliced;
4901 use crate::live_collections::stream::ExactlyOnce;
4902
4903 let mut flow = FlowBuilder::new();
4904 let node = flow.process::<()>();
4905
4906 let (trigger_send, trigger) = node.sim_input::<i32, TotalOrder, ExactlyOnce>();
4907
4908 let out_recv = sliced! {
4909 let batch = use(trigger, nondet!());
4910 let offset = batch
4911 .location()
4912 .source_iter(q!(vec![10i32]))
4913 .fold(q!(|| 0i32), q!(|acc, v| *acc += v));
4914 let offset_ref = offset.by_ref();
4915 batch
4916 .location()
4917 .source_iter(q!(vec![1i32, 2, 3]))
4918 .scan(
4919 q!(|| 0i32),
4920 q!(move |acc: &mut i32, x| {
4921 *acc += x + *offset_ref;
4922 Some(*acc)
4923 }),
4924 )
4925 }
4926 .sim_output();
4927
4928 let count = flow.sim().exhaustive(async || {
4929 trigger_send.send(1);
4930 let all: Vec<i32> = out_recv.collect().await;
4931 assert_eq!(all, vec![11, 23, 36]);
4936 });
4937
4938 assert_eq!(
4939 count, 1,
4940 "Expected a single simulation instance for a totally-ordered scan, got {}",
4941 count
4942 );
4943 }
4944
4945 #[cfg(feature = "sim")]
4949 #[test]
4950 #[ignore = "observe_nondet not yet supported for top-level bounded inputs (https://github.com/hydro-project/hydro/issues/2950)"]
4951 fn sim_map_with_mut_on_unordered_top_level() {
4952 use crate::properties::manual_proof;
4953
4954 let mut flow = FlowBuilder::new();
4955 let node = flow.process::<()>();
4956
4957 let counter = node
4958 .source_iter(q!(vec![0i32]))
4959 .fold(q!(|| 0i32), q!(|acc, v| *acc += v));
4960 let counter_mut = counter.by_mut();
4961
4962 let out_recv = node
4963 .source_iter(q!(vec![1i32, 2]))
4964 .weaken_ordering::<NoOrder>()
4965 .map(q!(
4966 |x| {
4967 *counter_mut += x;
4968 *counter_mut
4969 },
4970 commutative = manual_proof!()
4971 ))
4972 .assume_ordering::<TotalOrder>(nondet!())
4973 .sim_output();
4974
4975 counter.into_stream().for_each(q!(|_| {}));
4976
4977 let count = flow.sim().exhaustive(async || {
4978 let _all: Vec<i32> = out_recv.collect().await;
4979 });
4980
4981 assert_eq!(
4982 count, 2,
4983 "Expected 2 simulation instances due to mut on unordered input, got {}",
4984 count
4985 );
4986 }
4987}