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::OperatorContext;
15use super::boundedness::{Bounded, Boundedness, IsBounded, Unbounded};
16use super::keyed_singleton::KeyedSingleton;
17use super::keyed_stream::{Generate, KeyedStream};
18use super::optional::Optional;
19use super::singleton::Singleton;
20use crate::compile::builder::{CycleId, FlowState};
21use crate::compile::ir::{
22 CollectionKind, HydroIrOpMetadata, HydroNode, HydroRoot, SharedNode, StreamOrder, StreamRetry,
23};
24#[cfg(stageleft_runtime)]
25use crate::forward_handle::{CycleCollection, CycleCollectionWithInitial, ReceiverComplete};
26use crate::forward_handle::{ForwardRef, TickCycle};
27use crate::live_collections::batch_atomic::BatchAtomic;
28use crate::live_collections::singleton::SingletonBound;
29#[cfg(stageleft_runtime)]
30use crate::location::dynamic::{DynLocation, LocationId};
31use crate::location::tick::{Atomic, DeferTick};
32use crate::location::{Location, Tick, TopLevel, check_matching_location};
33use crate::manual_expr::ManualExpr;
34use crate::nondet::{NonDet, nondet};
35use crate::prelude::manual_proof;
36use crate::properties::{
37 AggFuncAlgebra, ApplyMonotoneStream, NotProved, StreamMapFuncAlgebra, ValidCommutativityFor,
38 ValidIdempotenceFor, ValidMutBorrowCommutativityFor, ValidMutBorrowIdempotenceFor,
39 ValidMutCommutativityFor, ValidMutIdempotenceFor,
40};
41
42pub mod networking;
43
44#[sealed::sealed]
46pub trait Ordering:
47 MinOrder<Self, Min = Self> + MinOrder<TotalOrder, Min = Self> + MinOrder<NoOrder, Min = NoOrder>
48{
49 const ORDERING_KIND: StreamOrder;
51}
52
53pub enum TotalOrder {}
57
58#[sealed::sealed]
59impl Ordering for TotalOrder {
60 const ORDERING_KIND: StreamOrder = StreamOrder::TotalOrder;
61}
62
63pub enum NoOrder {}
69
70#[sealed::sealed]
71impl Ordering for NoOrder {
72 const ORDERING_KIND: StreamOrder = StreamOrder::NoOrder;
73}
74
75#[sealed::sealed]
79pub trait WeakerOrderingThan<Other: ?Sized>: Ordering {}
80#[sealed::sealed]
81impl<O, O2: Ordering> WeakerOrderingThan<O2> for O where O: Ordering + MinOrder<O2, Min = O> {}
82
83#[sealed::sealed]
85pub trait MinOrder<Other: ?Sized> {
86 type Min: Ordering;
88}
89
90#[sealed::sealed]
91impl<O: Ordering> MinOrder<O> for TotalOrder {
92 type Min = O;
93}
94
95#[sealed::sealed]
96impl<O: Ordering> MinOrder<O> for NoOrder {
97 type Min = NoOrder;
98}
99
100#[sealed::sealed]
102pub trait Retries:
103 MinRetries<Self, Min = Self>
104 + MinRetries<ExactlyOnce, Min = Self>
105 + MinRetries<AtLeastOnce, Min = AtLeastOnce>
106{
107 const RETRIES_KIND: StreamRetry;
109}
110
111pub enum ExactlyOnce {}
114
115#[sealed::sealed]
116impl Retries for ExactlyOnce {
117 const RETRIES_KIND: StreamRetry = StreamRetry::ExactlyOnce;
118}
119
120pub enum AtLeastOnce {}
123
124#[sealed::sealed]
125impl Retries for AtLeastOnce {
126 const RETRIES_KIND: StreamRetry = StreamRetry::AtLeastOnce;
127}
128
129#[sealed::sealed]
133pub trait WeakerRetryThan<Other: ?Sized>: Retries {}
134#[sealed::sealed]
135impl<R, R2: Retries> WeakerRetryThan<R2> for R where R: Retries + MinRetries<R2, Min = R> {}
136
137#[sealed::sealed]
139pub trait MinRetries<Other: ?Sized> {
140 type Min: Retries + WeakerRetryThan<Self> + WeakerRetryThan<Other>;
142}
143
144#[sealed::sealed]
145impl<R: Retries> MinRetries<R> for ExactlyOnce {
146 type Min = R;
147}
148
149#[sealed::sealed]
150impl<R: Retries> MinRetries<R> for AtLeastOnce {
151 type Min = AtLeastOnce;
152}
153
154#[sealed::sealed]
155#[diagnostic::on_unimplemented(
156 message = "the input stream must be totally ordered (`TotalOrder`), but has order `{Self}`",
157 label = "required here",
158 note = "strengthen the order upstream, or consider a different API",
159 note = "to intentionally process the stream by observing a non-deterministic (shuffled) order of elements, use `.assume_ordering`; this introduces non-determinism, so avoid it unless necessary"
160)]
161pub trait IsOrdered: Ordering {}
163
164#[sealed::sealed]
165#[diagnostic::do_not_recommend]
166impl IsOrdered for TotalOrder {}
167
168#[sealed::sealed]
169#[diagnostic::on_unimplemented(
170 message = "the input stream must be exactly-once (`ExactlyOnce`), but has retries `{Self}`",
171 label = "required here",
172 note = "strengthen the retries guarantee upstream, or consider a different API",
173 note = "to intentionally process the stream by observing non-deterministic (randomly duplicated) retries, use `.assume_retries`; this introduces non-determinism, so avoid it unless necessary"
174)]
175pub trait IsExactlyOnce: Retries {}
177
178#[sealed::sealed]
179#[diagnostic::do_not_recommend]
180impl IsExactlyOnce for ExactlyOnce {}
181
182pub struct Stream<
202 Type,
203 Loc,
204 Bound: Boundedness = Unbounded,
205 Order: Ordering = TotalOrder,
206 Retry: Retries = ExactlyOnce,
207> {
208 pub(crate) location: Loc,
209 pub(crate) ir_node: Rc<RefCell<HydroNode>>,
210 pub(crate) flow_state: FlowState,
211
212 _phantom: PhantomData<(Type, Loc, Bound, Order, Retry)>,
213}
214
215impl<T, L, B: Boundedness, O: Ordering, R: Retries> Drop for Stream<T, L, B, O, R> {
216 fn drop(&mut self) {
217 let ir_node = self.ir_node.replace(HydroNode::Placeholder);
218 if !matches!(ir_node, HydroNode::Placeholder) && !ir_node.is_shared_with_others() {
219 self.flow_state.borrow_mut().try_push_root(HydroRoot::Null {
220 input: Box::new(ir_node),
221 op_metadata: HydroIrOpMetadata::new(),
222 });
223 }
224 }
225}
226
227impl<'a, T, L, O: Ordering, R: Retries> From<Stream<T, L, Bounded, O, R>>
228 for Stream<T, L, Unbounded, O, R>
229where
230 L: Location<'a>,
231{
232 fn from(stream: Stream<T, L, Bounded, O, R>) -> Stream<T, L, Unbounded, O, R> {
233 let new_meta = stream
234 .location
235 .new_node_metadata(Stream::<T, L, Unbounded, O, R>::collection_kind());
236
237 let flow_state = stream.flow_state.clone();
238 Stream {
239 location: stream.location.clone(),
240 ir_node: super::tracked_ir_node(
241 &flow_state,
242 HydroNode::Cast {
243 inner: Box::new(stream.ir_node.replace(HydroNode::Placeholder)),
244 metadata: new_meta,
245 },
246 ),
247 flow_state,
248 _phantom: PhantomData,
249 }
250 }
251}
252
253impl<'a, T, L, B: Boundedness, R: Retries> From<Stream<T, L, B, TotalOrder, R>>
254 for Stream<T, L, B, NoOrder, R>
255where
256 L: Location<'a>,
257{
258 fn from(stream: Stream<T, L, B, TotalOrder, R>) -> Stream<T, L, B, NoOrder, R> {
259 stream.weaken_ordering()
260 }
261}
262
263impl<'a, T, L, B: Boundedness, O: Ordering> From<Stream<T, L, B, O, ExactlyOnce>>
264 for Stream<T, L, B, O, AtLeastOnce>
265where
266 L: Location<'a>,
267{
268 fn from(stream: Stream<T, L, B, O, ExactlyOnce>) -> Stream<T, L, B, O, AtLeastOnce> {
269 stream.weaken_retries()
270 }
271}
272
273impl<'a, T, L, O: Ordering, R: Retries> DeferTick for Stream<T, Tick<L>, Bounded, O, R>
274where
275 L: Location<'a>,
276{
277 fn defer_tick(self) -> Self {
278 Stream::defer_tick(self)
279 }
280}
281
282impl<'a, T, L, O: Ordering, R: Retries> CycleCollection<'a, TickCycle>
283 for Stream<T, Tick<L>, Bounded, O, R>
284where
285 L: Location<'a>,
286{
287 type Location = Tick<L>;
288
289 fn create_source(cycle_id: CycleId, location: Tick<L>) -> Self {
290 Stream::new(
291 location.clone(),
292 HydroNode::CycleSource {
293 cycle_id,
294 metadata: location.new_node_metadata(Self::collection_kind()),
295 },
296 )
297 }
298}
299
300impl<'a, T, L, O: Ordering, R: Retries> CycleCollectionWithInitial<'a, TickCycle>
301 for Stream<T, Tick<L>, Bounded, O, R>
302where
303 L: Location<'a>,
304{
305 type Location = Tick<L>;
306
307 fn location(&self) -> &Self::Location {
308 self.location()
309 }
310
311 fn create_source_with_initial(cycle_id: CycleId, initial: Self, location: Tick<L>) -> Self {
312 let from_previous_tick: Stream<T, Tick<L>, Bounded, O, R> = Stream::new(
313 location.clone(),
314 HydroNode::DeferTick {
315 input: Box::new(HydroNode::CycleSource {
316 cycle_id,
317 metadata: location.new_node_metadata(Self::collection_kind()),
318 }),
319 metadata: location.new_node_metadata(Self::collection_kind()),
320 },
321 );
322
323 from_previous_tick.chain(initial.filter_if(location.optional_first_tick(q!(())).is_some()))
324 }
325}
326
327impl<'a, T, L, O: Ordering, R: Retries> ReceiverComplete<'a, TickCycle>
328 for Stream<T, Tick<L>, Bounded, O, R>
329where
330 L: Location<'a>,
331{
332 fn complete(self, cycle_id: CycleId, expected_location: LocationId) {
333 assert_eq!(
334 Location::id(&self.location),
335 expected_location,
336 "locations do not match"
337 );
338 self.location
339 .flow_state()
340 .borrow_mut()
341 .push_root(HydroRoot::CycleSink {
342 cycle_id,
343 input: Box::new(self.ir_node.replace(HydroNode::Placeholder)),
344 op_metadata: HydroIrOpMetadata::new(),
345 });
346 }
347}
348
349impl<'a, T, L, B: Boundedness, O: Ordering, R: Retries> CycleCollection<'a, ForwardRef>
350 for Stream<T, L, B, O, R>
351where
352 L: Location<'a>,
353{
354 type Location = L;
355
356 fn create_source(cycle_id: CycleId, location: L) -> Self {
357 Stream::new(
358 location.clone(),
359 HydroNode::CycleSource {
360 cycle_id,
361 metadata: location.new_node_metadata(Self::collection_kind()),
362 },
363 )
364 }
365}
366
367impl<'a, T, L, B: Boundedness, O: Ordering, R: Retries> ReceiverComplete<'a, ForwardRef>
368 for Stream<T, L, B, O, R>
369where
370 L: Location<'a>,
371{
372 fn complete(self, cycle_id: CycleId, expected_location: LocationId) {
373 assert_eq!(
374 Location::id(&self.location),
375 expected_location,
376 "locations do not match"
377 );
378 self.location
379 .flow_state()
380 .borrow_mut()
381 .push_root(HydroRoot::CycleSink {
382 cycle_id,
383 input: Box::new(self.ir_node.replace(HydroNode::Placeholder)),
384 op_metadata: HydroIrOpMetadata::new(),
385 });
386 }
387}
388
389impl<'a, T, L, B: Boundedness, O: Ordering, R: Retries> Clone for Stream<T, L, B, O, R>
390where
391 T: Clone,
392 L: Location<'a>,
393{
394 fn clone(&self) -> Self {
395 if !matches!(self.ir_node.borrow().deref(), HydroNode::Tee { .. }) {
396 let orig_ir_node = self.ir_node.replace(HydroNode::Placeholder);
397 *self.ir_node.borrow_mut() = HydroNode::Tee {
398 inner: SharedNode(Rc::new(RefCell::new(orig_ir_node))),
399 metadata: self.location.new_node_metadata(Self::collection_kind()),
400 };
401 }
402
403 let HydroNode::Tee { inner, metadata } = &*self.ir_node.borrow() else {
404 unreachable!()
405 };
406 Stream {
407 location: self.location.clone(),
408 flow_state: self.flow_state.clone(),
409 ir_node: super::tracked_ir_node(
410 &self.flow_state,
411 HydroNode::Tee {
412 inner: SharedNode(inner.0.clone()),
413 metadata: metadata.clone(),
414 },
415 ),
416 _phantom: PhantomData,
417 }
418 }
419}
420
421impl<'a, T, L, B: Boundedness, O: Ordering, R: Retries> Stream<T, L, B, O, R>
422where
423 L: Location<'a>,
424{
425 pub(crate) fn new(location: L, ir_node: HydroNode) -> Self {
426 debug_assert_eq!(ir_node.metadata().location_id, Location::id(&location));
427 debug_assert_eq!(ir_node.metadata().collection_kind, Self::collection_kind());
428
429 let flow_state = location.flow_state().clone();
430 let ir_node = super::tracked_ir_node(&flow_state, ir_node);
431 Stream {
432 location,
433 flow_state,
434 ir_node,
435 _phantom: PhantomData,
436 }
437 }
438
439 pub fn location(&self) -> &L {
441 &self.location
442 }
443
444 pub fn by_ref(&self) -> crate::handoff_ref::StreamRef<'a, '_, T, L, B>
449 where
450 B: IsBounded,
451 {
452 crate::handoff_ref::StreamRef::new(&self.ir_node)
453 }
454
455 pub fn by_mut(&self) -> crate::handoff_ref::StreamMut<'a, '_, T, L, B>
458 where
459 B: IsBounded,
460 {
461 crate::handoff_ref::StreamMut::new(&self.ir_node)
462 }
463
464 pub fn weaken_consistency(self) -> Stream<T, L::DropConsistency, B, O, R>
467 where
468 L: Location<'a>,
469 {
470 if L::consistency()
471 .is_none_or(|c| c == crate::location::dynamic::ClusterConsistency::NoConsistency)
472 {
473 Stream::new(
475 self.location.drop_consistency(),
476 self.ir_node.replace(HydroNode::Placeholder),
477 )
478 } else {
479 Stream::new(
480 self.location.drop_consistency(),
481 HydroNode::Cast {
482 inner: Box::new(self.ir_node.replace(HydroNode::Placeholder)),
483 metadata: self.location.drop_consistency().new_node_metadata(Stream::<
484 T,
485 L::DropConsistency,
486 B,
487 O,
488 R,
489 >::collection_kind(
490 )),
491 },
492 )
493 }
494 }
495
496 pub fn assert_has_consistency_of<L2: Location<'a, DropConsistency = L::DropConsistency>>(
500 self,
501 _proof: impl crate::properties::ConsistencyProof,
502 ) -> Stream<T, L2, B, O, R>
503 where
504 L: Location<'a>,
505 {
506 if L::consistency() == L2::consistency() {
507 Stream::new(
508 self.location.with_consistency_of(),
509 self.ir_node.replace(HydroNode::Placeholder),
510 )
511 } else {
512 Stream::new(
513 self.location.with_consistency_of(),
514 HydroNode::AssertIsConsistent {
515 inner: Box::new(self.ir_node.replace(HydroNode::Placeholder)),
516 trusted: false,
517 metadata: self
518 .location
519 .clone()
520 .with_consistency_of::<L2>()
521 .new_node_metadata(Stream::<T, L2, B, O, R>::collection_kind()),
522 },
523 )
524 }
525 }
526
527 pub(crate) fn assert_has_consistency_of_trusted<
528 L2: Location<'a, DropConsistency = L::DropConsistency>,
529 >(
530 self,
531 _proof: impl crate::properties::ConsistencyProof,
532 ) -> Stream<T, L2, B, O, R>
533 where
534 L: Location<'a>,
535 {
536 if L::consistency() == L2::consistency() {
537 Stream::new(
538 self.location.with_consistency_of(),
539 self.ir_node.replace(HydroNode::Placeholder),
540 )
541 } else {
542 Stream::new(
543 self.location.with_consistency_of(),
544 HydroNode::AssertIsConsistent {
545 inner: Box::new(self.ir_node.replace(HydroNode::Placeholder)),
546 trusted: true,
547 metadata: self
548 .location
549 .clone()
550 .with_consistency_of::<L2>()
551 .new_node_metadata(Stream::<T, L2, B, O, R>::collection_kind()),
552 },
553 )
554 }
555 }
556
557 pub(crate) fn collection_kind() -> CollectionKind {
558 CollectionKind::Stream {
559 bound: B::BOUND_KIND,
560 order: O::ORDERING_KIND,
561 retry: R::RETRIES_KIND,
562 element_type: quote_type::<T>().into(),
563 }
564 }
565
566 pub fn map<U, F, C, I, const WAS_MUT: bool>(
594 self,
595 f: impl IntoQuotedMut<
596 'a,
597 F,
598 OperatorContext<L, B>,
599 StreamMapFuncAlgebra<T, B, C, I, L::SimHookScope>,
600 >,
601 ) -> Stream<U, L, B, O, R>
602 where
603 F: FnMut(T) -> U + 'a,
604 C: ValidMutCommutativityFor<F, T, U, O, WAS_MUT>,
605 I: ValidMutIdempotenceFor<F, T, U, R, WAS_MUT>,
606 {
607 let f = crate::handoff_ref::with_ref_capture(|| {
608 let (expr, proof) =
609 f.splice_fnmut1_ctx_props(&OperatorContext::<L, B>::new(&self.location));
610 proof.register_proof(&expr);
611 expr.into()
612 });
613 Stream::new(
614 self.location.clone(),
615 HydroNode::Map {
616 f,
617 input: Box::new(self.ir_node.replace(HydroNode::Placeholder)),
618 metadata: self
619 .location
620 .new_node_metadata(Stream::<U, L, B, O, R>::collection_kind()),
621 },
622 )
623 }
624
625 pub fn flat_map_ordered<U, I, F, C, Idemp, const WAS_MUT: bool>(
650 self,
651 f: impl IntoQuotedMut<
652 'a,
653 F,
654 OperatorContext<L, B>,
655 StreamMapFuncAlgebra<T, B, C, Idemp, L::SimHookScope>,
656 >,
657 ) -> Stream<U, L, B, O, R>
658 where
659 I: IntoIterator<Item = U>,
660 F: FnMut(T) -> I + 'a,
661 C: ValidMutCommutativityFor<F, T, I, O, WAS_MUT>,
662 Idemp: ValidMutIdempotenceFor<F, T, I, R, WAS_MUT>,
663 {
664 let f = crate::handoff_ref::with_ref_capture(|| {
665 let (expr, proof) =
666 f.splice_fnmut1_ctx_props(&OperatorContext::<L, B>::new(&self.location));
667 proof.register_proof(&expr);
668 expr.into()
669 });
670 Stream::new(
671 self.location.clone(),
672 HydroNode::FlatMap {
673 f,
674 input: Box::new(self.ir_node.replace(HydroNode::Placeholder)),
675 metadata: self
676 .location
677 .new_node_metadata(Stream::<U, L, B, O, R>::collection_kind()),
678 },
679 )
680 }
681
682 pub fn flat_map_unordered<U, I, F, C, Idemp, const WAS_MUT: bool>(
709 self,
710 f: impl IntoQuotedMut<
711 'a,
712 F,
713 OperatorContext<L, B>,
714 StreamMapFuncAlgebra<T, B, C, Idemp, L::SimHookScope>,
715 >,
716 ) -> Stream<U, L, B, NoOrder, R>
717 where
718 I: IntoIterator<Item = U>,
719 F: FnMut(T) -> I + 'a,
720 C: ValidMutCommutativityFor<F, T, I, O, WAS_MUT>,
721 Idemp: ValidMutIdempotenceFor<F, T, I, R, WAS_MUT>,
722 {
723 let f = crate::handoff_ref::with_ref_capture(|| {
724 let (expr, proof) =
725 f.splice_fnmut1_ctx_props(&OperatorContext::<L, B>::new(&self.location));
726 proof.register_proof(&expr);
727 expr.into()
728 });
729 Stream::new(
730 self.location.clone(),
731 HydroNode::FlatMap {
732 f,
733 input: Box::new(self.ir_node.replace(HydroNode::Placeholder)),
734 metadata: self
735 .location
736 .new_node_metadata(Stream::<U, L, B, NoOrder, R>::collection_kind()),
737 },
738 )
739 }
740
741 pub fn flatten_ordered<U>(self) -> Stream<U, L, B, O, R>
764 where
765 T: IntoIterator<Item = U>,
766 {
767 self.flat_map_ordered(q!(|d| d))
768 }
769
770 pub fn flatten_unordered<U>(self) -> Stream<U, L, B, NoOrder, R>
797 where
798 T: IntoIterator<Item = U>,
799 {
800 self.flat_map_unordered(q!(|d| d))
801 }
802
803 pub fn flat_map_stream_blocking<U, S, F, C, Idemp, const WAS_MUT: bool>(
807 self,
808 f: impl IntoQuotedMut<
809 'a,
810 F,
811 OperatorContext<L, B>,
812 StreamMapFuncAlgebra<T, B, C, Idemp, L::SimHookScope>,
813 >,
814 ) -> Stream<U, L, B, O, R>
815 where
816 S: futures::Stream<Item = U>,
817 F: FnMut(T) -> S + 'a,
818 C: ValidMutCommutativityFor<F, T, S, O, WAS_MUT>,
819 Idemp: ValidMutIdempotenceFor<F, T, S, R, WAS_MUT>,
820 {
821 let f = crate::handoff_ref::with_ref_capture(|| {
822 let (expr, proof) =
823 f.splice_fnmut1_ctx_props(&OperatorContext::<L, B>::new(&self.location));
824 proof.register_proof(&expr);
825 expr.into()
826 });
827 Stream::new(
828 self.location.clone(),
829 HydroNode::FlatMapStreamBlocking {
830 f,
831 input: Box::new(self.ir_node.replace(HydroNode::Placeholder)),
832 metadata: self
833 .location
834 .new_node_metadata(Stream::<U, L, B, O, R>::collection_kind()),
835 },
836 )
837 }
838
839 pub fn flatten_stream_blocking<U>(self) -> Stream<U, L, B, O, R>
843 where
844 T: futures::Stream<Item = U>,
845 {
846 self.flat_map_stream_blocking(q!(|d| d))
847 }
848
849 pub fn filter<F, C, Idemp, const WAS_MUT: bool>(
881 self,
882 f: impl IntoQuotedMut<
883 'a,
884 F,
885 OperatorContext<L, B>,
886 StreamMapFuncAlgebra<T, B, C, Idemp, L::SimHookScope>,
887 >,
888 ) -> Self
889 where
890 F: FnMut(&T) -> bool + 'a,
891 C: ValidMutBorrowCommutativityFor<F, T, bool, O, WAS_MUT>,
892 Idemp: ValidMutBorrowIdempotenceFor<F, T, bool, R, WAS_MUT>,
893 {
894 let f = crate::handoff_ref::with_ref_capture(|| {
895 let (expr, proof) =
896 f.splice_fnmut1_borrow_ctx_props(&OperatorContext::<L, B>::new(&self.location));
897 proof.register_proof(&expr);
898 expr.into()
899 });
900 Stream::new(
901 self.location.clone(),
902 HydroNode::Filter {
903 f,
904 input: Box::new(self.ir_node.replace(HydroNode::Placeholder)),
905 metadata: self.location.new_node_metadata(Self::collection_kind()),
906 },
907 )
908 }
909
910 pub fn partition<F, C, Idemp, const WAS_MUT: bool>(
945 self,
946 f: impl IntoQuotedMut<
947 'a,
948 F,
949 OperatorContext<L, B>,
950 StreamMapFuncAlgebra<T, B, C, Idemp, L::SimHookScope>,
951 >,
952 ) -> (Stream<T, L, B, O, R>, Stream<T, L, B, O, R>)
953 where
954 F: FnMut(&T) -> bool + 'a,
955 C: ValidMutBorrowCommutativityFor<F, T, bool, O, WAS_MUT>,
956 Idemp: ValidMutBorrowIdempotenceFor<F, T, bool, R, WAS_MUT>,
957 {
958 let f = crate::handoff_ref::with_ref_capture(|| {
959 let (expr, proof) =
960 f.splice_fnmut1_borrow_ctx_props(&OperatorContext::<L, B>::new(&self.location));
961 proof.register_proof(&expr);
962 expr.into()
963 });
964 let shared = Rc::new(RefCell::new(HydroNode::PartitionShared {
965 input: Box::new(self.ir_node.replace(HydroNode::Placeholder)),
966 f,
967 metadata: self.location.new_node_metadata(Self::collection_kind()),
968 }));
969
970 let true_stream = Stream::new(
971 self.location.clone(),
972 HydroNode::PartitionSide {
973 inner: SharedNode(Rc::clone(&shared)),
974 is_true: true,
975 metadata: self.location.new_node_metadata(Self::collection_kind()),
976 },
977 );
978
979 let false_stream = Stream::new(
980 self.location.clone(),
981 HydroNode::PartitionSide {
982 inner: SharedNode(shared),
983 is_true: false,
984 metadata: self.location.new_node_metadata(Self::collection_kind()),
985 },
986 );
987
988 (true_stream, false_stream)
989 }
990
991 pub fn filter_map<U, F, C, Idemp, const WAS_MUT: bool>(
1011 self,
1012 f: impl IntoQuotedMut<
1013 'a,
1014 F,
1015 OperatorContext<L, B>,
1016 StreamMapFuncAlgebra<T, B, C, Idemp, L::SimHookScope>,
1017 >,
1018 ) -> Stream<U, L, B, O, R>
1019 where
1020 F: FnMut(T) -> Option<U> + 'a,
1021 C: ValidMutCommutativityFor<F, T, Option<U>, O, WAS_MUT>,
1022 Idemp: ValidMutIdempotenceFor<F, T, Option<U>, R, WAS_MUT>,
1023 {
1024 let f = crate::handoff_ref::with_ref_capture(|| {
1025 let (expr, proof) =
1026 f.splice_fnmut1_ctx_props(&OperatorContext::<L, B>::new(&self.location));
1027 proof.register_proof(&expr);
1028 expr.into()
1029 });
1030 Stream::new(
1031 self.location.clone(),
1032 HydroNode::FilterMap {
1033 f,
1034 input: Box::new(self.ir_node.replace(HydroNode::Placeholder)),
1035 metadata: self
1036 .location
1037 .new_node_metadata(Stream::<U, L, B, O, R>::collection_kind()),
1038 },
1039 )
1040 }
1041
1042 pub fn cross_singleton<O2>(
1067 self,
1068 other: impl Into<Optional<O2, L, Bounded>>,
1069 ) -> Stream<(T, O2), L, B, O, R>
1070 where
1071 O2: Clone,
1072 {
1073 let other: Optional<O2, L, Bounded> = other.into();
1074 check_matching_location(&self.location, &other.location);
1075
1076 Stream::new(
1077 self.location.clone(),
1078 HydroNode::CrossSingleton {
1079 left: Box::new(self.ir_node.replace(HydroNode::Placeholder)),
1080 right: Box::new(other.ir_node.replace(HydroNode::Placeholder)),
1081 metadata: self
1082 .location
1083 .new_node_metadata(Stream::<(T, O2), L, B, O, R>::collection_kind()),
1084 },
1085 )
1086 }
1087
1088 pub fn filter_if(self, signal: Singleton<bool, L, Bounded>) -> Stream<T, L, B, O, R> {
1120 self.cross_singleton(signal.filter(q!(|b| *b)))
1121 .map(q!(|(d, _)| d))
1122 }
1123
1124 #[deprecated(note = "use `filter_if` with `Optional::is_some()` instead")]
1159 pub fn filter_if_some<U>(self, signal: Optional<U, L, Bounded>) -> Stream<T, L, B, O, R> {
1160 self.filter_if(signal.is_some())
1161 }
1162
1163 #[deprecated(note = "use `filter_if` with `!Optional::is_some()` instead")]
1198 pub fn filter_if_none<U>(self, other: Optional<U, L, Bounded>) -> Stream<T, L, B, O, R> {
1199 self.filter_if(other.is_none())
1200 }
1201
1202 pub fn cross_product<T2, B2: Boundedness, O2: Ordering, R2: Retries>(
1227 self,
1228 other: Stream<T2, L, B2, O2, R2>,
1229 ) -> Stream<(T, T2), L, B, B2::PreserveOrderIfBounded<O>, <R as MinRetries<R2>>::Min>
1230 where
1231 T: Clone,
1232 T2: Clone,
1233 R: MinRetries<R2>,
1234 {
1235 self.map(q!(|v| ((), v)))
1236 .join(other.map(q!(|v| ((), v))))
1237 .map(q!(|((), (v1, v2))| (v1, v2)))
1238 }
1239
1240 pub fn unique(self) -> Stream<T, L, B, O, ExactlyOnce>
1259 where
1260 T: Eq + Hash,
1261 {
1262 Stream::new(
1263 self.location.clone(),
1264 HydroNode::Unique {
1265 input: Box::new(self.ir_node.replace(HydroNode::Placeholder)),
1266 metadata: self
1267 .location
1268 .new_node_metadata(Stream::<T, L, B, O, ExactlyOnce>::collection_kind()),
1269 },
1270 )
1271 }
1272
1273 pub fn filter_not_in<O2: Ordering, B2>(self, other: Stream<T, L, B2, O2, R>) -> Self
1299 where
1300 T: Eq + Hash,
1301 B2: IsBounded,
1302 {
1303 check_matching_location(&self.location, &other.location);
1304
1305 Stream::new(
1306 self.location.clone(),
1307 HydroNode::Difference {
1308 pos: Box::new(self.ir_node.replace(HydroNode::Placeholder)),
1309 neg: Box::new(other.ir_node.replace(HydroNode::Placeholder)),
1310 metadata: self
1311 .location
1312 .new_node_metadata(Stream::<T, L, Bounded, O, R>::collection_kind()),
1313 },
1314 )
1315 }
1316
1317 pub fn inspect<F, C, Idemp, const WAS_MUT: bool>(
1343 self,
1344 f: impl IntoQuotedMut<
1345 'a,
1346 F,
1347 OperatorContext<L::DropConsistency, B>,
1348 StreamMapFuncAlgebra<T, B, C, Idemp, L::SimHookScope>,
1349 >,
1350 ) -> Self
1351 where
1352 F: FnMut(&T) + 'a,
1353 C: ValidMutBorrowCommutativityFor<F, T, (), O, WAS_MUT>,
1354 Idemp: ValidMutBorrowIdempotenceFor<F, T, (), R, WAS_MUT>,
1355 {
1356 let f = crate::handoff_ref::with_ref_capture(|| {
1357 let (expr, proof) =
1358 f.splice_fnmut1_borrow_ctx_props(&OperatorContext::<L::DropConsistency, B>::new(
1359 &self.location.drop_consistency(),
1360 ));
1361 proof.register_proof(&expr);
1362 expr.into()
1363 });
1364
1365 Stream::new(
1366 self.location.clone(),
1367 HydroNode::Inspect {
1368 f,
1369 input: Box::new(self.ir_node.replace(HydroNode::Placeholder)),
1370 metadata: self.location.new_node_metadata(Self::collection_kind()),
1371 },
1372 )
1373 }
1374
1375 pub fn for_each<F: FnMut(T) + 'a, C, I>(
1397 self,
1398 f: impl IntoQuotedMut<
1399 'a,
1400 F,
1401 OperatorContext<L, B>,
1402 AggFuncAlgebra<T, B, C, I, NotProved, L::SimHookScope>,
1403 >,
1404 ) where
1405 C: ValidCommutativityFor<O>,
1406 I: ValidIdempotenceFor<R>,
1407 {
1408 let f = crate::handoff_ref::with_ref_capture(|| {
1409 let (f, proof) =
1410 f.splice_fnmut1_ctx_props(&OperatorContext::<L, B>::new(&self.location));
1411 proof.register_proof(&f);
1412 f.into()
1413 });
1414 self.location
1415 .flow_state()
1416 .borrow_mut()
1417 .push_root(HydroRoot::ForEach {
1418 input: Box::new(self.ir_node.replace(HydroNode::Placeholder)),
1419 f,
1420 op_metadata: HydroIrOpMetadata::new(),
1421 });
1422 }
1423
1424 pub fn dest_sink<S>(self, sink: impl QuotedWithContext<'a, S, L>)
1430 where
1431 O: IsOrdered,
1432 R: IsExactlyOnce,
1433 S: 'a + futures::Sink<T> + Unpin,
1434 {
1435 self.location
1436 .flow_state()
1437 .borrow_mut()
1438 .push_root(HydroRoot::DestSink {
1439 sink: sink.splice_typed_ctx(&self.location).into(),
1440 input: Box::new(self.ir_node.replace(HydroNode::Placeholder)),
1441 op_metadata: HydroIrOpMetadata::new(),
1442 });
1443 }
1444
1445 pub fn enumerate(self) -> Stream<(usize, T), L, B, O, R>
1465 where
1466 O: IsOrdered,
1467 R: IsExactlyOnce,
1468 {
1469 Stream::new(
1470 self.location.clone(),
1471 HydroNode::Enumerate {
1472 input: Box::new(self.ir_node.replace(HydroNode::Placeholder)),
1473 metadata: self.location.new_node_metadata(Stream::<
1474 (usize, T),
1475 L,
1476 B,
1477 TotalOrder,
1478 ExactlyOnce,
1479 >::collection_kind()),
1480 },
1481 )
1482 }
1483
1484 pub fn fold<A, I, F, C, Idemp, M, B2: SingletonBound>(
1508 self,
1509 init: impl IntoQuotedMut<'a, I, OperatorContext<L, B>>,
1510 comb: impl IntoQuotedMut<
1511 'a,
1512 F,
1513 OperatorContext<L, B>,
1514 AggFuncAlgebra<T, B, C, Idemp, M, L::SimHookScope>,
1515 >,
1516 ) -> Singleton<A, L, B2>
1517 where
1518 I: Fn() -> A + 'a,
1519 F: 'a + Fn(&mut A, T),
1520 C: ValidCommutativityFor<O>,
1521 Idemp: ValidIdempotenceFor<R>,
1522 B: ApplyMonotoneStream<M, B2>,
1523 {
1524 let init = init
1525 .splice_fn0_ctx(&OperatorContext::<L, B>::new(&self.location))
1526 .into();
1527 let (comb, proof) =
1528 comb.splice_fn2_borrow_mut_ctx_props(&OperatorContext::<L, B>::new(&self.location));
1529 let ordering_hook = proof.register_proof(&comb);
1530
1531 let nondet = nondet!();
1535 let retried: Stream<T, L::DropConsistency, B, O, ExactlyOnce> = self.assume_retries(nondet);
1536
1537 let mut metadata = retried
1538 .location
1539 .new_node_metadata(Singleton::<A, L::DropConsistency, B2>::collection_kind());
1540 metadata.op.sim_hook_id = ordering_hook.map(|hook| hook.id);
1541
1542 let core = HydroNode::Fold {
1543 init,
1544 acc: comb.into(),
1545 input: Box::new(retried.ir_node.replace(HydroNode::Placeholder)),
1546 metadata,
1547 };
1552
1553 Singleton::new(retried.location.clone(), core)
1554 .assert_has_consistency_of(manual_proof!())
1555 }
1556
1557 pub fn reduce<F, C, Idemp>(
1580 self,
1581 comb: impl IntoQuotedMut<
1582 'a,
1583 F,
1584 OperatorContext<L, B>,
1585 AggFuncAlgebra<T, B, C, Idemp, NotProved, L::SimHookScope>,
1586 >,
1587 ) -> Optional<T, L, B::AggregatedOptional>
1588 where
1589 F: Fn(&mut T, T) + 'a,
1590 C: ValidCommutativityFor<O>,
1591 Idemp: ValidIdempotenceFor<R>,
1592 {
1593 let (f, proof) =
1594 comb.splice_fn2_borrow_mut_ctx_props(&OperatorContext::<L, B>::new(&self.location));
1595 let ordering_hook = proof.register_proof(&f);
1596
1597 let nondet_retries = nondet!();
1598 let ordered_etc: Stream<T, L::DropConsistency, B> =
1599 self.assume_retries(nondet_retries).assume_ordering(nondet!(
1600 hook = ordering_hook
1603 ));
1604
1605 let core = HydroNode::Reduce {
1606 f: f.into(),
1607 input: Box::new(ordered_etc.ir_node.replace(HydroNode::Placeholder)),
1608 metadata: ordered_etc.location.new_node_metadata(Optional::<
1609 T,
1610 L::DropConsistency,
1611 B::AggregatedOptional,
1612 >::collection_kind()),
1613 };
1614
1615 Optional::new(ordered_etc.location.clone(), core)
1616 .assert_has_consistency_of(manual_proof!())
1617 }
1618
1619 pub fn max(self) -> Optional<T, L, B::AggregatedOptional>
1639 where
1640 T: Ord,
1641 {
1642 self.assume_retries_trusted::<ExactlyOnce>(nondet!())
1643 .assume_ordering_trusted_bounded::<TotalOrder>(
1644 nondet!(),
1645 )
1646 .reduce(q!(|curr, new| {
1647 if new > *curr {
1648 *curr = new;
1649 }
1650 }))
1651 }
1652
1653 pub fn min(self) -> Optional<T, L, B::AggregatedOptional>
1673 where
1674 T: Ord,
1675 {
1676 self.assume_retries_trusted::<ExactlyOnce>(nondet!())
1677 .assume_ordering_trusted_bounded::<TotalOrder>(
1678 nondet!(),
1679 )
1680 .reduce(q!(|curr, new| {
1681 if new < *curr {
1682 *curr = new;
1683 }
1684 }))
1685 }
1686
1687 pub fn first(self) -> Optional<T, L, B::AggregatedOptional>
1710 where
1711 O: IsOrdered,
1712 {
1713 self.make_totally_ordered()
1714 .assume_retries_trusted::<ExactlyOnce>(nondet!())
1715 .generator(q!(|| ()), q!(|_, item| Generate::Return(item)))
1716 .reduce(q!(|_, _| {}))
1717 }
1718
1719 pub fn last(self) -> Optional<T, L, B::AggregatedOptional>
1742 where
1743 O: IsOrdered,
1744 {
1745 self.make_totally_ordered()
1746 .assume_retries_trusted::<ExactlyOnce>(nondet!())
1747 .reduce(q!(|curr, new| *curr = new))
1748 }
1749
1750 pub fn limit(
1773 self,
1774 n: impl QuotedWithContext<'a, usize, OperatorContext<L, B>> + Copy + 'a,
1775 ) -> Stream<T, L, B, TotalOrder, ExactlyOnce>
1776 where
1777 O: IsOrdered,
1778 R: IsExactlyOnce,
1779 {
1780 self.generator(
1781 q!(|| 0usize),
1782 q!(move |count, item| {
1783 if *count == n {
1784 Generate::Break
1785 } else {
1786 *count += 1;
1787 if *count == n {
1788 Generate::Return(item)
1789 } else {
1790 Generate::Yield(item)
1791 }
1792 }
1793 }),
1794 )
1795 }
1796
1797 pub fn collect_vec(self) -> Singleton<Vec<T>, L, B>
1823 where
1824 O: IsOrdered,
1825 R: IsExactlyOnce,
1826 {
1827 self.make_totally_ordered().make_exactly_once().fold(
1828 q!(|| vec![]),
1829 q!(|acc, v| {
1830 acc.push(v);
1831 }),
1832 )
1833 }
1834
1835 pub fn scan<A, U, I, F>(
1901 self,
1902 init: impl IntoQuotedMut<'a, I, OperatorContext<L, B>>,
1903 f: impl IntoQuotedMut<'a, F, OperatorContext<L, B>>,
1904 ) -> Stream<U, L, B, TotalOrder, ExactlyOnce>
1905 where
1906 O: IsOrdered,
1907 R: IsExactlyOnce,
1908 I: Fn() -> A + 'a,
1909 F: Fn(&mut A, T) -> Option<U> + 'a,
1910 {
1911 let init = crate::handoff_ref::with_ref_capture(|| {
1912 init.splice_fn0_ctx(&OperatorContext::<L, B>::new(&self.location))
1913 .into()
1914 });
1915 let f = crate::handoff_ref::with_ref_capture(|| {
1916 f.splice_fn2_borrow_mut_ctx(&OperatorContext::<L, B>::new(&self.location))
1917 .into()
1918 });
1919
1920 Stream::new(
1921 self.location.clone(),
1922 HydroNode::Scan {
1923 init,
1924 acc: f,
1925 input: Box::new(self.ir_node.replace(HydroNode::Placeholder)),
1926 metadata: self.location.new_node_metadata(
1927 Stream::<U, L, B, TotalOrder, ExactlyOnce>::collection_kind(),
1928 ),
1929 },
1930 )
1931 }
1932
1933 pub fn scan_async_blocking<A, U, I, F, Fut>(
1972 self,
1973 init: impl IntoQuotedMut<'a, I, OperatorContext<L, B>>,
1974 f: impl IntoQuotedMut<'a, F, OperatorContext<L, B>>,
1975 ) -> Stream<U, L, B, TotalOrder, ExactlyOnce>
1976 where
1977 O: IsOrdered,
1978 R: IsExactlyOnce,
1979 I: Fn() -> A + 'a,
1980 F: Fn(&mut A, T) -> Fut + 'a,
1981 Fut: Future<Output = Option<U>> + 'a,
1982 {
1983 let init = crate::handoff_ref::with_ref_capture(|| {
1984 init.splice_fn0_ctx(&OperatorContext::<L, B>::new(&self.location))
1985 .into()
1986 });
1987 let f = crate::handoff_ref::with_ref_capture(|| {
1988 f.splice_fn2_borrow_mut_ctx(&OperatorContext::<L, B>::new(&self.location))
1989 .into()
1990 });
1991
1992 Stream::new(
1993 self.location.clone(),
1994 HydroNode::ScanAsyncBlocking {
1995 init,
1996 acc: f,
1997 input: Box::new(self.ir_node.replace(HydroNode::Placeholder)),
1998 metadata: self.location.new_node_metadata(
1999 Stream::<U, L, B, TotalOrder, ExactlyOnce>::collection_kind(),
2000 ),
2001 },
2002 )
2003 }
2004
2005 pub fn generator<A, U, I, F>(
2050 self,
2051 init: impl IntoQuotedMut<'a, I, OperatorContext<L, B>> + Copy,
2052 f: impl IntoQuotedMut<'a, F, OperatorContext<L, B>> + Copy,
2053 ) -> Stream<U, L, B, TotalOrder, ExactlyOnce>
2054 where
2055 O: IsOrdered,
2056 R: IsExactlyOnce,
2057 I: Fn() -> A + 'a,
2058 F: Fn(&mut A, T) -> Generate<U> + 'a,
2059 {
2060 let init: ManualExpr<I, _> =
2061 ManualExpr::new(move |ctx: &OperatorContext<L, B>| init.splice_fn0_ctx(ctx));
2062 let f: ManualExpr<F, _> =
2063 ManualExpr::new(move |ctx: &OperatorContext<L, B>| f.splice_fn2_borrow_mut_ctx(ctx));
2064
2065 let this = self.make_totally_ordered().make_exactly_once();
2066
2067 let scan_init = crate::handoff_ref::with_ref_capture(|| {
2072 q!(|| None)
2073 .splice_fn0_ctx::<Option<Option<A>>>(&this.location)
2074 .into()
2075 });
2076 let scan_f = crate::handoff_ref::with_ref_capture(|| {
2077 q!(move |state: &mut Option<Option<_>>, v| {
2078 if state.is_none() {
2079 *state = Some(Some(init()));
2080 }
2081 match state {
2082 Some(Some(state_value)) => match f(state_value, v) {
2083 Generate::Yield(out) => Some(Some(out)),
2084 Generate::Return(out) => {
2085 *state = Some(None);
2086 Some(Some(out))
2087 }
2088 Generate::Break => None,
2092 Generate::Continue => Some(None),
2093 },
2094 _ => None,
2096 }
2097 })
2098 .splice_fn2_borrow_mut_ctx::<Option<Option<A>>, T, _>(&OperatorContext::<L, B>::new(
2099 &this.location,
2100 ))
2101 .into()
2102 });
2103
2104 let scan_node = HydroNode::Scan {
2105 init: scan_init,
2106 acc: scan_f,
2107 input: Box::new(this.ir_node.replace(HydroNode::Placeholder)),
2108 metadata: this.location.new_node_metadata(Stream::<
2109 Option<U>,
2110 L,
2111 B,
2112 TotalOrder,
2113 ExactlyOnce,
2114 >::collection_kind()),
2115 };
2116
2117 let flatten_f = q!(|d| d)
2118 .splice_fn1_ctx::<Option<U>, _>(&this.location)
2119 .into();
2120 let flatten_node = HydroNode::FlatMap {
2121 f: flatten_f,
2122 input: Box::new(scan_node),
2123 metadata: this
2124 .location
2125 .new_node_metadata(Stream::<U, L, B, TotalOrder, ExactlyOnce>::collection_kind()),
2126 };
2127
2128 Stream::new(this.location.clone(), flatten_node)
2129 }
2130
2131 #[cfg(feature = "tokio")]
2144 #[expect(
2145 clippy::type_complexity,
2146 reason = "composite hook payload names each internal operator's handle type"
2147 )]
2148 pub fn sample_every(
2149 self,
2150 interval: impl QuotedWithContext<'a, std::time::Duration, L> + Copy + 'a,
2151 mut nondet: NonDet<(
2152 Option<crate::sim_hooks::BatchHook<T, O, R, L::SimHookScope>>,
2153 Option<crate::sim_hooks::BatchHook<(), TotalOrder, ExactlyOnce, L::SimHookScope>>,
2154 )>,
2155 ) -> Stream<T, L::DropConsistency, Unbounded, O, AtLeastOnce>
2156 where
2157 L: TopLevel<'a>,
2158 {
2159 let samples = self.location.source_interval(interval);
2160 let (elements_hook, samples_hook) = nondet.take_hook();
2161
2162 let tick = self.location.tick();
2163 self.batch(
2164 &tick,
2165 nondet!(
2166 hook = elements_hook
2168 ),
2169 )
2170 .filter_if(
2171 samples
2172 .batch(
2173 &tick,
2174 nondet!(
2175 hook = samples_hook
2177 ),
2178 )
2179 .first()
2180 .is_some(),
2181 )
2182 .all_ticks()
2183 .weaken_retries()
2184 }
2185
2186 #[cfg(feature = "tokio")]
2196 pub fn timeout(
2197 self,
2198 duration: impl QuotedWithContext<
2199 'a,
2200 std::time::Duration,
2201 OperatorContext<Tick<L::DropConsistency>, Bounded>,
2202 > + Copy
2203 + 'a,
2204 nondet: NonDet,
2205 ) -> Optional<(), L::DropConsistency, Unbounded>
2206 where
2207 L: TopLevel<'a>,
2208 {
2209 let tick = self.location.tick();
2210
2211 let latest_received = self
2212 .assume_retries::<ExactlyOnce>(nondet!(
2213 nondet
2216 ))
2217 .fold(
2218 q!(|| None),
2219 q!(
2220 |latest, _| {
2221 *latest = Some(Instant::now());
2222 },
2223 commutative = manual_proof!()
2224 ),
2225 );
2226
2227 latest_received
2228 .snapshot(
2229 &tick,
2230 nondet!(
2231 nondet
2233 ),
2234 )
2235 .filter_map(q!(move |latest_received| {
2236 if let Some(latest_received) = latest_received {
2237 if Instant::now().duration_since(latest_received) > duration {
2238 Some(())
2239 } else {
2240 None
2241 }
2242 } else {
2243 Some(())
2244 }
2245 }))
2246 .latest()
2247 }
2248
2249 pub fn atomic(self) -> Stream<T, Atomic<L>, B, O, R>
2255 where
2256 L: TopLevel<'a>,
2257 {
2258 let out_location = Atomic {
2259 tick: self.location.tick(),
2260 };
2261 Stream::new(
2262 out_location.clone(),
2263 HydroNode::BeginAtomic {
2264 inner: Box::new(self.ir_node.replace(HydroNode::Placeholder)),
2265 metadata: out_location
2266 .new_node_metadata(Stream::<T, Atomic<L>, B, O, R>::collection_kind()),
2267 },
2268 )
2269 }
2270
2271 pub fn batch<L2: Location<'a, DropConsistency = L::DropConsistency>>(
2283 self,
2284 tick: &Tick<L2>,
2285 mut nondet: NonDet<Option<crate::sim_hooks::BatchHook<T, O, R, L::SimHookScope>>>,
2286 ) -> Stream<T, Tick<L::DropConsistency>, Bounded, O, R> {
2287 assert_eq!(
2288 Location::id(tick.parent_location()),
2289 Location::id(&self.location)
2290 );
2291
2292 let mut metadata =
2293 tick.new_node_metadata(Stream::<T, Tick<L>, Bounded, O, R>::collection_kind());
2294 metadata.op.sim_hook_id = nondet.take_hook().map(|h| h.id);
2295 Stream::new(
2296 tick.drop_consistency(),
2297 HydroNode::Batch {
2298 inner: Box::new(self.ir_node.replace(HydroNode::Placeholder)),
2299 metadata,
2300 },
2301 )
2302 }
2303
2304 pub fn ir_node_named(self, name: &str) -> Stream<T, L, B, O, R> {
2307 {
2308 let mut node = self.ir_node.borrow_mut();
2309 let metadata = node.metadata_mut();
2310 metadata.tag = Some(name.to_owned());
2311 }
2312 self
2313 }
2314
2315 pub(crate) fn cast_at_most_one_element(self) -> Optional<T, L, B>
2319 where
2320 B: IsBounded,
2321 {
2322 Optional::new(
2323 self.location.clone(),
2324 HydroNode::Cast {
2325 inner: Box::new(self.ir_node.replace(HydroNode::Placeholder)),
2326 metadata: self
2327 .location
2328 .new_node_metadata(Optional::<T, L, B>::collection_kind()),
2329 },
2330 )
2331 }
2332
2333 pub(crate) fn use_ordering_type<O2: Ordering>(self) -> Stream<T, L, B, O2, R> {
2334 if O::ORDERING_KIND == O2::ORDERING_KIND {
2335 Stream::new(
2336 self.location.clone(),
2337 self.ir_node.replace(HydroNode::Placeholder),
2338 )
2339 } else {
2340 panic!(
2341 "Runtime ordering {:?} did not match requested cast {:?}.",
2342 O::ORDERING_KIND,
2343 O2::ORDERING_KIND
2344 )
2345 }
2346 }
2347
2348 pub fn assume_ordering<O2: Ordering>(
2357 self,
2358 mut nondet: NonDet<Option<crate::sim_hooks::OrderingHook<T, B, R, L::SimHookScope>>>,
2359 ) -> Stream<T, L::DropConsistency, B, O2, R> {
2360 if O::ORDERING_KIND == O2::ORDERING_KIND {
2361 self.use_ordering_type().weaken_consistency()
2362 } else if O2::ORDERING_KIND == StreamOrder::NoOrder {
2363 let target_location = self.location().drop_consistency();
2365 Stream::new(
2366 target_location.clone(),
2367 HydroNode::Cast {
2368 inner: Box::new(self.ir_node.replace(HydroNode::Placeholder)),
2369 metadata: target_location
2370 .new_node_metadata(Stream::<T, L, B, O2, R>::collection_kind()),
2371 },
2372 )
2373 } else {
2374 let target_location = self.location().drop_consistency();
2375 let mut metadata =
2376 target_location.new_node_metadata(Stream::<T, L, B, O2, R>::collection_kind());
2377 metadata.op.sim_hook_id = nondet.take_hook().map(|hook| hook.id);
2378 Stream::new(
2379 target_location,
2380 HydroNode::ObserveNonDet {
2381 inner: Box::new(self.ir_node.replace(HydroNode::Placeholder)),
2382 trusted: false,
2383 metadata,
2384 },
2385 )
2386 }
2387 }
2388
2389 fn assume_ordering_trusted_bounded<O2: Ordering>(
2392 self,
2393 nondet: NonDet,
2394 ) -> Stream<T, L, B, O2, R> {
2395 if B::BOUNDED {
2396 self.assume_ordering_trusted(nondet)
2397 } else {
2398 let self_location = self.location.clone();
2399 let inner: Stream<T, L::DropConsistency, B, O2, R> = self.assume_ordering(nondet!(
2400 nondet
2402 ));
2403 Stream::new(self_location, inner.ir_node.replace(HydroNode::Placeholder))
2404 }
2405 }
2406
2407 pub(crate) fn assume_ordering_trusted<O2: Ordering>(
2410 self,
2411 _nondet: NonDet,
2412 ) -> Stream<T, L, B, O2, R> {
2413 if O::ORDERING_KIND == O2::ORDERING_KIND {
2414 self.use_ordering_type()
2415 } else if O2::ORDERING_KIND == StreamOrder::NoOrder {
2416 Stream::new(
2418 self.location.clone(),
2419 HydroNode::Cast {
2420 inner: Box::new(self.ir_node.replace(HydroNode::Placeholder)),
2421 metadata: self
2422 .location
2423 .new_node_metadata(Stream::<T, L, B, O2, R>::collection_kind()),
2424 },
2425 )
2426 } else {
2427 Stream::new(
2428 self.location.clone(),
2429 HydroNode::ObserveNonDet {
2430 inner: Box::new(self.ir_node.replace(HydroNode::Placeholder)),
2431 trusted: true,
2432 metadata: self
2433 .location
2434 .new_node_metadata(Stream::<T, L, B, O2, R>::collection_kind()),
2435 },
2436 )
2437 }
2438 }
2439
2440 #[deprecated = "use `weaken_ordering::<NoOrder>()` instead"]
2441 pub fn weakest_ordering(self) -> Stream<T, L, B, NoOrder, R> {
2444 self.weaken_ordering::<NoOrder>()
2445 }
2446
2447 pub fn weaken_ordering<O2: WeakerOrderingThan<O>>(self) -> Stream<T, L, B, O2, R> {
2450 let nondet = nondet!();
2451 self.assume_ordering_trusted::<O2>(nondet)
2452 }
2453
2454 pub fn make_totally_ordered(self) -> Stream<T, L, B, TotalOrder, R>
2457 where
2458 O: IsOrdered,
2459 {
2460 self.assume_ordering_trusted(nondet!())
2461 }
2462
2463 pub fn assume_retries<R2: Retries>(
2479 self,
2480 mut nondet: NonDet<Option<crate::sim_hooks::RetriesHook<T, O, B, L::SimHookScope>>>,
2481 ) -> Stream<T, L::DropConsistency, B, O, R2> {
2482 if R::RETRIES_KIND == R2::RETRIES_KIND {
2483 Stream::new(
2484 self.location.drop_consistency(),
2485 self.ir_node.replace(HydroNode::Placeholder),
2486 )
2487 } else if R2::RETRIES_KIND == StreamRetry::AtLeastOnce {
2488 let target_location = self.location.drop_consistency();
2490 Stream::new(
2491 target_location.clone(),
2492 HydroNode::Cast {
2493 inner: Box::new(self.ir_node.replace(HydroNode::Placeholder)),
2494 metadata: target_location
2495 .new_node_metadata(Stream::<T, L, B, O, R2>::collection_kind()),
2496 },
2497 )
2498 } else {
2499 let target_location = self.location.drop_consistency();
2500 let mut metadata =
2501 target_location.new_node_metadata(Stream::<T, L, B, O, R2>::collection_kind());
2502 metadata.op.sim_hook_id = nondet.take_hook().map(|hook| hook.id);
2503 Stream::new(
2504 target_location.clone(),
2505 HydroNode::ObserveNonDet {
2506 inner: Box::new(self.ir_node.replace(HydroNode::Placeholder)),
2507 trusted: false,
2508 metadata,
2509 },
2510 )
2511 }
2512 }
2513
2514 fn assume_retries_trusted<R2: Retries>(self, _nondet: NonDet) -> Stream<T, L, B, O, R2> {
2517 if R::RETRIES_KIND == R2::RETRIES_KIND {
2518 Stream::new(
2519 self.location.clone(),
2520 self.ir_node.replace(HydroNode::Placeholder),
2521 )
2522 } else if R2::RETRIES_KIND == StreamRetry::AtLeastOnce {
2523 Stream::new(
2525 self.location.clone(),
2526 HydroNode::Cast {
2527 inner: Box::new(self.ir_node.replace(HydroNode::Placeholder)),
2528 metadata: self
2529 .location
2530 .new_node_metadata(Stream::<T, L, B, O, R2>::collection_kind()),
2531 },
2532 )
2533 } else {
2534 Stream::new(
2535 self.location.clone(),
2536 HydroNode::ObserveNonDet {
2537 inner: Box::new(self.ir_node.replace(HydroNode::Placeholder)),
2538 trusted: true,
2539 metadata: self
2540 .location
2541 .new_node_metadata(Stream::<T, L, B, O, R2>::collection_kind()),
2542 },
2543 )
2544 }
2545 }
2546
2547 #[deprecated = "use `weaken_retries::<AtLeastOnce>()` instead"]
2548 pub fn weakest_retries(self) -> Stream<T, L, B, O, AtLeastOnce> {
2551 self.weaken_retries::<AtLeastOnce>()
2552 }
2553
2554 pub fn weaken_retries<R2: WeakerRetryThan<R>>(self) -> Stream<T, L, B, O, R2> {
2557 let nondet = nondet!();
2558 self.assume_retries_trusted::<R2>(nondet)
2559 }
2560
2561 pub fn make_exactly_once(self) -> Stream<T, L, B, O, ExactlyOnce>
2564 where
2565 R: IsExactlyOnce,
2566 {
2567 self.assume_retries_trusted(nondet!())
2568 }
2569
2570 pub fn make_bounded(self) -> Stream<T, L, Bounded, O, R>
2573 where
2574 B: IsBounded,
2575 {
2576 self.weaken_boundedness()
2577 }
2578
2579 pub fn weaken_boundedness<B2: Boundedness>(self) -> Stream<T, L, B2, O, R> {
2582 if B::BOUNDED == B2::BOUNDED {
2583 Stream::new(
2584 self.location.clone(),
2585 self.ir_node.replace(HydroNode::Placeholder),
2586 )
2587 } else {
2588 Stream::new(
2590 self.location.clone(),
2591 HydroNode::Cast {
2592 inner: Box::new(self.ir_node.replace(HydroNode::Placeholder)),
2593 metadata: self
2594 .location
2595 .new_node_metadata(Stream::<T, L, B2, O, R>::collection_kind()),
2596 },
2597 )
2598 }
2599 }
2600}
2601
2602impl<'a, T, L, B: Boundedness, O: Ordering, R: Retries> Stream<&T, L, B, O, R>
2603where
2604 L: Location<'a>,
2605{
2606 pub fn cloned(self) -> Stream<T, L, B, O, R>
2624 where
2625 T: Clone,
2626 {
2627 self.map(q!(|d| d.clone()))
2628 }
2629}
2630
2631impl<'a, T, L, B: Boundedness, O: Ordering> Stream<T, L, B, O, ExactlyOnce>
2632where
2633 L: Location<'a>,
2634{
2635 pub fn count(self) -> Singleton<usize, L, B::StreamToMonotone> {
2654 self.assume_ordering_trusted::<TotalOrder>(nondet!(
2655 ))
2657 .fold(
2658 q!(|| 0usize),
2659 q!(
2660 |count, _| *count += 1,
2661 monotone = manual_proof!()
2662 ),
2663 )
2664 }
2665}
2666
2667impl<'a, T, L: Location<'a>, O: Ordering, R: Retries> Stream<T, L, Unbounded, O, R> {
2668 pub fn merge_unordered<O2: Ordering, R2: Retries>(
2692 self,
2693 other: Stream<T, L, Unbounded, O2, R2>,
2694 ) -> Stream<T, L, Unbounded, NoOrder, <R as MinRetries<R2>>::Min>
2695 where
2696 R: MinRetries<R2>,
2697 {
2698 Stream::new(
2699 self.location.clone(),
2700 HydroNode::Chain {
2701 first: Box::new(self.ir_node.replace(HydroNode::Placeholder)),
2702 second: Box::new(other.ir_node.replace(HydroNode::Placeholder)),
2703 metadata: self.location.new_node_metadata(Stream::<
2704 T,
2705 L,
2706 Unbounded,
2707 NoOrder,
2708 <R as MinRetries<R2>>::Min,
2709 >::collection_kind()),
2710 },
2711 )
2712 }
2713
2714 #[deprecated(note = "use `merge_unordered` instead")]
2716 pub fn interleave<O2: Ordering, R2: Retries>(
2717 self,
2718 other: Stream<T, L, Unbounded, O2, R2>,
2719 ) -> Stream<T, L, Unbounded, NoOrder, <R as MinRetries<R2>>::Min>
2720 where
2721 R: MinRetries<R2>,
2722 {
2723 self.merge_unordered(other)
2724 }
2725}
2726
2727impl<'a, T, L: Location<'a>, B: Boundedness, R: Retries> Stream<T, L, B, TotalOrder, R> {
2728 pub fn merge_ordered<R2: Retries>(
2760 self,
2761 other: Stream<T, L, B, TotalOrder, R2>,
2762 mut nondet: NonDet<Option<crate::sim_hooks::MergeOrderedHook<T, B, L::SimHookScope>>>,
2763 ) -> Stream<T, L::DropConsistency, B, TotalOrder, <R as MinRetries<R2>>::Min>
2764 where
2765 R: MinRetries<R2>,
2766 {
2767 let target_location = self.location().drop_consistency();
2768 let mut metadata = target_location.new_node_metadata(Stream::<
2769 T,
2770 L::DropConsistency,
2771 B,
2772 TotalOrder,
2773 <R as MinRetries<R2>>::Min,
2774 >::collection_kind());
2775 metadata.op.sim_hook_id = nondet.take_hook().map(|hook| hook.id);
2776 Stream::new(
2777 target_location,
2778 HydroNode::MergeOrdered {
2779 first: Box::new(self.ir_node.replace(HydroNode::Placeholder)),
2780 second: Box::new(other.ir_node.replace(HydroNode::Placeholder)),
2781 metadata,
2782 },
2783 )
2784 }
2785}
2786
2787impl<'a, T, L, B: Boundedness, O: Ordering, R: Retries> Stream<T, L, B, O, R>
2788where
2789 L: Location<'a>,
2790{
2791 pub fn sort(self) -> Stream<T, L, Bounded, TotalOrder, R>
2817 where
2818 B: IsBounded,
2819 T: Ord,
2820 {
2821 let this = self.make_bounded();
2822 Stream::new(
2823 this.location.clone(),
2824 HydroNode::Sort {
2825 input: Box::new(this.ir_node.replace(HydroNode::Placeholder)),
2826 metadata: this
2827 .location
2828 .new_node_metadata(Stream::<T, L, Bounded, TotalOrder, R>::collection_kind()),
2829 },
2830 )
2831 }
2832
2833 pub fn chain<O2: Ordering, R2: Retries, B2: Boundedness>(
2861 self,
2862 other: Stream<T, L, B2, O2, R2>,
2863 ) -> Stream<T, L, B2, <O as MinOrder<O2>>::Min, <R as MinRetries<R2>>::Min>
2864 where
2865 B: IsBounded,
2866 O: MinOrder<O2>,
2867 R: MinRetries<R2>,
2868 {
2869 check_matching_location(&self.location, &other.location);
2870
2871 Stream::new(
2872 self.location.clone(),
2873 HydroNode::Chain {
2874 first: Box::new(self.ir_node.replace(HydroNode::Placeholder)),
2875 second: Box::new(other.ir_node.replace(HydroNode::Placeholder)),
2876 metadata: self.location.new_node_metadata(Stream::<
2877 T,
2878 L,
2879 B2,
2880 <O as MinOrder<O2>>::Min,
2881 <R as MinRetries<R2>>::Min,
2882 >::collection_kind()),
2883 },
2884 )
2885 }
2886
2887 pub fn cross_product_nested_loop<T2, O2: Ordering + MinOrder<O>, R2: Retries>(
2891 self,
2892 other: Stream<T2, L, Bounded, O2, R2>,
2893 ) -> Stream<(T, T2), L, Bounded, <O2 as MinOrder<O>>::Min, <R as MinRetries<R2>>::Min>
2894 where
2895 B: IsBounded,
2896 T: Clone,
2897 T2: Clone,
2898 R: MinRetries<R2>,
2899 {
2900 let this = self.make_bounded();
2901 check_matching_location(&this.location, &other.location);
2902
2903 Stream::new(
2904 this.location.clone(),
2905 HydroNode::CrossProduct {
2906 left: Box::new(this.ir_node.replace(HydroNode::Placeholder)),
2907 right: Box::new(other.ir_node.replace(HydroNode::Placeholder)),
2908 metadata: this.location.new_node_metadata(Stream::<
2909 (T, T2),
2910 L,
2911 Bounded,
2912 <O2 as MinOrder<O>>::Min,
2913 <R as MinRetries<R2>>::Min,
2914 >::collection_kind()),
2915 },
2916 )
2917 }
2918
2919 pub fn repeat_with_keys<K, V2>(
2957 self,
2958 keys: KeyedSingleton<K, V2, L, Bounded>,
2959 ) -> KeyedStream<K, T, L, Bounded, O, R>
2960 where
2961 B: IsBounded,
2962 K: Clone,
2963 T: Clone,
2964 {
2965 keys.keys()
2966 .assume_ordering_trusted::<TotalOrder>(
2967 nondet!(),
2968 )
2969 .cross_product_nested_loop(self.make_bounded())
2970 .into_keyed()
2971 }
2972
2973 pub fn resolve_futures_blocking(self) -> Stream<T::Output, L, B, NoOrder, R>
3010 where
3011 T: Future,
3012 {
3013 Stream::new(
3014 self.location.clone(),
3015 HydroNode::ResolveFuturesBlocking {
3016 input: Box::new(self.ir_node.replace(HydroNode::Placeholder)),
3017 metadata: self
3018 .location
3019 .new_node_metadata(Stream::<T::Output, L, B, NoOrder, R>::collection_kind()),
3020 },
3021 )
3022 }
3023
3024 #[expect(clippy::wrong_self_convention, reason = "stream function naming")]
3044 pub fn is_empty(self) -> Singleton<bool, L, Bounded>
3045 where
3046 B: IsBounded,
3047 {
3048 self.make_bounded()
3049 .assume_ordering_trusted::<TotalOrder>(
3050 nondet!(),
3051 )
3052 .first()
3053 .is_none()
3054 }
3055}
3056
3057impl<'a, K, V1, L, B: Boundedness, O: Ordering, R: Retries> Stream<(K, V1), L, B, O, R>
3058where
3059 L: Location<'a>,
3060{
3061 pub fn join<V2, B2: Boundedness, O2: Ordering, R2: Retries>(
3086 self,
3087 n: Stream<(K, V2), L, B2, O2, R2>,
3088 ) -> Stream<(K, (V1, V2)), L, B, B2::PreserveOrderIfBounded<O>, <R as MinRetries<R2>>::Min>
3089 where
3090 K: Eq + Hash + Clone,
3091 R: MinRetries<R2>,
3092 V1: Clone,
3093 V2: Clone,
3094 {
3095 check_matching_location(&self.location, &n.location);
3096
3097 let ir_node = if B2::BOUNDED {
3098 HydroNode::JoinHalf {
3099 left: Box::new(self.ir_node.replace(HydroNode::Placeholder)),
3100 right: Box::new(n.ir_node.replace(HydroNode::Placeholder)),
3101 metadata: self.location.new_node_metadata(Stream::<
3102 (K, (V1, V2)),
3103 L,
3104 B,
3105 B2::PreserveOrderIfBounded<O>,
3106 <R as MinRetries<R2>>::Min,
3107 >::collection_kind()),
3108 }
3109 } else {
3110 HydroNode::Join {
3111 left: Box::new(self.ir_node.replace(HydroNode::Placeholder)),
3112 right: Box::new(n.ir_node.replace(HydroNode::Placeholder)),
3113 metadata: self.location.new_node_metadata(Stream::<
3114 (K, (V1, V2)),
3115 L,
3116 B,
3117 B2::PreserveOrderIfBounded<O>,
3118 <R as MinRetries<R2>>::Min,
3119 >::collection_kind()),
3120 }
3121 };
3122
3123 Stream::new(self.location.clone(), ir_node)
3124 }
3125
3126 pub fn anti_join<O2: Ordering, R2: Retries>(
3152 self,
3153 n: Stream<K, L, Bounded, O2, R2>,
3154 ) -> Stream<(K, V1), L, B, O, R>
3155 where
3156 K: Eq + Hash,
3157 {
3158 check_matching_location(&self.location, &n.location);
3159
3160 Stream::new(
3161 self.location.clone(),
3162 HydroNode::AntiJoin {
3163 pos: Box::new(self.ir_node.replace(HydroNode::Placeholder)),
3164 neg: Box::new(n.ir_node.replace(HydroNode::Placeholder)),
3165 metadata: self
3166 .location
3167 .new_node_metadata(Stream::<(K, V1), L, B, O, R>::collection_kind()),
3168 },
3169 )
3170 }
3171}
3172
3173impl<'a, K, V, L: Location<'a>, B: Boundedness, O: Ordering, R: Retries>
3174 Stream<(K, V), L, B, O, R>
3175{
3176 pub fn into_keyed(self) -> KeyedStream<K, V, L, B, O, R> {
3203 KeyedStream::new(
3204 self.location.clone(),
3205 HydroNode::Cast {
3206 inner: Box::new(self.ir_node.replace(HydroNode::Placeholder)),
3207 metadata: self
3208 .location
3209 .new_node_metadata(KeyedStream::<K, V, L, B, O, R>::collection_kind()),
3210 },
3211 )
3212 }
3213}
3214
3215impl<'a, K, V, L, O: Ordering, R: Retries> Stream<(K, V), Tick<L>, Bounded, O, R>
3216where
3217 K: Eq + Hash,
3218 L: Location<'a>,
3219{
3220 pub fn keys(self) -> Stream<K, Tick<L>, Bounded, NoOrder, ExactlyOnce> {
3239 self.into_keyed()
3240 .fold(
3241 q!(|| ()),
3242 q!(
3243 |_, _| {},
3244 commutative = manual_proof!(),
3245 idempotent = manual_proof!()
3246 ),
3247 )
3248 .keys()
3249 }
3250}
3251
3252impl<'a, T, L, B: Boundedness, O: Ordering, R: Retries> Stream<T, Atomic<L>, B, O, R>
3253where
3254 L: Location<'a>,
3255{
3256 pub fn batch_atomic<L2: Location<'a, DropConsistency = L::DropConsistency>>(
3263 self,
3264 tick: &Tick<L2>,
3265 mut nondet: NonDet<Option<crate::sim_hooks::BatchHook<T, O, R, L::SimHookScope>>>,
3266 ) -> Stream<T, Tick<L::DropConsistency>, Bounded, O, R> {
3267 assert_eq!(
3268 Location::id(tick.parent_location()),
3269 Location::id(self.location.tick.parent_location())
3270 );
3271
3272 let mut metadata =
3273 tick.new_node_metadata(Stream::<T, Tick<L>, Bounded, O, R>::collection_kind());
3274
3275 metadata.op.sim_hook_id = nondet.take_hook().map(|h| h.id);
3276 Stream::new(
3277 tick.drop_consistency(),
3278 HydroNode::Batch {
3279 inner: Box::new(self.ir_node.replace(HydroNode::Placeholder)),
3280 metadata,
3281 },
3282 )
3283 }
3284
3285 pub fn end_atomic(self) -> Stream<T, L, B, O, R> {
3288 Stream::new(
3289 self.location.tick.l.clone(),
3290 HydroNode::EndAtomic {
3291 inner: Box::new(self.ir_node.replace(HydroNode::Placeholder)),
3292 metadata: self
3293 .location
3294 .tick
3295 .l
3296 .new_node_metadata(Stream::<T, L, B, O, R>::collection_kind()),
3297 },
3298 )
3299 }
3300}
3301
3302impl<'a, F, T, L, B: Boundedness, O: Ordering, R: Retries> Stream<F, L, B, O, R>
3303where
3304 L: TopLevel<'a>,
3305 F: Future<Output = T>,
3306{
3307 pub fn resolve_futures(self) -> Stream<T, L, Unbounded, NoOrder, R> {
3338 Stream::new(
3339 self.location.clone(),
3340 HydroNode::ResolveFutures {
3341 input: Box::new(self.ir_node.replace(HydroNode::Placeholder)),
3342 metadata: self
3343 .location
3344 .new_node_metadata(Stream::<T, L, Unbounded, NoOrder, R>::collection_kind()),
3345 },
3346 )
3347 }
3348
3349 pub fn resolve_futures_ordered(self) -> Stream<T, L, Unbounded, O, R> {
3380 Stream::new(
3381 self.location.clone(),
3382 HydroNode::ResolveFuturesOrdered {
3383 input: Box::new(self.ir_node.replace(HydroNode::Placeholder)),
3384 metadata: self
3385 .location
3386 .new_node_metadata(Stream::<T, L, Unbounded, O, R>::collection_kind()),
3387 },
3388 )
3389 }
3390}
3391
3392impl<'a, T, L, O: Ordering, R: Retries> Stream<T, Tick<L>, Bounded, O, R>
3393where
3394 L: Location<'a>,
3395{
3396 pub fn all_ticks(self) -> Stream<T, L, Unbounded, O, R> {
3399 Stream::new(
3400 self.location.parent_location().clone(),
3401 HydroNode::YieldConcat {
3402 inner: Box::new(self.ir_node.replace(HydroNode::Placeholder)),
3403 metadata: self.location.parent_location().new_node_metadata(Stream::<
3404 T,
3405 L,
3406 Unbounded,
3407 O,
3408 R,
3409 >::collection_kind(
3410 )),
3411 },
3412 )
3413 }
3414
3415 pub fn all_ticks_atomic(self) -> Stream<T, Atomic<L>, Unbounded, O, R> {
3422 let out_location = Atomic {
3423 tick: self.location.clone(),
3424 };
3425
3426 Stream::new(
3427 out_location.clone(),
3428 HydroNode::YieldConcat {
3429 inner: Box::new(self.ir_node.replace(HydroNode::Placeholder)),
3430 metadata: out_location
3431 .new_node_metadata(Stream::<T, Atomic<L>, Unbounded, O, R>::collection_kind()),
3432 },
3433 )
3434 }
3435
3436 pub fn across_ticks<Out: BatchAtomic<'a>>(
3471 self,
3472 thunk: impl FnOnce(Stream<T, Atomic<L>, Unbounded, O, R>) -> Out,
3473 ) -> Out::Batched {
3474 thunk(self.all_ticks_atomic()).batched_atomic()
3475 }
3476
3477 pub fn defer_tick(self) -> Stream<T, Tick<L>, Bounded, O, R> {
3516 Stream::new(
3517 self.location.clone(),
3518 HydroNode::DeferTick {
3519 input: Box::new(self.ir_node.replace(HydroNode::Placeholder)),
3520 metadata: self
3521 .location
3522 .new_node_metadata(Stream::<T, Tick<L>, Bounded, O, R>::collection_kind()),
3523 },
3524 )
3525 }
3526}
3527
3528#[cfg(test)]
3529mod tests {
3530 #[cfg(feature = "deploy")]
3531 use futures::{SinkExt, StreamExt};
3532 #[cfg(feature = "deploy")]
3533 use hydro_deploy::Deployment;
3534 #[cfg(feature = "deploy")]
3535 use serde::{Deserialize, Serialize};
3536 #[cfg(any(feature = "deploy", feature = "sim"))]
3537 use stageleft::q;
3538
3539 #[cfg(any(feature = "deploy", feature = "sim"))]
3540 use crate::compile::builder::FlowBuilder;
3541 #[cfg(feature = "deploy")]
3542 use crate::live_collections::sliced::sliced;
3543 #[cfg(feature = "deploy")]
3544 use crate::live_collections::stream::ExactlyOnce;
3545 #[cfg(feature = "sim")]
3546 use crate::live_collections::stream::NoOrder;
3547 #[cfg(any(feature = "deploy", feature = "sim"))]
3548 use crate::live_collections::stream::TotalOrder;
3549 #[cfg(any(feature = "deploy", feature = "sim"))]
3550 use crate::location::Location;
3551 #[cfg(feature = "sim")]
3552 use crate::networking::TCP;
3553 #[cfg(any(feature = "deploy", feature = "sim"))]
3554 use crate::nondet::nondet;
3555
3556 mod backtrace_chained_ops;
3557
3558 #[cfg(feature = "deploy")]
3559 struct P1 {}
3560 #[cfg(feature = "deploy")]
3561 struct P2 {}
3562
3563 #[cfg(feature = "deploy")]
3564 #[derive(Serialize, Deserialize, Debug)]
3565 struct SendOverNetwork {
3566 n: u32,
3567 }
3568
3569 #[cfg(feature = "deploy")]
3570 #[tokio::test]
3571 async fn first_ten_distributed() {
3572 use crate::networking::TCP;
3573
3574 let mut deployment = Deployment::new();
3575
3576 let mut flow = FlowBuilder::new();
3577 let first_node = flow.process::<P1>();
3578 let second_node = flow.process::<P2>();
3579 let external = flow.external::<P2>();
3580
3581 let numbers = first_node.source_iter(q!(0..10));
3582 let out_port = numbers
3583 .map(q!(|n| SendOverNetwork { n }))
3584 .send(&second_node, TCP.fail_stop().bincode())
3585 .send_bincode_external(&external);
3586
3587 let nodes = flow
3588 .with_process(&first_node, deployment.Localhost())
3589 .with_process(&second_node, deployment.Localhost())
3590 .with_external(&external, deployment.Localhost())
3591 .deploy(&mut deployment);
3592
3593 deployment.deploy().await.unwrap();
3594
3595 let mut external_out = nodes.connect(out_port).await;
3596
3597 deployment.start().await.unwrap();
3598
3599 for i in 0..10 {
3600 assert_eq!(external_out.next().await.unwrap().n, i);
3601 }
3602 }
3603
3604 #[cfg(feature = "deploy")]
3605 #[tokio::test]
3606 async fn first_cardinality() {
3607 let mut deployment = Deployment::new();
3608
3609 let mut flow = FlowBuilder::new();
3610 let node = flow.process::<()>();
3611 let external = flow.external::<()>();
3612
3613 let node_tick = node.tick();
3614 let count = node_tick
3615 .singleton(q!([1, 2, 3]))
3616 .into_stream()
3617 .flatten_ordered()
3618 .first()
3619 .into_stream()
3620 .count()
3621 .all_ticks()
3622 .send_bincode_external(&external);
3623
3624 let nodes = flow
3625 .with_process(&node, deployment.Localhost())
3626 .with_external(&external, deployment.Localhost())
3627 .deploy(&mut deployment);
3628
3629 deployment.deploy().await.unwrap();
3630
3631 let mut external_out = nodes.connect(count).await;
3632
3633 deployment.start().await.unwrap();
3634
3635 assert_eq!(external_out.next().await.unwrap(), 1);
3636 }
3637
3638 #[cfg(feature = "deploy")]
3639 #[tokio::test]
3640 async fn unbounded_reduce_remembers_state() {
3641 let mut deployment = Deployment::new();
3642
3643 let mut flow = FlowBuilder::new();
3644 let node = flow.process::<()>();
3645 let external = flow.external::<()>();
3646
3647 let (input_port, input) = node.source_external_bincode(&external);
3648 let out = input
3649 .reduce(q!(|acc, v| *acc += v))
3650 .sample_eager(nondet!())
3651 .send_bincode_external(&external);
3652
3653 let nodes = flow
3654 .with_process(&node, deployment.Localhost())
3655 .with_external(&external, deployment.Localhost())
3656 .deploy(&mut deployment);
3657
3658 deployment.deploy().await.unwrap();
3659
3660 let mut external_in = nodes.connect(input_port).await;
3661 let mut external_out = nodes.connect(out).await;
3662
3663 deployment.start().await.unwrap();
3664
3665 external_in.send(1).await.unwrap();
3666 assert_eq!(external_out.next().await.unwrap(), 1);
3667
3668 external_in.send(2).await.unwrap();
3669 assert_eq!(external_out.next().await.unwrap(), 3);
3670 }
3671
3672 #[cfg(feature = "deploy")]
3673 #[tokio::test]
3674 async fn top_level_bounded_cross_singleton() {
3675 let mut deployment = Deployment::new();
3676
3677 let mut flow = FlowBuilder::new();
3678 let node = flow.process::<()>();
3679 let external = flow.external::<()>();
3680
3681 let (input_port, input) =
3682 node.source_external_bincode::<_, _, TotalOrder, ExactlyOnce>(&external);
3683
3684 let out = input
3685 .cross_singleton(
3686 node.source_iter(q!(vec![1, 2, 3]))
3687 .fold(q!(|| 0), q!(|acc, v| *acc += v)),
3688 )
3689 .send_bincode_external(&external);
3690
3691 let nodes = flow
3692 .with_process(&node, deployment.Localhost())
3693 .with_external(&external, deployment.Localhost())
3694 .deploy(&mut deployment);
3695
3696 deployment.deploy().await.unwrap();
3697
3698 let mut external_in = nodes.connect(input_port).await;
3699 let mut external_out = nodes.connect(out).await;
3700
3701 deployment.start().await.unwrap();
3702
3703 external_in.send(1).await.unwrap();
3704 assert_eq!(external_out.next().await.unwrap(), (1, 6));
3705
3706 external_in.send(2).await.unwrap();
3707 assert_eq!(external_out.next().await.unwrap(), (2, 6));
3708 }
3709
3710 #[cfg(feature = "deploy")]
3711 #[tokio::test]
3712 async fn top_level_bounded_reduce_cardinality() {
3713 let mut deployment = Deployment::new();
3714
3715 let mut flow = FlowBuilder::new();
3716 let node = flow.process::<()>();
3717 let external = flow.external::<()>();
3718
3719 let (input_port, input) =
3720 node.source_external_bincode::<_, _, TotalOrder, ExactlyOnce>(&external);
3721
3722 let out = sliced! {
3723 let input = use::batch(input, nondet!());
3724 let v = use::snapshot(node.source_iter(q!(vec![1, 2, 3])).reduce(q!(|acc, v| *acc += v)), nondet!());
3725 input.cross_singleton(v.into_stream().count())
3726 }
3727 .send_bincode_external(&external);
3728
3729 let nodes = flow
3730 .with_process(&node, deployment.Localhost())
3731 .with_external(&external, deployment.Localhost())
3732 .deploy(&mut deployment);
3733
3734 deployment.deploy().await.unwrap();
3735
3736 let mut external_in = nodes.connect(input_port).await;
3737 let mut external_out = nodes.connect(out).await;
3738
3739 deployment.start().await.unwrap();
3740
3741 external_in.send(1).await.unwrap();
3742 assert_eq!(external_out.next().await.unwrap(), (1, 1));
3743
3744 external_in.send(2).await.unwrap();
3745 assert_eq!(external_out.next().await.unwrap(), (2, 1));
3746 }
3747
3748 #[cfg(feature = "deploy")]
3749 #[tokio::test]
3750 async fn top_level_bounded_into_singleton_cardinality() {
3751 let mut deployment = Deployment::new();
3752
3753 let mut flow = FlowBuilder::new();
3754 let node = flow.process::<()>();
3755 let external = flow.external::<()>();
3756
3757 let (input_port, input) =
3758 node.source_external_bincode::<_, _, TotalOrder, ExactlyOnce>(&external);
3759
3760 let out = sliced! {
3761 let input = use::batch(input, nondet!());
3762 let v = use::snapshot(node.source_iter(q!(vec![1, 2, 3])).reduce(q!(|acc, v| *acc += v)).into_singleton(), nondet!());
3763 input.cross_singleton(v.into_stream().count())
3764 }
3765 .send_bincode_external(&external);
3766
3767 let nodes = flow
3768 .with_process(&node, deployment.Localhost())
3769 .with_external(&external, deployment.Localhost())
3770 .deploy(&mut deployment);
3771
3772 deployment.deploy().await.unwrap();
3773
3774 let mut external_in = nodes.connect(input_port).await;
3775 let mut external_out = nodes.connect(out).await;
3776
3777 deployment.start().await.unwrap();
3778
3779 external_in.send(1).await.unwrap();
3780 assert_eq!(external_out.next().await.unwrap(), (1, 1));
3781
3782 external_in.send(2).await.unwrap();
3783 assert_eq!(external_out.next().await.unwrap(), (2, 1));
3784 }
3785
3786 #[cfg(feature = "deploy")]
3787 #[tokio::test]
3788 async fn atomic_fold_replays_each_tick() {
3789 let mut deployment = Deployment::new();
3790
3791 let mut flow = FlowBuilder::new();
3792 let node = flow.process::<()>();
3793 let external = flow.external::<()>();
3794
3795 let (input_port, input) =
3796 node.source_external_bincode::<_, _, TotalOrder, ExactlyOnce>(&external);
3797 let tick = node.tick();
3798
3799 let out = input
3800 .batch(&tick, nondet!())
3801 .cross_singleton(
3802 node.source_iter(q!(vec![1, 2, 3]))
3803 .atomic()
3804 .fold(q!(|| 0), q!(|acc, v| *acc += v))
3805 .snapshot_atomic(&tick, nondet!()),
3806 )
3807 .all_ticks()
3808 .send_bincode_external(&external);
3809
3810 let nodes = flow
3811 .with_process(&node, deployment.Localhost())
3812 .with_external(&external, deployment.Localhost())
3813 .deploy(&mut deployment);
3814
3815 deployment.deploy().await.unwrap();
3816
3817 let mut external_in = nodes.connect(input_port).await;
3818 let mut external_out = nodes.connect(out).await;
3819
3820 deployment.start().await.unwrap();
3821
3822 external_in.send(1).await.unwrap();
3823 assert_eq!(external_out.next().await.unwrap(), (1, 6));
3824
3825 external_in.send(2).await.unwrap();
3826 assert_eq!(external_out.next().await.unwrap(), (2, 6));
3827 }
3828
3829 #[cfg(feature = "deploy")]
3830 #[tokio::test]
3831 async fn unbounded_scan_remembers_state() {
3832 let mut deployment = Deployment::new();
3833
3834 let mut flow = FlowBuilder::new();
3835 let node = flow.process::<()>();
3836 let external = flow.external::<()>();
3837
3838 let (input_port, input) = node.source_external_bincode(&external);
3839 let out = input
3840 .scan(
3841 q!(|| 0),
3842 q!(|acc, v| {
3843 *acc += v;
3844 Some(*acc)
3845 }),
3846 )
3847 .send_bincode_external(&external);
3848
3849 let nodes = flow
3850 .with_process(&node, deployment.Localhost())
3851 .with_external(&external, deployment.Localhost())
3852 .deploy(&mut deployment);
3853
3854 deployment.deploy().await.unwrap();
3855
3856 let mut external_in = nodes.connect(input_port).await;
3857 let mut external_out = nodes.connect(out).await;
3858
3859 deployment.start().await.unwrap();
3860
3861 external_in.send(1).await.unwrap();
3862 assert_eq!(external_out.next().await.unwrap(), 1);
3863
3864 external_in.send(2).await.unwrap();
3865 assert_eq!(external_out.next().await.unwrap(), 3);
3866 }
3867
3868 #[cfg(feature = "deploy")]
3869 #[tokio::test]
3870 async fn unbounded_enumerate_remembers_state() {
3871 let mut deployment = Deployment::new();
3872
3873 let mut flow = FlowBuilder::new();
3874 let node = flow.process::<()>();
3875 let external = flow.external::<()>();
3876
3877 let (input_port, input) = node.source_external_bincode(&external);
3878 let out = input.enumerate().send_bincode_external(&external);
3879
3880 let nodes = flow
3881 .with_process(&node, deployment.Localhost())
3882 .with_external(&external, deployment.Localhost())
3883 .deploy(&mut deployment);
3884
3885 deployment.deploy().await.unwrap();
3886
3887 let mut external_in = nodes.connect(input_port).await;
3888 let mut external_out = nodes.connect(out).await;
3889
3890 deployment.start().await.unwrap();
3891
3892 external_in.send(1).await.unwrap();
3893 assert_eq!(external_out.next().await.unwrap(), (0, 1));
3894
3895 external_in.send(2).await.unwrap();
3896 assert_eq!(external_out.next().await.unwrap(), (1, 2));
3897 }
3898
3899 #[cfg(feature = "deploy")]
3900 #[tokio::test]
3901 async fn unbounded_unique_remembers_state() {
3902 let mut deployment = Deployment::new();
3903
3904 let mut flow = FlowBuilder::new();
3905 let node = flow.process::<()>();
3906 let external = flow.external::<()>();
3907
3908 let (input_port, input) =
3909 node.source_external_bincode::<_, _, TotalOrder, ExactlyOnce>(&external);
3910 let out = input.unique().send_bincode_external(&external);
3911
3912 let nodes = flow
3913 .with_process(&node, deployment.Localhost())
3914 .with_external(&external, deployment.Localhost())
3915 .deploy(&mut deployment);
3916
3917 deployment.deploy().await.unwrap();
3918
3919 let mut external_in = nodes.connect(input_port).await;
3920 let mut external_out = nodes.connect(out).await;
3921
3922 deployment.start().await.unwrap();
3923
3924 external_in.send(1).await.unwrap();
3925 assert_eq!(external_out.next().await.unwrap(), 1);
3926
3927 external_in.send(2).await.unwrap();
3928 assert_eq!(external_out.next().await.unwrap(), 2);
3929
3930 external_in.send(1).await.unwrap();
3931 external_in.send(3).await.unwrap();
3932 assert_eq!(external_out.next().await.unwrap(), 3);
3933 }
3934
3935 #[cfg(feature = "sim")]
3936 #[test]
3937 #[should_panic]
3938 fn sim_batch_nondet_size() {
3939 let mut flow = FlowBuilder::new();
3940 let node = flow.process::<()>();
3941
3942 let (in_send, input) = node.sim_input::<_, TotalOrder, _>();
3943
3944 let tick = node.tick();
3945 let out_recv = input
3946 .batch(&tick, nondet!())
3947 .count()
3948 .all_ticks()
3949 .sim_output();
3950
3951 flow.sim().exhaustive(async || {
3952 in_send.send(());
3953 in_send.send(());
3954 in_send.send(());
3955
3956 assert_eq!(out_recv.next().await, 3); });
3958 }
3959
3960 #[cfg(feature = "sim")]
3961 #[test]
3962 fn sim_batch_preserves_order() {
3963 let mut flow = FlowBuilder::new();
3964 let node = flow.process::<()>();
3965
3966 let (in_send, input) = node.sim_input();
3967
3968 let tick = node.tick();
3969 let out_recv = input
3970 .batch(&tick, nondet!())
3971 .all_ticks()
3972 .sim_output();
3973
3974 flow.sim().exhaustive(async || {
3975 in_send.send(1);
3976 in_send.send(2);
3977 in_send.send(3);
3978
3979 out_recv.assert_yields_only([1, 2, 3]).await;
3980 });
3981 }
3982
3983 #[cfg(feature = "sim")]
3984 #[test]
3985 #[should_panic]
3986 fn sim_batch_unordered_shuffles() {
3987 let mut flow = FlowBuilder::new();
3988 let node = flow.process::<()>();
3989
3990 let (in_send, input) = node.sim_input::<_, NoOrder, _>();
3991
3992 let tick = node.tick();
3993 let batch = input.batch(&tick, nondet!());
3994 let out_recv = batch
3995 .clone()
3996 .min()
3997 .zip(batch.max())
3998 .all_ticks()
3999 .sim_output();
4000
4001 flow.sim().exhaustive(async || {
4002 in_send.send_many_unordered([1, 2, 3]);
4003
4004 assert!(
4005 out_recv.collect::<Vec<_>>().await != vec![(1, 3), (2, 2)],
4006 "saw both (1, 3) and (2, 2), so batching must have shuffled the order"
4007 )
4008 });
4009 }
4010
4011 #[cfg(feature = "sim")]
4012 #[test]
4013 fn sim_batch_unordered_shuffles_count() {
4014 let mut flow = FlowBuilder::new();
4015 let node = flow.process::<()>();
4016
4017 let (in_send, input) = node.sim_input::<_, NoOrder, _>();
4018
4019 let tick = node.tick();
4020 let batch = input.batch(&tick, nondet!());
4021 let out_recv = batch.all_ticks().sim_output();
4022
4023 let instance_count = flow.sim().exhaustive(async || {
4024 in_send.send_many_unordered([1, 2, 3, 4]);
4025 out_recv.assert_yields_only_unordered([1, 2, 3, 4]).await;
4026 });
4027
4028 assert_eq!(
4029 instance_count,
4030 75 )
4032 }
4033
4034 #[cfg(feature = "sim")]
4035 #[test]
4036 #[should_panic]
4037 fn sim_observe_order_batched() {
4038 let mut flow = FlowBuilder::new();
4039 let node = flow.process::<()>();
4040
4041 let (in_send, input) = node.sim_input::<_, NoOrder, _>();
4042
4043 let tick = node.tick();
4044 let batch = input.batch(&tick, nondet!());
4045 let out_recv = batch
4046 .assume_ordering::<TotalOrder>(nondet!())
4047 .all_ticks()
4048 .sim_output();
4049
4050 flow.sim().exhaustive(async || {
4051 in_send.send_many_unordered([1, 2, 3, 4]);
4052 out_recv.assert_yields_only([1, 2, 3, 4]).await; });
4054 }
4055
4056 #[cfg(feature = "sim")]
4057 #[test]
4058 fn sim_observe_order_batched_count() {
4059 let mut flow = FlowBuilder::new();
4060 let node = flow.process::<()>();
4061
4062 let (in_send, input) = node.sim_input::<_, NoOrder, _>();
4063
4064 let tick = node.tick();
4065 let batch = input.batch(&tick, nondet!());
4066 let out_recv = batch
4067 .assume_ordering::<TotalOrder>(nondet!())
4068 .all_ticks()
4069 .sim_output();
4070
4071 let instance_count = flow.sim().exhaustive(async || {
4072 in_send.send_many_unordered([1, 2, 3, 4]);
4073 let _ = out_recv.collect::<Vec<_>>().await;
4074 });
4075
4076 assert_eq!(
4077 instance_count,
4078 192 )
4080 }
4081
4082 #[cfg(feature = "sim")]
4083 #[test]
4084 fn sim_unordered_count_instance_count() {
4085 let mut flow = FlowBuilder::new();
4086 let node = flow.process::<()>();
4087
4088 let (in_send, input) = node.sim_input::<_, NoOrder, _>();
4089
4090 let tick = node.tick();
4091 let out_recv = input
4092 .count()
4093 .snapshot(&tick, nondet!())
4094 .all_ticks()
4095 .sim_output();
4096
4097 let instance_count = flow.sim().exhaustive(async || {
4098 in_send.send_many_unordered([1, 2, 3, 4]);
4099 assert_eq!(out_recv.collect::<Vec<_>>().await.last(), Some(&4));
4100 });
4101
4102 assert_eq!(
4103 instance_count,
4104 16 )
4106 }
4107
4108 #[cfg(feature = "sim")]
4109 #[test]
4110 fn sim_top_level_assume_ordering() {
4111 let mut flow = FlowBuilder::new();
4112 let node = flow.process::<()>();
4113
4114 let (in_send, input) = node.sim_input::<_, NoOrder, _>();
4115
4116 let out_recv = input
4117 .assume_ordering::<TotalOrder>(nondet!())
4118 .sim_output();
4119
4120 let instance_count = flow.sim().exhaustive(async || {
4121 in_send.send_many_unordered([1, 2, 3]);
4122 let mut out = out_recv.collect::<Vec<_>>().await;
4123 out.sort();
4124 assert_eq!(out, vec![1, 2, 3]);
4125 });
4126
4127 assert_eq!(instance_count, 6)
4128 }
4129
4130 #[cfg(feature = "sim")]
4131 #[test]
4132 fn sim_top_level_assume_ordering_cycle_back() {
4133 let mut flow = FlowBuilder::new();
4134 let node = flow.process::<()>();
4135 let node2 = flow.process::<()>();
4136
4137 let (in_send, input) = node.sim_input::<_, NoOrder, _>();
4138
4139 let (complete_cycle_back, cycle_back) =
4140 node.forward_ref::<super::Stream<_, _, _, NoOrder>>();
4141 let ordered = input
4142 .merge_unordered(cycle_back)
4143 .assume_ordering::<TotalOrder>(nondet!());
4144 complete_cycle_back.complete(
4145 ordered
4146 .clone()
4147 .map(q!(|v| v + 1))
4148 .filter(q!(|v| v % 2 == 1))
4149 .send(&node2, TCP.fail_stop().bincode())
4150 .send(&node, TCP.fail_stop().bincode()),
4151 );
4152
4153 let out_recv = ordered.sim_output();
4154
4155 let mut saw = false;
4156 let instance_count = flow.sim().exhaustive(async || {
4157 in_send.send_many_unordered([0, 2]);
4158 let out = out_recv.collect::<Vec<_>>().await;
4159
4160 if out.starts_with(&[0, 1, 2]) {
4161 saw = true;
4162 }
4163 });
4164
4165 assert!(saw, "did not see an instance with 0, 1, 2 in order");
4166 assert_eq!(instance_count, 6);
4167 }
4168
4169 #[cfg(feature = "sim")]
4170 #[test]
4171 fn sim_top_level_assume_ordering_cycle_back_tick() {
4172 let mut flow = FlowBuilder::new();
4173 let node = flow.process::<()>();
4174 let node2 = flow.process::<()>();
4175
4176 let (in_send, input) = node.sim_input::<_, NoOrder, _>();
4177
4178 let (complete_cycle_back, cycle_back) =
4179 node.forward_ref::<super::Stream<_, _, _, NoOrder>>();
4180 let ordered = input
4181 .merge_unordered(cycle_back)
4182 .assume_ordering::<TotalOrder>(nondet!());
4183 complete_cycle_back.complete(
4184 ordered
4185 .clone()
4186 .batch(&node.tick(), nondet!())
4187 .all_ticks()
4188 .map(q!(|v| v + 1))
4189 .filter(q!(|v| v % 2 == 1))
4190 .send(&node2, TCP.fail_stop().bincode())
4191 .send(&node, TCP.fail_stop().bincode()),
4192 );
4193
4194 let out_recv = ordered.sim_output();
4195
4196 let mut saw = false;
4197 let instance_count = flow.sim().exhaustive(async || {
4198 in_send.send_many_unordered([0, 2]);
4199 let out = out_recv.collect::<Vec<_>>().await;
4200
4201 if out.starts_with(&[0, 1, 2]) {
4202 saw = true;
4203 }
4204 });
4205
4206 assert!(saw, "did not see an instance with 0, 1, 2 in order");
4207 assert_eq!(instance_count, 58);
4208 }
4209
4210 #[cfg(feature = "sim")]
4211 #[test]
4212 fn sim_top_level_assume_ordering_multiple() {
4213 let mut flow = FlowBuilder::new();
4214 let node = flow.process::<()>();
4215 let node2 = flow.process::<()>();
4216
4217 let (in_send, input) = node.sim_input::<_, NoOrder, _>();
4218 let (_, input2) = node.sim_input::<_, NoOrder, _>();
4219
4220 let (complete_cycle_back, cycle_back) =
4221 node.forward_ref::<super::Stream<_, _, _, NoOrder>>();
4222 let input1_ordered = input
4223 .clone()
4224 .merge_unordered(cycle_back)
4225 .assume_ordering::<TotalOrder>(nondet!());
4226 let foo = input1_ordered
4227 .clone()
4228 .map(q!(|v| v + 3))
4229 .weaken_ordering::<NoOrder>()
4230 .merge_unordered(input2)
4231 .assume_ordering::<TotalOrder>(nondet!());
4232
4233 complete_cycle_back.complete(
4234 foo.filter(q!(|v| *v == 3))
4235 .send(&node2, TCP.fail_stop().bincode())
4236 .send(&node, TCP.fail_stop().bincode()),
4237 );
4238
4239 let out_recv = input1_ordered.sim_output();
4240
4241 let mut saw = false;
4242 let instance_count = flow.sim().exhaustive(async || {
4243 in_send.send_many_unordered([0, 1]);
4244 let out = out_recv.collect::<Vec<_>>().await;
4245
4246 if out.starts_with(&[0, 3, 1]) {
4247 saw = true;
4248 }
4249 });
4250
4251 assert!(saw, "did not see an instance with 0, 3, 1 in order");
4252 assert_eq!(instance_count, 15);
4253 }
4254
4255 #[cfg(feature = "sim")]
4256 #[test]
4257 fn sim_atomic_assume_ordering_cycle_back() {
4258 let mut flow = FlowBuilder::new();
4259 let node = flow.process::<()>();
4260 let node2 = flow.process::<()>();
4261
4262 let (in_send, input) = node.sim_input::<_, NoOrder, _>();
4263
4264 let (complete_cycle_back, cycle_back) =
4265 node.forward_ref::<super::Stream<_, _, _, NoOrder>>();
4266 let ordered = input
4267 .merge_unordered(cycle_back)
4268 .atomic()
4269 .assume_ordering::<TotalOrder>(nondet!())
4270 .end_atomic();
4271 complete_cycle_back.complete(
4272 ordered
4273 .clone()
4274 .map(q!(|v| v + 1))
4275 .filter(q!(|v| v % 2 == 1))
4276 .send(&node2, TCP.fail_stop().bincode())
4277 .send(&node, TCP.fail_stop().bincode()),
4278 );
4279
4280 let out_recv = ordered.sim_output();
4281
4282 let instance_count = flow.sim().exhaustive(async || {
4283 in_send.send_many_unordered([0, 2]);
4284 let out = out_recv.collect::<Vec<_>>().await;
4285 assert_eq!(out.len(), 4);
4286 });
4287 assert_eq!(instance_count, 22);
4288 }
4289
4290 #[cfg(feature = "deploy")]
4291 #[tokio::test]
4292 async fn partition_evens_odds() {
4293 let mut deployment = Deployment::new();
4294
4295 let mut flow = FlowBuilder::new();
4296 let node = flow.process::<()>();
4297 let external = flow.external::<()>();
4298
4299 let numbers = node.source_iter(q!(vec![1i32, 2, 3, 4, 5, 6]));
4300 let (evens, odds) = numbers.partition(q!(|x: &i32| x % 2 == 0));
4301 let evens_port = evens.send_bincode_external(&external);
4302 let odds_port = odds.send_bincode_external(&external);
4303
4304 let nodes = flow
4305 .with_process(&node, deployment.Localhost())
4306 .with_external(&external, deployment.Localhost())
4307 .deploy(&mut deployment);
4308
4309 deployment.deploy().await.unwrap();
4310
4311 let mut evens_out = nodes.connect(evens_port).await;
4312 let mut odds_out = nodes.connect(odds_port).await;
4313
4314 deployment.start().await.unwrap();
4315
4316 let mut even_results = Vec::new();
4317 for _ in 0..3 {
4318 even_results.push(evens_out.next().await.unwrap());
4319 }
4320 even_results.sort();
4321 assert_eq!(even_results, vec![2, 4, 6]);
4322
4323 let mut odd_results = Vec::new();
4324 for _ in 0..3 {
4325 odd_results.push(odds_out.next().await.unwrap());
4326 }
4327 odd_results.sort();
4328 assert_eq!(odd_results, vec![1, 3, 5]);
4329 }
4330
4331 #[cfg(feature = "deploy")]
4332 #[tokio::test]
4333 async fn unconsumed_inspect_still_runs() {
4334 use crate::deploy::DeployCrateWrapper;
4335
4336 let mut deployment = Deployment::new();
4337
4338 let mut flow = FlowBuilder::new();
4339 let node = flow.process::<()>();
4340
4341 node.source_iter(q!(0..5))
4344 .inspect(q!(|x| println!("inspect: {}", x)));
4345
4346 let nodes = flow
4347 .with_process(&node, deployment.Localhost())
4348 .deploy(&mut deployment);
4349
4350 deployment.deploy().await.unwrap();
4351
4352 let mut stdout = nodes.get_process(&node).stdout();
4353
4354 deployment.start().await.unwrap();
4355
4356 let mut lines = Vec::new();
4357 for _ in 0..5 {
4358 lines.push(stdout.recv().await.unwrap());
4359 }
4360 lines.sort();
4361 assert_eq!(
4362 lines,
4363 vec![
4364 "inspect: 0",
4365 "inspect: 1",
4366 "inspect: 2",
4367 "inspect: 3",
4368 "inspect: 4",
4369 ]
4370 );
4371 }
4372
4373 #[cfg(feature = "deploy")]
4374 #[tokio::test]
4375 async fn unconsumed_inspect_alive_at_deploy_still_runs() {
4376 use crate::deploy::DeployCrateWrapper;
4377
4378 let mut deployment = Deployment::new();
4379
4380 let mut flow = FlowBuilder::new();
4381 let node = flow.process::<()>();
4382
4383 let _inspected = node
4388 .source_iter(q!(0..5))
4389 .inspect(q!(|x| println!("inspect: {}", x)));
4390
4391 let nodes = flow
4392 .with_process(&node, deployment.Localhost())
4393 .deploy(&mut deployment);
4394
4395 deployment.deploy().await.unwrap();
4396
4397 let mut stdout = nodes.get_process(&node).stdout();
4398
4399 deployment.start().await.unwrap();
4400
4401 let mut lines = Vec::new();
4402 for _ in 0..5 {
4403 lines.push(stdout.recv().await.unwrap());
4404 }
4405 lines.sort();
4406 assert_eq!(
4407 lines,
4408 vec![
4409 "inspect: 0",
4410 "inspect: 1",
4411 "inspect: 2",
4412 "inspect: 3",
4413 "inspect: 4",
4414 ]
4415 );
4416 }
4417
4418 #[cfg(feature = "sim")]
4419 #[test]
4420 fn sim_limit() {
4421 let mut flow = FlowBuilder::new();
4422 let node = flow.process::<()>();
4423
4424 let (in_send, input) = node.sim_input();
4425
4426 let out_recv = input.limit(q!(3)).sim_output();
4427
4428 flow.sim().exhaustive(async || {
4429 in_send.send(1);
4430 in_send.send(2);
4431 in_send.send(3);
4432 in_send.send(4);
4433 in_send.send(5);
4434
4435 out_recv.assert_yields_only([1, 2, 3]).await;
4436 });
4437 }
4438
4439 #[cfg(feature = "sim")]
4440 #[test]
4441 fn sim_limit_zero() {
4442 let mut flow = FlowBuilder::new();
4443 let node = flow.process::<()>();
4444
4445 let (in_send, input) = node.sim_input();
4446
4447 let out_recv = input.limit(q!(0)).sim_output();
4448
4449 flow.sim().exhaustive(async || {
4450 in_send.send(1);
4451 in_send.send(2);
4452
4453 out_recv.assert_yields_only::<i32, _>([]).await;
4454 });
4455 }
4456
4457 #[cfg(feature = "sim")]
4458 #[test]
4459 fn sim_merge_ordered() {
4460 let mut flow = FlowBuilder::new();
4461 let node = flow.process::<()>();
4462
4463 let (in_send, input) = node.sim_input();
4464 let (in_send2, input2) = node.sim_input();
4465
4466 let out_recv = input
4467 .merge_ordered(input2, nondet!())
4468 .sim_output();
4469
4470 let mut saw_out_of_order = false;
4471 let instances = flow.sim().exhaustive(async || {
4472 in_send.send(1);
4473 in_send.send(2);
4474 in_send2.send(3);
4475 in_send2.send(4);
4476
4477 let out = out_recv.collect::<Vec<_>>().await;
4478
4479 if out == [1, 3, 2, 4] {
4480 saw_out_of_order = true;
4481 }
4482
4483 let mut first_elements = out.iter().filter(|v| **v <= 2).copied().collect::<Vec<_>>();
4486 let mut second_elements = out.iter().filter(|v| **v > 2).copied().collect::<Vec<_>>();
4487 assert_eq!(
4488 first_elements,
4489 vec![1, 2],
4490 "first input order violated: {:?}",
4491 out
4492 );
4493 assert_eq!(
4494 second_elements,
4495 vec![3, 4],
4496 "second input order violated: {:?}",
4497 out
4498 );
4499
4500 first_elements.append(&mut second_elements);
4501 first_elements.sort();
4502 assert_eq!(first_elements, vec![1, 2, 3, 4]);
4503 });
4504
4505 assert!(saw_out_of_order);
4506 assert_eq!(instances, 6);
4507 }
4508
4509 #[cfg(feature = "sim")]
4512 #[test]
4513 fn sim_merge_ordered_one_empty() {
4514 let mut flow = FlowBuilder::new();
4515 let node = flow.process::<()>();
4516
4517 let (in_send, input) = node.sim_input();
4518 let (_in_send2, input2) = node.sim_input();
4519
4520 let out_recv = input
4521 .merge_ordered(input2, nondet!())
4522 .sim_output();
4523
4524 let instances = flow.sim().exhaustive(async || {
4525 in_send.send(1);
4526 in_send.send(2);
4527
4528 let out = out_recv.collect::<Vec<_>>().await;
4529 assert_eq!(out, vec![1, 2]);
4530 });
4531
4532 assert_eq!(instances, 1);
4534 }
4535
4536 #[cfg(feature = "sim")]
4542 #[test]
4543 fn sim_merge_ordered_cycle_back() {
4544 let mut flow = FlowBuilder::new();
4545 let node = flow.process::<()>();
4546
4547 let (in_send, input) = node.sim_input();
4548
4549 let (complete_cycle_back, cycle_back) =
4551 node.forward_ref::<super::Stream<_, _, _, TotalOrder>>();
4552
4553 let merged = input.merge_ordered(cycle_back, nondet!());
4555
4556 complete_cycle_back.complete(merged.clone().filter(q!(|v| *v == 1)).map(q!(|v| v * 10)));
4558
4559 let out_recv = merged.sim_output();
4560
4561 let mut saw_cycle_before_second = false;
4564 flow.sim().exhaustive(async || {
4565 in_send.send(1);
4566 in_send.send(2);
4567
4568 let out = out_recv.collect::<Vec<_>>().await;
4569
4570 let pos_1 = out.iter().position(|v| *v == 1).unwrap();
4572 let pos_10 = out.iter().position(|v| *v == 10).unwrap();
4573 assert!(pos_1 < pos_10, "causal order violated: {:?}", out);
4574
4575 if out == [1, 10, 2] {
4577 saw_cycle_before_second = true;
4578 }
4579
4580 let mut sorted = out;
4581 sorted.sort();
4582 assert_eq!(sorted, vec![1, 2, 10]);
4583 });
4584
4585 assert!(
4586 saw_cycle_before_second,
4587 "never saw the cycled element arrive before the second input element"
4588 );
4589 }
4590
4591 #[cfg(feature = "sim")]
4595 #[test]
4596 fn sim_merge_ordered_delayed() {
4597 let mut flow = FlowBuilder::new();
4598 let node = flow.process::<()>();
4599
4600 let (in_send, input) = node.sim_input();
4601 let (in_send2, input2) = node.sim_input();
4602
4603 let out_recv = input
4604 .merge_ordered(input2, nondet!())
4605 .sim_output();
4606
4607 let mut saw_delayed_interleaving = false;
4608 flow.sim().exhaustive(async || {
4609 in_send.send(1);
4611 in_send2.send(3);
4612 in_send2.send(4);
4613
4614 let first_batch = out_recv.collect::<Vec<_>>().await;
4616
4617 in_send.send(2);
4619 let second_batch = out_recv.collect::<Vec<_>>().await;
4620
4621 let mut all: Vec<_> = first_batch
4622 .iter()
4623 .chain(second_batch.iter())
4624 .copied()
4625 .collect();
4626
4627 if all == [1, 3, 4, 2] {
4629 saw_delayed_interleaving = true;
4630 }
4631
4632 all.sort();
4633 assert_eq!(all, vec![1, 2, 3, 4]);
4634 });
4635
4636 assert!(saw_delayed_interleaving);
4637 }
4638
4639 #[cfg(feature = "deploy")]
4644 #[tokio::test]
4645 async fn deploy_merge_ordered_delayed() {
4646 let mut deployment = Deployment::new();
4647
4648 let mut flow = FlowBuilder::new();
4649 let node = flow.process::<()>();
4650 let external = flow.external::<()>();
4651
4652 let (input_a_port, input_a) = node.source_external_bincode(&external);
4653 let (input_b_port, input_b) = node.source_external_bincode(&external);
4654
4655 let out = input_a
4656 .assume_ordering(nondet!())
4657 .merge_ordered(
4658 input_b.assume_ordering(nondet!()),
4659 nondet!(),
4660 )
4661 .send_bincode_external(&external);
4662
4663 let nodes = flow
4664 .with_process(&node, deployment.Localhost())
4665 .with_external(&external, deployment.Localhost())
4666 .deploy(&mut deployment);
4667
4668 deployment.deploy().await.unwrap();
4669
4670 let mut ext_a = nodes.connect(input_a_port).await;
4671 let mut ext_b = nodes.connect(input_b_port).await;
4672 let mut ext_out = nodes.connect(out).await;
4673
4674 deployment.start().await.unwrap();
4675
4676 ext_a.send(1).await.unwrap();
4678 ext_b.send(3).await.unwrap();
4679 ext_b.send(4).await.unwrap();
4680
4681 let mut received = Vec::new();
4683 for _ in 0..3 {
4684 received.push(ext_out.next().await.unwrap());
4685 }
4686
4687 ext_a.send(2).await.unwrap();
4689 received.push(ext_out.next().await.unwrap());
4690
4691 received.sort();
4693 assert_eq!(received, vec![1, 2, 3, 4]);
4694 }
4695
4696 #[cfg(feature = "deploy")]
4697 #[tokio::test]
4698 async fn monotone_fold_threshold() {
4699 use crate::properties::manual_proof;
4700
4701 let mut deployment = Deployment::new();
4702
4703 let mut flow = FlowBuilder::new();
4704 let node = flow.process::<()>();
4705 let external = flow.external::<()>();
4706
4707 let in_unbounded: super::Stream<_, _> =
4708 node.source_iter(q!(vec![1i32, 2, 3, 4, 5, 6])).into();
4709 let sum = in_unbounded.fold(
4710 q!(|| 0),
4711 q!(
4712 |sum, v| {
4713 *sum += v;
4714 },
4715 monotone = manual_proof!()
4716 ),
4717 );
4718
4719 let threshold_out = sum
4720 .threshold_greater_or_equal(node.singleton(q!(7)))
4721 .send_bincode_external(&external);
4722
4723 let nodes = flow
4724 .with_process(&node, deployment.Localhost())
4725 .with_external(&external, deployment.Localhost())
4726 .deploy(&mut deployment);
4727
4728 deployment.deploy().await.unwrap();
4729
4730 let mut threshold_out = nodes.connect(threshold_out).await;
4731
4732 deployment.start().await.unwrap();
4733
4734 assert_eq!(threshold_out.next().await.unwrap(), 7);
4735 }
4736
4737 #[cfg(feature = "deploy")]
4738 #[tokio::test]
4739 async fn monotone_count_threshold() {
4740 let mut deployment = Deployment::new();
4741
4742 let mut flow = FlowBuilder::new();
4743 let node = flow.process::<()>();
4744 let external = flow.external::<()>();
4745
4746 let in_unbounded: super::Stream<_, _> =
4747 node.source_iter(q!(vec![1i32, 2, 3, 4, 5, 6])).into();
4748 let sum = in_unbounded.count();
4749
4750 let threshold_out = sum
4751 .threshold_greater_or_equal(node.singleton(q!(3)))
4752 .send_bincode_external(&external);
4753
4754 let nodes = flow
4755 .with_process(&node, deployment.Localhost())
4756 .with_external(&external, deployment.Localhost())
4757 .deploy(&mut deployment);
4758
4759 deployment.deploy().await.unwrap();
4760
4761 let mut threshold_out = nodes.connect(threshold_out).await;
4762
4763 deployment.start().await.unwrap();
4764
4765 assert_eq!(threshold_out.next().await.unwrap(), 3);
4766 }
4767
4768 #[cfg(feature = "deploy")]
4769 #[tokio::test]
4770 async fn monotone_map_order_preserving_threshold() {
4771 use crate::properties::manual_proof;
4772
4773 let mut deployment = Deployment::new();
4774
4775 let mut flow = FlowBuilder::new();
4776 let node = flow.process::<()>();
4777 let external = flow.external::<()>();
4778
4779 let in_unbounded: super::Stream<_, _> =
4780 node.source_iter(q!(vec![1i32, 2, 3, 4, 5, 6])).into();
4781 let sum = in_unbounded.fold(
4782 q!(|| 0),
4783 q!(
4784 |sum, v| {
4785 *sum += v;
4786 },
4787 monotone = manual_proof!()
4788 ),
4789 );
4790
4791 let doubled = sum.map(q!(
4793 |v| v * 2,
4794 order_preserving = manual_proof!()
4795 ));
4796
4797 let threshold_out = doubled
4798 .threshold_greater_or_equal(node.singleton(q!(14)))
4799 .send_bincode_external(&external);
4800
4801 let nodes = flow
4802 .with_process(&node, deployment.Localhost())
4803 .with_external(&external, deployment.Localhost())
4804 .deploy(&mut deployment);
4805
4806 deployment.deploy().await.unwrap();
4807
4808 let mut threshold_out = nodes.connect(threshold_out).await;
4809
4810 deployment.start().await.unwrap();
4811
4812 assert_eq!(threshold_out.next().await.unwrap(), 14);
4813 }
4814
4815 #[cfg(any(feature = "deploy", feature = "sim"))]
4818 mod join_ordering_type_tests {
4819 use crate::live_collections::boundedness::{Bounded, Unbounded};
4820 use crate::live_collections::stream::{ExactlyOnce, NoOrder, Stream, TotalOrder};
4821 use crate::location::{Location, Process};
4822
4823 #[expect(dead_code, reason = "compile-time type test")]
4824 fn join_unbounded_with_bounded_preserves_order<'a>(
4825 left: Stream<(i32, char), Process<'a>, Unbounded, TotalOrder, ExactlyOnce>,
4826 right: Stream<(i32, char), Process<'a>, Bounded, TotalOrder, ExactlyOnce>,
4827 ) -> Stream<(i32, (char, char)), Process<'a>, Unbounded, TotalOrder, ExactlyOnce> {
4828 left.join(right)
4829 }
4830
4831 #[expect(dead_code, reason = "compile-time type test")]
4832 fn join_unbounded_with_unbounded_is_no_order<'a>(
4833 left: Stream<(i32, char), Process<'a>, Unbounded, TotalOrder, ExactlyOnce>,
4834 right: Stream<(i32, char), Process<'a>, Unbounded, TotalOrder, ExactlyOnce>,
4835 ) -> Stream<(i32, (char, char)), Process<'a>, Unbounded, NoOrder, ExactlyOnce> {
4836 left.join(right)
4837 }
4838
4839 #[expect(dead_code, reason = "compile-time type test")]
4840 fn join_bounded_with_bounded_preserves_order<'a, L: Location<'a>>(
4841 left: Stream<(i32, char), L, Bounded, TotalOrder, ExactlyOnce>,
4842 right: Stream<(i32, char), L, Bounded, TotalOrder, ExactlyOnce>,
4843 ) -> Stream<(i32, (char, char)), L, Bounded, TotalOrder, ExactlyOnce> {
4844 left.join(right)
4845 }
4846
4847 #[expect(dead_code, reason = "compile-time type test")]
4848 fn join_unbounded_noorder_with_bounded<'a>(
4849 left: Stream<(i32, char), Process<'a>, Unbounded, NoOrder, ExactlyOnce>,
4850 right: Stream<(i32, char), Process<'a>, Bounded, NoOrder, ExactlyOnce>,
4851 ) -> Stream<(i32, (char, char)), Process<'a>, Unbounded, NoOrder, ExactlyOnce> {
4852 left.join(right)
4853 }
4854
4855 #[expect(dead_code, reason = "compile-time type test")]
4858 fn cross_product_unbounded_with_bounded_preserves_order<'a>(
4859 left: Stream<i32, Process<'a>, Unbounded, TotalOrder, ExactlyOnce>,
4860 right: Stream<char, Process<'a>, Bounded, TotalOrder, ExactlyOnce>,
4861 ) -> Stream<(i32, char), Process<'a>, Unbounded, TotalOrder, ExactlyOnce> {
4862 left.cross_product(right)
4863 }
4864
4865 #[expect(dead_code, reason = "compile-time type test")]
4866 fn cross_product_bounded_with_bounded_preserves_order<'a>(
4867 left: Stream<i32, Process<'a>, Bounded, TotalOrder, ExactlyOnce>,
4868 right: Stream<char, Process<'a>, Bounded, TotalOrder, ExactlyOnce>,
4869 ) -> Stream<(i32, char), Process<'a>, Bounded, TotalOrder, ExactlyOnce> {
4870 left.cross_product(right)
4871 }
4872
4873 #[expect(dead_code, reason = "compile-time type test")]
4874 fn cross_product_unbounded_with_unbounded_is_no_order<'a>(
4875 left: Stream<i32, Process<'a>, Unbounded, TotalOrder, ExactlyOnce>,
4876 right: Stream<char, Process<'a>, Unbounded, TotalOrder, ExactlyOnce>,
4877 ) -> Stream<(i32, char), Process<'a>, Unbounded, NoOrder, ExactlyOnce> {
4878 left.cross_product(right)
4879 }
4880 } #[cfg(feature = "sim")]
4885 #[test]
4886 fn cross_product_mixed_boundedness_correctness() {
4887 use stageleft::q;
4888
4889 use crate::compile::builder::FlowBuilder;
4890 use crate::nondet::nondet;
4891
4892 let mut flow = FlowBuilder::new();
4893 let process = flow.process::<()>();
4894 let tick = process.tick();
4895
4896 let left = process.source_iter(q!(vec![1, 2]));
4897 let right = process
4898 .source_iter(q!(vec!['a', 'b']))
4899 .batch(&tick, nondet!())
4900 .all_ticks();
4901
4902 let out = left.cross_product(right).sim_output();
4903
4904 flow.sim().exhaustive(async || {
4905 out.assert_yields_only_unordered(vec![(1, 'a'), (1, 'b'), (2, 'a'), (2, 'b')])
4906 .await;
4907 });
4908 }
4909
4910 #[cfg(feature = "sim")]
4911 #[test]
4912 fn join_mixed_boundedness_correctness() {
4913 use stageleft::q;
4914
4915 use crate::compile::builder::FlowBuilder;
4916 use crate::nondet::nondet;
4917
4918 let mut flow = FlowBuilder::new();
4919 let process = flow.process::<()>();
4920 let tick = process.tick();
4921
4922 let left = process.source_iter(q!(vec![(1, 'a'), (2, 'b')]));
4923 let right = process
4924 .source_iter(q!(vec![(1, 'x'), (2, 'y')]))
4925 .batch(&tick, nondet!())
4926 .all_ticks();
4927
4928 let out = left.join(right).sim_output();
4929
4930 flow.sim().exhaustive(async || {
4931 out.assert_yields_only_unordered(vec![(1, ('a', 'x')), (2, ('b', 'y'))])
4932 .await;
4933 });
4934 }
4935
4936 #[cfg(feature = "sim")]
4937 #[test]
4938 fn sim_merge_unordered_independent_atomics() {
4939 let mut flow = FlowBuilder::new();
4940 let node = flow.process::<()>();
4941
4942 let (in1_send, input1) = node.sim_input::<_, TotalOrder, _>();
4943 let (in2_send, input2) = node.sim_input::<_, TotalOrder, _>();
4944
4945 let out = input1
4946 .atomic()
4947 .merge_unordered(input2.atomic())
4948 .end_atomic()
4949 .sim_output();
4950
4951 flow.sim().exhaustive(async || {
4952 in1_send.send(1);
4953 in2_send.send(2);
4954
4955 out.assert_yields_only_unordered(vec![1, 2]).await;
4956 });
4957 }
4958
4959 #[cfg(feature = "deploy")]
4960 #[tokio::test]
4961 async fn test_stream_ref() {
4962 let mut deployment = Deployment::new();
4963
4964 let mut flow = FlowBuilder::new();
4965 let external = flow.external::<()>();
4966 let p1 = flow.process::<()>();
4967
4968 let my_stream = p1.source_iter(q!(1..=5i32));
4970
4971 let stream_ref = my_stream.by_ref();
4972
4973 let out_port = p1
4975 .source_iter(q!([()]))
4976 .map(q!(|_| stream_ref.len() as i32))
4977 .send_bincode_external(&external);
4978
4979 my_stream.for_each(q!(|_| {}));
4981
4982 let nodes = flow
4983 .with_default_optimize()
4984 .with_process(&p1, deployment.Localhost())
4985 .with_external(&external, deployment.Localhost())
4986 .deploy(&mut deployment);
4987
4988 deployment.deploy().await.unwrap();
4989
4990 let mut out_recv = nodes.connect(out_port).await;
4991
4992 deployment.start().await.unwrap();
4993
4994 let result = out_recv.next().await.unwrap();
4995 assert_eq!(result, 5);
4997 }
4998
4999 #[cfg(feature = "deploy")]
5000 #[tokio::test]
5001 async fn test_stream_ref_contents() {
5002 let mut deployment = Deployment::new();
5003
5004 let mut flow = FlowBuilder::new();
5005 let external = flow.external::<()>();
5006 let p1 = flow.process::<()>();
5007
5008 let my_stream = p1.source_iter(q!(1..=3i32));
5010
5011 let stream_ref = my_stream.by_ref();
5012
5013 let out_port = p1
5015 .source_iter(q!([()]))
5016 .map(q!(|_| stream_ref.iter().sum::<i32>()))
5017 .send_bincode_external(&external);
5018
5019 my_stream.for_each(q!(|_| {}));
5020
5021 let nodes = flow
5022 .with_default_optimize()
5023 .with_process(&p1, deployment.Localhost())
5024 .with_external(&external, deployment.Localhost())
5025 .deploy(&mut deployment);
5026
5027 deployment.deploy().await.unwrap();
5028
5029 let mut out_recv = nodes.connect(out_port).await;
5030
5031 deployment.start().await.unwrap();
5032
5033 let result = out_recv.next().await.unwrap();
5034 assert_eq!(result, 6);
5036 }
5037
5038 #[cfg(feature = "deploy")]
5039 #[tokio::test]
5040 async fn test_stream_ref_no_consumer() {
5041 let mut deployment = Deployment::new();
5042
5043 let mut flow = FlowBuilder::new();
5044 let external = flow.external::<()>();
5045 let p1 = flow.process::<()>();
5046
5047 let my_stream = p1.source_iter(q!(1..=4i32));
5049
5050 let stream_ref = my_stream.by_ref();
5051
5052 let out_port = p1
5053 .source_iter(q!([()]))
5054 .map(q!(|_| stream_ref.len() as i32))
5055 .send_bincode_external(&external);
5056
5057 let nodes = flow
5058 .with_default_optimize()
5059 .with_process(&p1, deployment.Localhost())
5060 .with_external(&external, deployment.Localhost())
5061 .deploy(&mut deployment);
5062
5063 deployment.deploy().await.unwrap();
5064
5065 let mut out_recv = nodes.connect(out_port).await;
5066
5067 deployment.start().await.unwrap();
5068
5069 let result = out_recv.next().await.unwrap();
5070 assert_eq!(result, 4);
5071 }
5072
5073 #[cfg(feature = "deploy")]
5074 #[tokio::test]
5075 async fn test_stream_mut() {
5076 let mut deployment = Deployment::new();
5077
5078 let mut flow = FlowBuilder::new();
5079 let external = flow.external::<()>();
5080 let p1 = flow.process::<()>();
5081
5082 let my_stream = p1.source_iter(q!(1..=5i32));
5084
5085 let stream_mut = my_stream.by_mut();
5086
5087 let out_port = p1
5089 .source_iter(q!([()]))
5090 .map(q!(|_| {
5091 stream_mut.retain(|x| *x > 3);
5092 stream_mut.len() as i32
5093 }))
5094 .send_bincode_external(&external);
5095
5096 my_stream.for_each(q!(|_| {}));
5097
5098 let nodes = flow
5099 .with_default_optimize()
5100 .with_process(&p1, deployment.Localhost())
5101 .with_external(&external, deployment.Localhost())
5102 .deploy(&mut deployment);
5103
5104 deployment.deploy().await.unwrap();
5105
5106 let mut out_recv = nodes.connect(out_port).await;
5107
5108 deployment.start().await.unwrap();
5109
5110 let result = out_recv.next().await.unwrap();
5111 assert_eq!(result, 2);
5113 }
5114
5115 #[cfg(feature = "sim")]
5119 #[test]
5120 fn sim_map_with_mut_on_unordered_explores_multiple_states() {
5121 use crate::live_collections::sliced::sliced;
5122 use crate::live_collections::stream::ExactlyOnce;
5123 use crate::properties::manual_proof;
5124
5125 let mut flow = FlowBuilder::new();
5126 let node = flow.process::<()>();
5127
5128 let (trigger_send, trigger) = node.sim_input::<i32, TotalOrder, ExactlyOnce>();
5129
5130 let out_recv = sliced! {
5131 let batch = use::batch(trigger, nondet!());
5132 let counter = batch.location().source_iter(q!(vec![0i32]))
5133 .fold(q!(|| 0i32), q!(|acc, v| *acc += v));
5134 let counter_mut = counter.by_mut();
5135 let items = batch.location().source_iter(q!(vec![1i32, 2])).weaken_ordering::<NoOrder>();
5136 items.map(q!(
5137 |x| {
5138 *counter_mut += x;
5139 *counter_mut
5140 },
5141 commutative = manual_proof!()
5142 ))
5143 }
5144 .sim_output();
5145
5146 let count = flow.sim().exhaustive(async || {
5147 trigger_send.send(1);
5148 let _all: Vec<i32> = out_recv.collect_sorted().await;
5149 });
5150
5151 assert_eq!(
5152 count, 2,
5153 "Expected 2 simulation instances due to mut on unordered input, got {}",
5154 count
5155 );
5156 }
5157
5158 #[cfg(feature = "sim")]
5162 #[test]
5163 fn sim_scan_with_ref_capture() {
5164 use crate::live_collections::sliced::sliced;
5165 use crate::live_collections::stream::ExactlyOnce;
5166
5167 let mut flow = FlowBuilder::new();
5168 let node = flow.process::<()>();
5169
5170 let (trigger_send, trigger) = node.sim_input::<i32, TotalOrder, ExactlyOnce>();
5171
5172 let out_recv = sliced! {
5173 let batch = use::batch(trigger, nondet!());
5174 let offset = batch
5175 .location()
5176 .source_iter(q!(vec![10i32]))
5177 .fold(q!(|| 0i32), q!(|acc, v| *acc += v));
5178 let offset_ref = offset.by_ref();
5179 batch
5180 .location()
5181 .source_iter(q!(vec![1i32, 2, 3]))
5182 .scan(
5183 q!(|| 0i32),
5184 q!(move |acc: &mut i32, x| {
5185 *acc += x + *offset_ref;
5186 Some(*acc)
5187 }),
5188 )
5189 }
5190 .sim_output();
5191
5192 let count = flow.sim().exhaustive(async || {
5193 trigger_send.send(1);
5194 let all: Vec<i32> = out_recv.collect().await;
5195 assert_eq!(all, vec![11, 23, 36]);
5200 });
5201
5202 assert_eq!(
5203 count, 1,
5204 "Expected a single simulation instance for a totally-ordered scan, got {}",
5205 count
5206 );
5207 }
5208
5209 #[cfg(feature = "sim")]
5213 #[test]
5214 #[ignore = "observe_nondet not yet supported for top-level bounded inputs (https://github.com/hydro-project/hydro/issues/2950)"]
5215 fn sim_map_with_mut_on_unordered_top_level() {
5216 use crate::properties::manual_proof;
5217
5218 let mut flow = FlowBuilder::new();
5219 let node = flow.process::<()>();
5220
5221 let counter = node
5222 .source_iter(q!(vec![0i32]))
5223 .fold(q!(|| 0i32), q!(|acc, v| *acc += v));
5224 let counter_mut = counter.by_mut();
5225
5226 let out_recv = node
5227 .source_iter(q!(vec![1i32, 2]))
5228 .weaken_ordering::<NoOrder>()
5229 .map(q!(
5230 |x| {
5231 *counter_mut += x;
5232 *counter_mut
5233 },
5234 commutative = manual_proof!()
5235 ))
5236 .assume_ordering::<TotalOrder>(nondet!())
5237 .sim_output();
5238
5239 counter.into_stream().for_each(q!(|_| {}));
5240
5241 let count = flow.sim().exhaustive(async || {
5242 let _all: Vec<i32> = out_recv.collect().await;
5243 });
5244
5245 assert_eq!(
5246 count, 2,
5247 "Expected 2 simulation instances due to mut on unordered input, got {}",
5248 count
5249 );
5250 }
5251}