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, 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: Ordering, O2: Ordering> WeakerOrderingThan<O2> for O where O: 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: Retries, R2: Retries> WeakerRetryThan<R2> for R where R: 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}`. Strengthen the order upstream or consider a different API.",
157 label = "required here",
158 note = "To intentionally process the stream by observing a non-deterministic (shuffled) order of elements, use `.assume_ordering`. This introduces non-determinism so avoid unless necessary."
159)]
160pub trait IsOrdered: Ordering {}
162
163#[sealed::sealed]
164#[diagnostic::do_not_recommend]
165impl IsOrdered for TotalOrder {}
166
167#[sealed::sealed]
168#[diagnostic::on_unimplemented(
169 message = "The input stream must be exactly-once (`ExactlyOnce`), but has retries `{Self}`. Strengthen the retries guarantee upstream or consider a different API.",
170 label = "required here",
171 note = "To intentionally process the stream by observing non-deterministic (randomly duplicated) retries, use `.assume_retries`. This introduces non-determinism so avoid unless necessary."
172)]
173pub trait IsExactlyOnce: Retries {}
175
176#[sealed::sealed]
177#[diagnostic::do_not_recommend]
178impl IsExactlyOnce for ExactlyOnce {}
179
180pub struct Stream<
200 Type,
201 Loc,
202 Bound: Boundedness = Unbounded,
203 Order: Ordering = TotalOrder,
204 Retry: Retries = ExactlyOnce,
205> {
206 pub(crate) location: Loc,
207 pub(crate) ir_node: Rc<RefCell<HydroNode>>,
208 pub(crate) flow_state: FlowState,
209
210 _phantom: PhantomData<(Type, Loc, Bound, Order, Retry)>,
211}
212
213impl<T, L, B: Boundedness, O: Ordering, R: Retries> Drop for Stream<T, L, B, O, R> {
214 fn drop(&mut self) {
215 let ir_node = self.ir_node.replace(HydroNode::Placeholder);
216 if !matches!(ir_node, HydroNode::Placeholder) && !ir_node.is_shared_with_others() {
217 self.flow_state.borrow_mut().try_push_root(HydroRoot::Null {
218 input: Box::new(ir_node),
219 op_metadata: HydroIrOpMetadata::new(),
220 });
221 }
222 }
223}
224
225impl<'a, T, L, O: Ordering, R: Retries> From<Stream<T, L, Bounded, O, R>>
226 for Stream<T, L, Unbounded, O, R>
227where
228 L: Location<'a>,
229{
230 fn from(stream: Stream<T, L, Bounded, O, R>) -> Stream<T, L, Unbounded, O, R> {
231 let new_meta = stream
232 .location
233 .new_node_metadata(Stream::<T, L, Unbounded, O, R>::collection_kind());
234
235 let flow_state = stream.flow_state.clone();
236 Stream {
237 location: stream.location.clone(),
238 ir_node: super::tracked_ir_node(
239 &flow_state,
240 HydroNode::Cast {
241 inner: Box::new(stream.ir_node.replace(HydroNode::Placeholder)),
242 metadata: new_meta,
243 },
244 ),
245 flow_state,
246 _phantom: PhantomData,
247 }
248 }
249}
250
251impl<'a, T, L, B: Boundedness, R: Retries> From<Stream<T, L, B, TotalOrder, R>>
252 for Stream<T, L, B, NoOrder, R>
253where
254 L: Location<'a>,
255{
256 fn from(stream: Stream<T, L, B, TotalOrder, R>) -> Stream<T, L, B, NoOrder, R> {
257 stream.weaken_ordering()
258 }
259}
260
261impl<'a, T, L, B: Boundedness, O: Ordering> From<Stream<T, L, B, O, ExactlyOnce>>
262 for Stream<T, L, B, O, AtLeastOnce>
263where
264 L: Location<'a>,
265{
266 fn from(stream: Stream<T, L, B, O, ExactlyOnce>) -> Stream<T, L, B, O, AtLeastOnce> {
267 stream.weaken_retries()
268 }
269}
270
271impl<'a, T, L, O: Ordering, R: Retries> DeferTick for Stream<T, Tick<L>, Bounded, O, R>
272where
273 L: Location<'a>,
274{
275 fn defer_tick(self) -> Self {
276 Stream::defer_tick(self)
277 }
278}
279
280impl<'a, T, L, O: Ordering, R: Retries> CycleCollection<'a, TickCycle>
281 for Stream<T, Tick<L>, Bounded, O, R>
282where
283 L: Location<'a>,
284{
285 type Location = Tick<L>;
286
287 fn create_source(cycle_id: CycleId, location: Tick<L>) -> Self {
288 Stream::new(
289 location.clone(),
290 HydroNode::CycleSource {
291 cycle_id,
292 metadata: location.new_node_metadata(Self::collection_kind()),
293 },
294 )
295 }
296}
297
298impl<'a, T, L, O: Ordering, R: Retries> CycleCollectionWithInitial<'a, TickCycle>
299 for Stream<T, Tick<L>, Bounded, O, R>
300where
301 L: Location<'a>,
302{
303 type Location = Tick<L>;
304
305 fn location(&self) -> &Self::Location {
306 self.location()
307 }
308
309 fn create_source_with_initial(cycle_id: CycleId, initial: Self, location: Tick<L>) -> Self {
310 let from_previous_tick: Stream<T, Tick<L>, Bounded, O, R> = Stream::new(
311 location.clone(),
312 HydroNode::DeferTick {
313 input: Box::new(HydroNode::CycleSource {
314 cycle_id,
315 metadata: location.new_node_metadata(Self::collection_kind()),
316 }),
317 metadata: location.new_node_metadata(Self::collection_kind()),
318 },
319 );
320
321 from_previous_tick.chain(initial.filter_if(location.optional_first_tick(q!(())).is_some()))
322 }
323}
324
325impl<'a, T, L, O: Ordering, R: Retries> ReceiverComplete<'a, TickCycle>
326 for Stream<T, Tick<L>, Bounded, O, R>
327where
328 L: Location<'a>,
329{
330 fn complete(self, cycle_id: CycleId, expected_location: LocationId) {
331 assert_eq!(
332 Location::id(&self.location),
333 expected_location,
334 "locations do not match"
335 );
336 self.location
337 .flow_state()
338 .borrow_mut()
339 .push_root(HydroRoot::CycleSink {
340 cycle_id,
341 input: Box::new(self.ir_node.replace(HydroNode::Placeholder)),
342 op_metadata: HydroIrOpMetadata::new(),
343 });
344 }
345}
346
347impl<'a, T, L, B: Boundedness, O: Ordering, R: Retries> CycleCollection<'a, ForwardRef>
348 for Stream<T, L, B, O, R>
349where
350 L: Location<'a>,
351{
352 type Location = L;
353
354 fn create_source(cycle_id: CycleId, location: L) -> Self {
355 Stream::new(
356 location.clone(),
357 HydroNode::CycleSource {
358 cycle_id,
359 metadata: location.new_node_metadata(Self::collection_kind()),
360 },
361 )
362 }
363}
364
365impl<'a, T, L, B: Boundedness, O: Ordering, R: Retries> ReceiverComplete<'a, ForwardRef>
366 for Stream<T, L, B, O, R>
367where
368 L: Location<'a>,
369{
370 fn complete(self, cycle_id: CycleId, expected_location: LocationId) {
371 assert_eq!(
372 Location::id(&self.location),
373 expected_location,
374 "locations do not match"
375 );
376 self.location
377 .flow_state()
378 .borrow_mut()
379 .push_root(HydroRoot::CycleSink {
380 cycle_id,
381 input: Box::new(self.ir_node.replace(HydroNode::Placeholder)),
382 op_metadata: HydroIrOpMetadata::new(),
383 });
384 }
385}
386
387impl<'a, T, L, B: Boundedness, O: Ordering, R: Retries> Clone for Stream<T, L, B, O, R>
388where
389 T: Clone,
390 L: Location<'a>,
391{
392 fn clone(&self) -> Self {
393 if !matches!(self.ir_node.borrow().deref(), HydroNode::Tee { .. }) {
394 let orig_ir_node = self.ir_node.replace(HydroNode::Placeholder);
395 *self.ir_node.borrow_mut() = HydroNode::Tee {
396 inner: SharedNode(Rc::new(RefCell::new(orig_ir_node))),
397 metadata: self.location.new_node_metadata(Self::collection_kind()),
398 };
399 }
400
401 let HydroNode::Tee { inner, metadata } = &*self.ir_node.borrow() else {
402 unreachable!()
403 };
404 Stream {
405 location: self.location.clone(),
406 flow_state: self.flow_state.clone(),
407 ir_node: super::tracked_ir_node(
408 &self.flow_state,
409 HydroNode::Tee {
410 inner: SharedNode(inner.0.clone()),
411 metadata: metadata.clone(),
412 },
413 ),
414 _phantom: PhantomData,
415 }
416 }
417}
418
419impl<'a, T, L, B: Boundedness, O: Ordering, R: Retries> Stream<T, L, B, O, R>
420where
421 L: Location<'a>,
422{
423 pub(crate) fn new(location: L, ir_node: HydroNode) -> Self {
424 debug_assert_eq!(ir_node.metadata().location_id, Location::id(&location));
425 debug_assert_eq!(ir_node.metadata().collection_kind, Self::collection_kind());
426
427 let flow_state = location.flow_state().clone();
428 let ir_node = super::tracked_ir_node(&flow_state, ir_node);
429 Stream {
430 location,
431 flow_state,
432 ir_node,
433 _phantom: PhantomData,
434 }
435 }
436
437 pub fn location(&self) -> &L {
439 &self.location
440 }
441
442 pub fn by_ref(&self) -> crate::handoff_ref::StreamRef<'a, '_, T, L, B>
447 where
448 B: IsBounded,
449 {
450 crate::handoff_ref::StreamRef::new(&self.ir_node)
451 }
452
453 pub fn by_mut(&self) -> crate::handoff_ref::StreamMut<'a, '_, T, L, B>
456 where
457 B: IsBounded,
458 {
459 crate::handoff_ref::StreamMut::new(&self.ir_node)
460 }
461
462 pub fn weaken_consistency(self) -> Stream<T, L::DropConsistency, B, O, R>
465 where
466 L: Location<'a>,
467 {
468 if L::consistency()
469 .is_none_or(|c| c == crate::location::dynamic::ClusterConsistency::NoConsistency)
470 {
471 Stream::new(
473 self.location.drop_consistency(),
474 self.ir_node.replace(HydroNode::Placeholder),
475 )
476 } else {
477 Stream::new(
478 self.location.drop_consistency(),
479 HydroNode::Cast {
480 inner: Box::new(self.ir_node.replace(HydroNode::Placeholder)),
481 metadata: self.location.drop_consistency().new_node_metadata(Stream::<
482 T,
483 L::DropConsistency,
484 B,
485 O,
486 R,
487 >::collection_kind(
488 )),
489 },
490 )
491 }
492 }
493
494 pub fn assert_has_consistency_of<L2: Location<'a, DropConsistency = L::DropConsistency>>(
498 self,
499 _proof: impl crate::properties::ConsistencyProof,
500 ) -> Stream<T, L2, B, O, R>
501 where
502 L: Location<'a>,
503 {
504 if L::consistency() == L2::consistency() {
505 Stream::new(
506 self.location.with_consistency_of(),
507 self.ir_node.replace(HydroNode::Placeholder),
508 )
509 } else {
510 Stream::new(
511 self.location.with_consistency_of(),
512 HydroNode::AssertIsConsistent {
513 inner: Box::new(self.ir_node.replace(HydroNode::Placeholder)),
514 trusted: false,
515 metadata: self
516 .location
517 .clone()
518 .with_consistency_of::<L2>()
519 .new_node_metadata(Stream::<T, L2, B, O, R>::collection_kind()),
520 },
521 )
522 }
523 }
524
525 pub(crate) fn assert_has_consistency_of_trusted<
526 L2: Location<'a, DropConsistency = L::DropConsistency>,
527 >(
528 self,
529 _proof: impl crate::properties::ConsistencyProof,
530 ) -> Stream<T, L2, B, O, R>
531 where
532 L: Location<'a>,
533 {
534 if L::consistency() == L2::consistency() {
535 Stream::new(
536 self.location.with_consistency_of(),
537 self.ir_node.replace(HydroNode::Placeholder),
538 )
539 } else {
540 Stream::new(
541 self.location.with_consistency_of(),
542 HydroNode::AssertIsConsistent {
543 inner: Box::new(self.ir_node.replace(HydroNode::Placeholder)),
544 trusted: true,
545 metadata: self
546 .location
547 .clone()
548 .with_consistency_of::<L2>()
549 .new_node_metadata(Stream::<T, L2, B, O, R>::collection_kind()),
550 },
551 )
552 }
553 }
554
555 pub(crate) fn collection_kind() -> CollectionKind {
556 CollectionKind::Stream {
557 bound: B::BOUND_KIND,
558 order: O::ORDERING_KIND,
559 retry: R::RETRIES_KIND,
560 element_type: quote_type::<T>().into(),
561 }
562 }
563
564 pub fn map<U, F, C, I, const WAS_MUT: bool>(
592 self,
593 f: impl IntoQuotedMut<'a, F, OperatorContext<L, B>, StreamMapFuncAlgebra<T, B, C, I>>,
594 ) -> Stream<U, L, B, O, R>
595 where
596 F: FnMut(T) -> U + 'a,
597 C: ValidMutCommutativityFor<F, T, U, O, WAS_MUT>,
598 I: ValidMutIdempotenceFor<F, T, U, R, WAS_MUT>,
599 {
600 let f = crate::handoff_ref::with_ref_capture(|| {
601 let (expr, proof) =
602 f.splice_fnmut1_ctx_props(&OperatorContext::<L, B>::new(&self.location));
603 proof.register_proof(&expr);
604 expr.into()
605 });
606 Stream::new(
607 self.location.clone(),
608 HydroNode::Map {
609 f,
610 input: Box::new(self.ir_node.replace(HydroNode::Placeholder)),
611 metadata: self
612 .location
613 .new_node_metadata(Stream::<U, L, B, O, R>::collection_kind()),
614 },
615 )
616 }
617
618 pub fn flat_map_ordered<U, I, F, C, Idemp, const WAS_MUT: bool>(
643 self,
644 f: impl IntoQuotedMut<'a, F, OperatorContext<L, B>, StreamMapFuncAlgebra<T, B, C, Idemp>>,
645 ) -> Stream<U, L, B, O, R>
646 where
647 I: IntoIterator<Item = U>,
648 F: FnMut(T) -> I + 'a,
649 C: ValidMutCommutativityFor<F, T, I, O, WAS_MUT>,
650 Idemp: ValidMutIdempotenceFor<F, T, I, R, WAS_MUT>,
651 {
652 let f = crate::handoff_ref::with_ref_capture(|| {
653 let (expr, proof) =
654 f.splice_fnmut1_ctx_props(&OperatorContext::<L, B>::new(&self.location));
655 proof.register_proof(&expr);
656 expr.into()
657 });
658 Stream::new(
659 self.location.clone(),
660 HydroNode::FlatMap {
661 f,
662 input: Box::new(self.ir_node.replace(HydroNode::Placeholder)),
663 metadata: self
664 .location
665 .new_node_metadata(Stream::<U, L, B, O, R>::collection_kind()),
666 },
667 )
668 }
669
670 pub fn flat_map_unordered<U, I, F, C, Idemp, const WAS_MUT: bool>(
697 self,
698 f: impl IntoQuotedMut<'a, F, OperatorContext<L, B>, StreamMapFuncAlgebra<T, B, C, Idemp>>,
699 ) -> Stream<U, L, B, NoOrder, R>
700 where
701 I: IntoIterator<Item = U>,
702 F: FnMut(T) -> I + 'a,
703 C: ValidMutCommutativityFor<F, T, I, O, WAS_MUT>,
704 Idemp: ValidMutIdempotenceFor<F, T, I, R, WAS_MUT>,
705 {
706 let f = crate::handoff_ref::with_ref_capture(|| {
707 let (expr, proof) =
708 f.splice_fnmut1_ctx_props(&OperatorContext::<L, B>::new(&self.location));
709 proof.register_proof(&expr);
710 expr.into()
711 });
712 Stream::new(
713 self.location.clone(),
714 HydroNode::FlatMap {
715 f,
716 input: Box::new(self.ir_node.replace(HydroNode::Placeholder)),
717 metadata: self
718 .location
719 .new_node_metadata(Stream::<U, L, B, NoOrder, R>::collection_kind()),
720 },
721 )
722 }
723
724 pub fn flatten_ordered<U>(self) -> Stream<U, L, B, O, R>
747 where
748 T: IntoIterator<Item = U>,
749 {
750 self.flat_map_ordered(q!(|d| d))
751 }
752
753 pub fn flatten_unordered<U>(self) -> Stream<U, L, B, NoOrder, R>
780 where
781 T: IntoIterator<Item = U>,
782 {
783 self.flat_map_unordered(q!(|d| d))
784 }
785
786 pub fn flat_map_stream_blocking<U, S, F, C, Idemp, const WAS_MUT: bool>(
790 self,
791 f: impl IntoQuotedMut<'a, F, OperatorContext<L, B>, StreamMapFuncAlgebra<T, B, C, Idemp>>,
792 ) -> Stream<U, L, B, O, R>
793 where
794 S: futures::Stream<Item = U>,
795 F: FnMut(T) -> S + 'a,
796 C: ValidMutCommutativityFor<F, T, S, O, WAS_MUT>,
797 Idemp: ValidMutIdempotenceFor<F, T, S, R, WAS_MUT>,
798 {
799 let f = crate::handoff_ref::with_ref_capture(|| {
800 let (expr, proof) =
801 f.splice_fnmut1_ctx_props(&OperatorContext::<L, B>::new(&self.location));
802 proof.register_proof(&expr);
803 expr.into()
804 });
805 Stream::new(
806 self.location.clone(),
807 HydroNode::FlatMapStreamBlocking {
808 f,
809 input: Box::new(self.ir_node.replace(HydroNode::Placeholder)),
810 metadata: self
811 .location
812 .new_node_metadata(Stream::<U, L, B, O, R>::collection_kind()),
813 },
814 )
815 }
816
817 pub fn flatten_stream_blocking<U>(self) -> Stream<U, L, B, O, R>
821 where
822 T: futures::Stream<Item = U>,
823 {
824 self.flat_map_stream_blocking(q!(|d| d))
825 }
826
827 pub fn filter<F, C, Idemp, const WAS_MUT: bool>(
859 self,
860 f: impl IntoQuotedMut<'a, F, OperatorContext<L, B>, StreamMapFuncAlgebra<T, B, C, Idemp>>,
861 ) -> Self
862 where
863 F: FnMut(&T) -> bool + 'a,
864 C: ValidMutBorrowCommutativityFor<F, T, bool, O, WAS_MUT>,
865 Idemp: ValidMutBorrowIdempotenceFor<F, T, bool, R, WAS_MUT>,
866 {
867 let f = crate::handoff_ref::with_ref_capture(|| {
868 let (expr, proof) =
869 f.splice_fnmut1_borrow_ctx_props(&OperatorContext::<L, B>::new(&self.location));
870 proof.register_proof(&expr);
871 expr.into()
872 });
873 Stream::new(
874 self.location.clone(),
875 HydroNode::Filter {
876 f,
877 input: Box::new(self.ir_node.replace(HydroNode::Placeholder)),
878 metadata: self.location.new_node_metadata(Self::collection_kind()),
879 },
880 )
881 }
882
883 pub fn partition<F, C, Idemp, const WAS_MUT: bool>(
918 self,
919 f: impl IntoQuotedMut<'a, F, OperatorContext<L, B>, StreamMapFuncAlgebra<T, B, C, Idemp>>,
920 ) -> (Stream<T, L, B, O, R>, Stream<T, L, B, O, R>)
921 where
922 F: FnMut(&T) -> bool + 'a,
923 C: ValidMutBorrowCommutativityFor<F, T, bool, O, WAS_MUT>,
924 Idemp: ValidMutBorrowIdempotenceFor<F, T, bool, R, WAS_MUT>,
925 {
926 let f = crate::handoff_ref::with_ref_capture(|| {
927 let (expr, proof) =
928 f.splice_fnmut1_borrow_ctx_props(&OperatorContext::<L, B>::new(&self.location));
929 proof.register_proof(&expr);
930 expr.into()
931 });
932 let shared = Rc::new(RefCell::new(HydroNode::PartitionShared {
933 input: Box::new(self.ir_node.replace(HydroNode::Placeholder)),
934 f,
935 metadata: self.location.new_node_metadata(Self::collection_kind()),
936 }));
937
938 let true_stream = Stream::new(
939 self.location.clone(),
940 HydroNode::PartitionSide {
941 inner: SharedNode(Rc::clone(&shared)),
942 is_true: true,
943 metadata: self.location.new_node_metadata(Self::collection_kind()),
944 },
945 );
946
947 let false_stream = Stream::new(
948 self.location.clone(),
949 HydroNode::PartitionSide {
950 inner: SharedNode(shared),
951 is_true: false,
952 metadata: self.location.new_node_metadata(Self::collection_kind()),
953 },
954 );
955
956 (true_stream, false_stream)
957 }
958
959 pub fn filter_map<U, F, C, Idemp, const WAS_MUT: bool>(
979 self,
980 f: impl IntoQuotedMut<'a, F, OperatorContext<L, B>, StreamMapFuncAlgebra<T, B, C, Idemp>>,
981 ) -> Stream<U, L, B, O, R>
982 where
983 F: FnMut(T) -> Option<U> + 'a,
984 C: ValidMutCommutativityFor<F, T, Option<U>, O, WAS_MUT>,
985 Idemp: ValidMutIdempotenceFor<F, T, Option<U>, R, WAS_MUT>,
986 {
987 let f = crate::handoff_ref::with_ref_capture(|| {
988 let (expr, proof) =
989 f.splice_fnmut1_ctx_props(&OperatorContext::<L, B>::new(&self.location));
990 proof.register_proof(&expr);
991 expr.into()
992 });
993 Stream::new(
994 self.location.clone(),
995 HydroNode::FilterMap {
996 f,
997 input: Box::new(self.ir_node.replace(HydroNode::Placeholder)),
998 metadata: self
999 .location
1000 .new_node_metadata(Stream::<U, L, B, O, R>::collection_kind()),
1001 },
1002 )
1003 }
1004
1005 pub fn cross_singleton<O2>(
1030 self,
1031 other: impl Into<Optional<O2, L, Bounded>>,
1032 ) -> Stream<(T, O2), L, B, O, R>
1033 where
1034 O2: Clone,
1035 {
1036 let other: Optional<O2, L, Bounded> = other.into();
1037 check_matching_location(&self.location, &other.location);
1038
1039 Stream::new(
1040 self.location.clone(),
1041 HydroNode::CrossSingleton {
1042 left: Box::new(self.ir_node.replace(HydroNode::Placeholder)),
1043 right: Box::new(other.ir_node.replace(HydroNode::Placeholder)),
1044 metadata: self
1045 .location
1046 .new_node_metadata(Stream::<(T, O2), L, B, O, R>::collection_kind()),
1047 },
1048 )
1049 }
1050
1051 pub fn filter_if(self, signal: Singleton<bool, L, Bounded>) -> Stream<T, L, B, O, R> {
1083 self.cross_singleton(signal.filter(q!(|b| *b)))
1084 .map(q!(|(d, _)| d))
1085 }
1086
1087 #[deprecated(note = "use `filter_if` with `Optional::is_some()` instead")]
1122 pub fn filter_if_some<U>(self, signal: Optional<U, L, Bounded>) -> Stream<T, L, B, O, R> {
1123 self.filter_if(signal.is_some())
1124 }
1125
1126 #[deprecated(note = "use `filter_if` with `!Optional::is_some()` instead")]
1161 pub fn filter_if_none<U>(self, other: Optional<U, L, Bounded>) -> Stream<T, L, B, O, R> {
1162 self.filter_if(other.is_none())
1163 }
1164
1165 pub fn cross_product<T2, B2: Boundedness, O2: Ordering, R2: Retries>(
1190 self,
1191 other: Stream<T2, L, B2, O2, R2>,
1192 ) -> Stream<(T, T2), L, B, B2::PreserveOrderIfBounded<O>, <R as MinRetries<R2>>::Min>
1193 where
1194 T: Clone,
1195 T2: Clone,
1196 R: MinRetries<R2>,
1197 {
1198 self.map(q!(|v| ((), v)))
1199 .join(other.map(q!(|v| ((), v))))
1200 .map(q!(|((), (v1, v2))| (v1, v2)))
1201 }
1202
1203 pub fn unique(self) -> Stream<T, L, B, O, ExactlyOnce>
1222 where
1223 T: Eq + Hash,
1224 {
1225 Stream::new(
1226 self.location.clone(),
1227 HydroNode::Unique {
1228 input: Box::new(self.ir_node.replace(HydroNode::Placeholder)),
1229 metadata: self
1230 .location
1231 .new_node_metadata(Stream::<T, L, B, O, ExactlyOnce>::collection_kind()),
1232 },
1233 )
1234 }
1235
1236 pub fn filter_not_in<O2: Ordering, B2>(self, other: Stream<T, L, B2, O2, R>) -> Self
1262 where
1263 T: Eq + Hash,
1264 B2: IsBounded,
1265 {
1266 check_matching_location(&self.location, &other.location);
1267
1268 Stream::new(
1269 self.location.clone(),
1270 HydroNode::Difference {
1271 pos: Box::new(self.ir_node.replace(HydroNode::Placeholder)),
1272 neg: Box::new(other.ir_node.replace(HydroNode::Placeholder)),
1273 metadata: self
1274 .location
1275 .new_node_metadata(Stream::<T, L, Bounded, O, R>::collection_kind()),
1276 },
1277 )
1278 }
1279
1280 pub fn inspect<F, C, Idemp, const WAS_MUT: bool>(
1306 self,
1307 f: impl IntoQuotedMut<
1308 'a,
1309 F,
1310 OperatorContext<L::DropConsistency, B>,
1311 StreamMapFuncAlgebra<T, B, C, Idemp>,
1312 >,
1313 ) -> Self
1314 where
1315 F: FnMut(&T) + 'a,
1316 C: ValidMutBorrowCommutativityFor<F, T, (), O, WAS_MUT>,
1317 Idemp: ValidMutBorrowIdempotenceFor<F, T, (), R, WAS_MUT>,
1318 {
1319 let f = crate::handoff_ref::with_ref_capture(|| {
1320 let (expr, proof) =
1321 f.splice_fnmut1_borrow_ctx_props(&OperatorContext::<L::DropConsistency, B>::new(
1322 &self.location.drop_consistency(),
1323 ));
1324 proof.register_proof(&expr);
1325 expr.into()
1326 });
1327
1328 Stream::new(
1329 self.location.clone(),
1330 HydroNode::Inspect {
1331 f,
1332 input: Box::new(self.ir_node.replace(HydroNode::Placeholder)),
1333 metadata: self.location.new_node_metadata(Self::collection_kind()),
1334 },
1335 )
1336 }
1337
1338 pub fn for_each<F: FnMut(T) + 'a, C, I>(
1360 self,
1361 f: impl IntoQuotedMut<'a, F, OperatorContext<L, B>, AggFuncAlgebra<T, B, C, I>>,
1362 ) where
1363 C: ValidCommutativityFor<O>,
1364 I: ValidIdempotenceFor<R>,
1365 {
1366 let f = crate::handoff_ref::with_ref_capture(|| {
1367 let (f, proof) =
1368 f.splice_fnmut1_ctx_props(&OperatorContext::<L, B>::new(&self.location));
1369 proof.register_proof(&f);
1370 f.into()
1371 });
1372 self.location
1373 .flow_state()
1374 .borrow_mut()
1375 .push_root(HydroRoot::ForEach {
1376 input: Box::new(self.ir_node.replace(HydroNode::Placeholder)),
1377 f,
1378 op_metadata: HydroIrOpMetadata::new(),
1379 });
1380 }
1381
1382 pub fn dest_sink<S>(self, sink: impl QuotedWithContext<'a, S, L>)
1388 where
1389 O: IsOrdered,
1390 R: IsExactlyOnce,
1391 S: 'a + futures::Sink<T> + Unpin,
1392 {
1393 self.location
1394 .flow_state()
1395 .borrow_mut()
1396 .push_root(HydroRoot::DestSink {
1397 sink: sink.splice_typed_ctx(&self.location).into(),
1398 input: Box::new(self.ir_node.replace(HydroNode::Placeholder)),
1399 op_metadata: HydroIrOpMetadata::new(),
1400 });
1401 }
1402
1403 pub fn enumerate(self) -> Stream<(usize, T), L, B, O, R>
1423 where
1424 O: IsOrdered,
1425 R: IsExactlyOnce,
1426 {
1427 Stream::new(
1428 self.location.clone(),
1429 HydroNode::Enumerate {
1430 input: Box::new(self.ir_node.replace(HydroNode::Placeholder)),
1431 metadata: self.location.new_node_metadata(Stream::<
1432 (usize, T),
1433 L,
1434 B,
1435 TotalOrder,
1436 ExactlyOnce,
1437 >::collection_kind()),
1438 },
1439 )
1440 }
1441
1442 pub fn fold<A, I, F, C, Idemp, M, B2: SingletonBound>(
1466 self,
1467 init: impl IntoQuotedMut<'a, I, OperatorContext<L, B>>,
1468 comb: impl IntoQuotedMut<'a, F, OperatorContext<L, B>, AggFuncAlgebra<T, B, C, Idemp, M>>,
1469 ) -> Singleton<A, L, B2>
1470 where
1471 I: Fn() -> A + 'a,
1472 F: 'a + Fn(&mut A, T),
1473 C: ValidCommutativityFor<O>,
1474 Idemp: ValidIdempotenceFor<R>,
1475 B: ApplyMonotoneStream<M, B2>,
1476 {
1477 let init = init
1478 .splice_fn0_ctx(&OperatorContext::<L, B>::new(&self.location))
1479 .into();
1480 let (comb, proof) =
1481 comb.splice_fn2_borrow_mut_ctx_props(&OperatorContext::<L, B>::new(&self.location));
1482 let ordering_hook = proof.register_proof(&comb);
1483
1484 let nondet = nondet!();
1488 let retried: Stream<T, L::DropConsistency, B, O, ExactlyOnce> = self.assume_retries(nondet);
1489
1490 let mut metadata = retried
1491 .location
1492 .new_node_metadata(Singleton::<A, L::DropConsistency, B2>::collection_kind());
1493 metadata.op.sim_hook_id = ordering_hook.map(|hook| hook.id);
1494
1495 let core = HydroNode::Fold {
1496 init,
1497 acc: comb.into(),
1498 input: Box::new(retried.ir_node.replace(HydroNode::Placeholder)),
1499 metadata,
1500 };
1505
1506 Singleton::new(retried.location.clone(), core)
1507 .assert_has_consistency_of(manual_proof!())
1508 }
1509
1510 pub fn reduce<F, C, Idemp>(
1533 self,
1534 comb: impl IntoQuotedMut<'a, F, OperatorContext<L, B>, AggFuncAlgebra<T, B, C, Idemp>>,
1535 ) -> Optional<T, L, B::AggregatedOptional>
1536 where
1537 F: Fn(&mut T, T) + 'a,
1538 C: ValidCommutativityFor<O>,
1539 Idemp: ValidIdempotenceFor<R>,
1540 {
1541 let (f, proof) =
1542 comb.splice_fn2_borrow_mut_ctx_props(&OperatorContext::<L, B>::new(&self.location));
1543 let ordering_hook = proof.register_proof(&f);
1544
1545 let nondet_retries = nondet!();
1546 let ordered_etc: Stream<T, L::DropConsistency, B> =
1547 self.assume_retries(nondet_retries).assume_ordering(nondet!(
1548 hook = ordering_hook
1551 ));
1552
1553 let core = HydroNode::Reduce {
1554 f: f.into(),
1555 input: Box::new(ordered_etc.ir_node.replace(HydroNode::Placeholder)),
1556 metadata: ordered_etc.location.new_node_metadata(Optional::<
1557 T,
1558 L::DropConsistency,
1559 B::AggregatedOptional,
1560 >::collection_kind()),
1561 };
1562
1563 Optional::new(ordered_etc.location.clone(), core)
1564 .assert_has_consistency_of(manual_proof!())
1565 }
1566
1567 pub fn max(self) -> Optional<T, L, B::AggregatedOptional>
1587 where
1588 T: Ord,
1589 {
1590 self.assume_retries_trusted::<ExactlyOnce>(nondet!())
1591 .assume_ordering_trusted_bounded::<TotalOrder>(
1592 nondet!(),
1593 )
1594 .reduce(q!(|curr, new| {
1595 if new > *curr {
1596 *curr = new;
1597 }
1598 }))
1599 }
1600
1601 pub fn min(self) -> Optional<T, L, B::AggregatedOptional>
1621 where
1622 T: Ord,
1623 {
1624 self.assume_retries_trusted::<ExactlyOnce>(nondet!())
1625 .assume_ordering_trusted_bounded::<TotalOrder>(
1626 nondet!(),
1627 )
1628 .reduce(q!(|curr, new| {
1629 if new < *curr {
1630 *curr = new;
1631 }
1632 }))
1633 }
1634
1635 pub fn first(self) -> Optional<T, L, B::AggregatedOptional>
1658 where
1659 O: IsOrdered,
1660 {
1661 self.make_totally_ordered()
1662 .assume_retries_trusted::<ExactlyOnce>(nondet!())
1663 .generator(q!(|| ()), q!(|_, item| Generate::Return(item)))
1664 .reduce(q!(|_, _| {}))
1665 }
1666
1667 pub fn last(self) -> Optional<T, L, B::AggregatedOptional>
1690 where
1691 O: IsOrdered,
1692 {
1693 self.make_totally_ordered()
1694 .assume_retries_trusted::<ExactlyOnce>(nondet!())
1695 .reduce(q!(|curr, new| *curr = new))
1696 }
1697
1698 pub fn limit(
1721 self,
1722 n: impl QuotedWithContext<'a, usize, OperatorContext<L, B>> + Copy + 'a,
1723 ) -> Stream<T, L, B, TotalOrder, ExactlyOnce>
1724 where
1725 O: IsOrdered,
1726 R: IsExactlyOnce,
1727 {
1728 self.generator(
1729 q!(|| 0usize),
1730 q!(move |count, item| {
1731 if *count == n {
1732 Generate::Break
1733 } else {
1734 *count += 1;
1735 if *count == n {
1736 Generate::Return(item)
1737 } else {
1738 Generate::Yield(item)
1739 }
1740 }
1741 }),
1742 )
1743 }
1744
1745 pub fn collect_vec(self) -> Singleton<Vec<T>, L, B>
1771 where
1772 O: IsOrdered,
1773 R: IsExactlyOnce,
1774 {
1775 self.make_totally_ordered().make_exactly_once().fold(
1776 q!(|| vec![]),
1777 q!(|acc, v| {
1778 acc.push(v);
1779 }),
1780 )
1781 }
1782
1783 pub fn scan<A, U, I, F>(
1849 self,
1850 init: impl IntoQuotedMut<'a, I, OperatorContext<L, B>>,
1851 f: impl IntoQuotedMut<'a, F, OperatorContext<L, B>>,
1852 ) -> Stream<U, L, B, TotalOrder, ExactlyOnce>
1853 where
1854 O: IsOrdered,
1855 R: IsExactlyOnce,
1856 I: Fn() -> A + 'a,
1857 F: Fn(&mut A, T) -> Option<U> + 'a,
1858 {
1859 let init = crate::handoff_ref::with_ref_capture(|| {
1860 init.splice_fn0_ctx(&OperatorContext::<L, B>::new(&self.location))
1861 .into()
1862 });
1863 let f = crate::handoff_ref::with_ref_capture(|| {
1864 f.splice_fn2_borrow_mut_ctx(&OperatorContext::<L, B>::new(&self.location))
1865 .into()
1866 });
1867
1868 Stream::new(
1869 self.location.clone(),
1870 HydroNode::Scan {
1871 init,
1872 acc: f,
1873 input: Box::new(self.ir_node.replace(HydroNode::Placeholder)),
1874 metadata: self.location.new_node_metadata(
1875 Stream::<U, L, B, TotalOrder, ExactlyOnce>::collection_kind(),
1876 ),
1877 },
1878 )
1879 }
1880
1881 pub fn scan_async_blocking<A, U, I, F, Fut>(
1920 self,
1921 init: impl IntoQuotedMut<'a, I, OperatorContext<L, B>>,
1922 f: impl IntoQuotedMut<'a, F, OperatorContext<L, B>>,
1923 ) -> Stream<U, L, B, TotalOrder, ExactlyOnce>
1924 where
1925 O: IsOrdered,
1926 R: IsExactlyOnce,
1927 I: Fn() -> A + 'a,
1928 F: Fn(&mut A, T) -> Fut + 'a,
1929 Fut: Future<Output = Option<U>> + 'a,
1930 {
1931 let init = crate::handoff_ref::with_ref_capture(|| {
1932 init.splice_fn0_ctx(&OperatorContext::<L, B>::new(&self.location))
1933 .into()
1934 });
1935 let f = crate::handoff_ref::with_ref_capture(|| {
1936 f.splice_fn2_borrow_mut_ctx(&OperatorContext::<L, B>::new(&self.location))
1937 .into()
1938 });
1939
1940 Stream::new(
1941 self.location.clone(),
1942 HydroNode::ScanAsyncBlocking {
1943 init,
1944 acc: f,
1945 input: Box::new(self.ir_node.replace(HydroNode::Placeholder)),
1946 metadata: self.location.new_node_metadata(
1947 Stream::<U, L, B, TotalOrder, ExactlyOnce>::collection_kind(),
1948 ),
1949 },
1950 )
1951 }
1952
1953 pub fn generator<A, U, I, F>(
1998 self,
1999 init: impl IntoQuotedMut<'a, I, OperatorContext<L, B>> + Copy,
2000 f: impl IntoQuotedMut<'a, F, OperatorContext<L, B>> + Copy,
2001 ) -> Stream<U, L, B, TotalOrder, ExactlyOnce>
2002 where
2003 O: IsOrdered,
2004 R: IsExactlyOnce,
2005 I: Fn() -> A + 'a,
2006 F: Fn(&mut A, T) -> Generate<U> + 'a,
2007 {
2008 let init: ManualExpr<I, _> =
2009 ManualExpr::new(move |ctx: &OperatorContext<L, B>| init.splice_fn0_ctx(ctx));
2010 let f: ManualExpr<F, _> =
2011 ManualExpr::new(move |ctx: &OperatorContext<L, B>| f.splice_fn2_borrow_mut_ctx(ctx));
2012
2013 let this = self.make_totally_ordered().make_exactly_once();
2014
2015 let scan_init = crate::handoff_ref::with_ref_capture(|| {
2020 q!(|| None)
2021 .splice_fn0_ctx::<Option<Option<A>>>(&this.location)
2022 .into()
2023 });
2024 let scan_f = crate::handoff_ref::with_ref_capture(|| {
2025 q!(move |state: &mut Option<Option<_>>, v| {
2026 if state.is_none() {
2027 *state = Some(Some(init()));
2028 }
2029 match state {
2030 Some(Some(state_value)) => match f(state_value, v) {
2031 Generate::Yield(out) => Some(Some(out)),
2032 Generate::Return(out) => {
2033 *state = Some(None);
2034 Some(Some(out))
2035 }
2036 Generate::Break => None,
2040 Generate::Continue => Some(None),
2041 },
2042 _ => None,
2044 }
2045 })
2046 .splice_fn2_borrow_mut_ctx::<Option<Option<A>>, T, _>(&OperatorContext::<L, B>::new(
2047 &this.location,
2048 ))
2049 .into()
2050 });
2051
2052 let scan_node = HydroNode::Scan {
2053 init: scan_init,
2054 acc: scan_f,
2055 input: Box::new(this.ir_node.replace(HydroNode::Placeholder)),
2056 metadata: this.location.new_node_metadata(Stream::<
2057 Option<U>,
2058 L,
2059 B,
2060 TotalOrder,
2061 ExactlyOnce,
2062 >::collection_kind()),
2063 };
2064
2065 let flatten_f = q!(|d| d)
2066 .splice_fn1_ctx::<Option<U>, _>(&this.location)
2067 .into();
2068 let flatten_node = HydroNode::FlatMap {
2069 f: flatten_f,
2070 input: Box::new(scan_node),
2071 metadata: this
2072 .location
2073 .new_node_metadata(Stream::<U, L, B, TotalOrder, ExactlyOnce>::collection_kind()),
2074 };
2075
2076 Stream::new(this.location.clone(), flatten_node)
2077 }
2078
2079 #[cfg(feature = "tokio")]
2092 pub fn sample_every(
2093 self,
2094 interval: impl QuotedWithContext<'a, std::time::Duration, L> + Copy + 'a,
2095 mut nondet: NonDet<(
2096 Option<crate::sim_hooks::BatchHook<T, O, R>>,
2097 Option<crate::sim_hooks::BatchHook<()>>,
2098 )>,
2099 ) -> Stream<T, L::DropConsistency, Unbounded, O, AtLeastOnce>
2100 where
2101 L: TopLevel<'a>,
2102 {
2103 let samples = self.location.source_interval(interval);
2104 let (elements_hook, samples_hook) = nondet.take_hook();
2105
2106 let tick = self.location.tick();
2107 self.batch(
2108 &tick,
2109 nondet!(
2110 hook = elements_hook
2112 ),
2113 )
2114 .filter_if(
2115 samples
2116 .batch(
2117 &tick,
2118 nondet!(
2119 hook = samples_hook
2121 ),
2122 )
2123 .first()
2124 .is_some(),
2125 )
2126 .all_ticks()
2127 .weaken_retries()
2128 }
2129
2130 #[cfg(feature = "tokio")]
2140 pub fn timeout(
2141 self,
2142 duration: impl QuotedWithContext<
2143 'a,
2144 std::time::Duration,
2145 OperatorContext<Tick<L::DropConsistency>, Bounded>,
2146 > + Copy
2147 + 'a,
2148 nondet: NonDet,
2149 ) -> Optional<(), L::DropConsistency, Unbounded>
2150 where
2151 L: TopLevel<'a>,
2152 {
2153 let tick = self.location.tick();
2154
2155 let latest_received = self.assume_retries::<ExactlyOnce>(nondet).fold(
2156 q!(|| None),
2157 q!(
2158 |latest, _| {
2159 *latest = Some(Instant::now());
2160 },
2161 commutative = manual_proof!()
2162 ),
2163 );
2164
2165 latest_received
2166 .snapshot(
2167 &tick,
2168 nondet!(
2169 nondet
2171 ),
2172 )
2173 .filter_map(q!(move |latest_received| {
2174 if let Some(latest_received) = latest_received {
2175 if Instant::now().duration_since(latest_received) > duration {
2176 Some(())
2177 } else {
2178 None
2179 }
2180 } else {
2181 Some(())
2182 }
2183 }))
2184 .latest()
2185 }
2186
2187 pub fn atomic(self) -> Stream<T, Atomic<L>, B, O, R>
2193 where
2194 L: TopLevel<'a>,
2195 {
2196 let out_location = Atomic {
2197 tick: self.location.tick(),
2198 };
2199 Stream::new(
2200 out_location.clone(),
2201 HydroNode::BeginAtomic {
2202 inner: Box::new(self.ir_node.replace(HydroNode::Placeholder)),
2203 metadata: out_location
2204 .new_node_metadata(Stream::<T, Atomic<L>, B, O, R>::collection_kind()),
2205 },
2206 )
2207 }
2208
2209 pub fn batch<L2: Location<'a, DropConsistency = L::DropConsistency>>(
2221 self,
2222 tick: &Tick<L2>,
2223 mut nondet: NonDet<Option<crate::sim_hooks::BatchHook<T, O, R>>>,
2224 ) -> Stream<T, Tick<L::DropConsistency>, Bounded, O, R> {
2225 assert_eq!(
2226 Location::id(tick.parent_location()),
2227 Location::id(&self.location)
2228 );
2229
2230 let mut metadata =
2231 tick.new_node_metadata(Stream::<T, Tick<L>, Bounded, O, R>::collection_kind());
2232 metadata.op.sim_hook_id = nondet.take_hook().map(|h| h.id);
2233 Stream::new(
2234 tick.drop_consistency(),
2235 HydroNode::Batch {
2236 inner: Box::new(self.ir_node.replace(HydroNode::Placeholder)),
2237 metadata,
2238 },
2239 )
2240 }
2241
2242 pub fn ir_node_named(self, name: &str) -> Stream<T, L, B, O, R> {
2245 {
2246 let mut node = self.ir_node.borrow_mut();
2247 let metadata = node.metadata_mut();
2248 metadata.tag = Some(name.to_owned());
2249 }
2250 self
2251 }
2252
2253 pub(crate) fn cast_at_most_one_element(self) -> Optional<T, L, B>
2257 where
2258 B: IsBounded,
2259 {
2260 Optional::new(
2261 self.location.clone(),
2262 HydroNode::Cast {
2263 inner: Box::new(self.ir_node.replace(HydroNode::Placeholder)),
2264 metadata: self
2265 .location
2266 .new_node_metadata(Optional::<T, L, B>::collection_kind()),
2267 },
2268 )
2269 }
2270
2271 pub(crate) fn use_ordering_type<O2: Ordering>(self) -> Stream<T, L, B, O2, R> {
2272 if O::ORDERING_KIND == O2::ORDERING_KIND {
2273 Stream::new(
2274 self.location.clone(),
2275 self.ir_node.replace(HydroNode::Placeholder),
2276 )
2277 } else {
2278 panic!(
2279 "Runtime ordering {:?} did not match requested cast {:?}.",
2280 O::ORDERING_KIND,
2281 O2::ORDERING_KIND
2282 )
2283 }
2284 }
2285
2286 pub fn assume_ordering<O2: Ordering>(
2295 self,
2296 mut nondet: NonDet<Option<crate::sim_hooks::OrderingHook<T, B>>>,
2297 ) -> Stream<T, L::DropConsistency, B, O2, R> {
2298 if O::ORDERING_KIND == O2::ORDERING_KIND {
2299 self.use_ordering_type().weaken_consistency()
2300 } else if O2::ORDERING_KIND == StreamOrder::NoOrder {
2301 let target_location = self.location().drop_consistency();
2303 Stream::new(
2304 target_location.clone(),
2305 HydroNode::Cast {
2306 inner: Box::new(self.ir_node.replace(HydroNode::Placeholder)),
2307 metadata: target_location
2308 .new_node_metadata(Stream::<T, L, B, O2, R>::collection_kind()),
2309 },
2310 )
2311 } else {
2312 let target_location = self.location().drop_consistency();
2313 let mut metadata =
2314 target_location.new_node_metadata(Stream::<T, L, B, O2, R>::collection_kind());
2315 metadata.op.sim_hook_id = nondet.take_hook().map(|hook| hook.id);
2316 Stream::new(
2317 target_location,
2318 HydroNode::ObserveNonDet {
2319 inner: Box::new(self.ir_node.replace(HydroNode::Placeholder)),
2320 trusted: false,
2321 metadata,
2322 },
2323 )
2324 }
2325 }
2326
2327 fn assume_ordering_trusted_bounded<O2: Ordering>(
2330 self,
2331 nondet: NonDet,
2332 ) -> Stream<T, L, B, O2, R> {
2333 if B::BOUNDED {
2334 self.assume_ordering_trusted(nondet)
2335 } else {
2336 let self_location = self.location.clone();
2337 let inner: Stream<T, L::DropConsistency, B, O2, R> = self.assume_ordering(nondet!(
2338 nondet
2340 ));
2341 Stream::new(self_location, inner.ir_node.replace(HydroNode::Placeholder))
2342 }
2343 }
2344
2345 pub(crate) fn assume_ordering_trusted<O2: Ordering>(
2348 self,
2349 _nondet: NonDet,
2350 ) -> Stream<T, L, B, O2, R> {
2351 if O::ORDERING_KIND == O2::ORDERING_KIND {
2352 self.use_ordering_type()
2353 } else if O2::ORDERING_KIND == StreamOrder::NoOrder {
2354 Stream::new(
2356 self.location.clone(),
2357 HydroNode::Cast {
2358 inner: Box::new(self.ir_node.replace(HydroNode::Placeholder)),
2359 metadata: self
2360 .location
2361 .new_node_metadata(Stream::<T, L, B, O2, R>::collection_kind()),
2362 },
2363 )
2364 } else {
2365 Stream::new(
2366 self.location.clone(),
2367 HydroNode::ObserveNonDet {
2368 inner: Box::new(self.ir_node.replace(HydroNode::Placeholder)),
2369 trusted: true,
2370 metadata: self
2371 .location
2372 .new_node_metadata(Stream::<T, L, B, O2, R>::collection_kind()),
2373 },
2374 )
2375 }
2376 }
2377
2378 #[deprecated = "use `weaken_ordering::<NoOrder>()` instead"]
2379 pub fn weakest_ordering(self) -> Stream<T, L, B, NoOrder, R> {
2382 self.weaken_ordering::<NoOrder>()
2383 }
2384
2385 pub fn weaken_ordering<O2: WeakerOrderingThan<O>>(self) -> Stream<T, L, B, O2, R> {
2388 let nondet = nondet!();
2389 self.assume_ordering_trusted::<O2>(nondet)
2390 }
2391
2392 pub fn make_totally_ordered(self) -> Stream<T, L, B, TotalOrder, R>
2395 where
2396 O: IsOrdered,
2397 {
2398 self.assume_ordering_trusted(nondet!())
2399 }
2400
2401 pub fn assume_retries<R2: Retries>(
2410 self,
2411 _nondet: NonDet,
2412 ) -> Stream<T, L::DropConsistency, B, O, R2> {
2413 if R::RETRIES_KIND == R2::RETRIES_KIND {
2414 Stream::new(
2415 self.location.drop_consistency(),
2416 self.ir_node.replace(HydroNode::Placeholder),
2417 )
2418 } else if R2::RETRIES_KIND == StreamRetry::AtLeastOnce {
2419 let target_location = self.location.drop_consistency();
2421 Stream::new(
2422 target_location.clone(),
2423 HydroNode::Cast {
2424 inner: Box::new(self.ir_node.replace(HydroNode::Placeholder)),
2425 metadata: target_location
2426 .new_node_metadata(Stream::<T, L, B, O, R2>::collection_kind()),
2427 },
2428 )
2429 } else {
2430 let target_location = self.location.drop_consistency();
2431 Stream::new(
2432 target_location.clone(),
2433 HydroNode::ObserveNonDet {
2434 inner: Box::new(self.ir_node.replace(HydroNode::Placeholder)),
2435 trusted: false,
2436 metadata: target_location
2437 .new_node_metadata(Stream::<T, L, B, O, R2>::collection_kind()),
2438 },
2439 )
2440 }
2441 }
2442
2443 fn assume_retries_trusted<R2: Retries>(self, _nondet: NonDet) -> Stream<T, L, B, O, R2> {
2446 if R::RETRIES_KIND == R2::RETRIES_KIND {
2447 Stream::new(
2448 self.location.clone(),
2449 self.ir_node.replace(HydroNode::Placeholder),
2450 )
2451 } else if R2::RETRIES_KIND == StreamRetry::AtLeastOnce {
2452 Stream::new(
2454 self.location.clone(),
2455 HydroNode::Cast {
2456 inner: Box::new(self.ir_node.replace(HydroNode::Placeholder)),
2457 metadata: self
2458 .location
2459 .new_node_metadata(Stream::<T, L, B, O, R2>::collection_kind()),
2460 },
2461 )
2462 } else {
2463 Stream::new(
2464 self.location.clone(),
2465 HydroNode::ObserveNonDet {
2466 inner: Box::new(self.ir_node.replace(HydroNode::Placeholder)),
2467 trusted: true,
2468 metadata: self
2469 .location
2470 .new_node_metadata(Stream::<T, L, B, O, R2>::collection_kind()),
2471 },
2472 )
2473 }
2474 }
2475
2476 #[deprecated = "use `weaken_retries::<AtLeastOnce>()` instead"]
2477 pub fn weakest_retries(self) -> Stream<T, L, B, O, AtLeastOnce> {
2480 self.weaken_retries::<AtLeastOnce>()
2481 }
2482
2483 pub fn weaken_retries<R2: WeakerRetryThan<R>>(self) -> Stream<T, L, B, O, R2> {
2486 let nondet = nondet!();
2487 self.assume_retries_trusted::<R2>(nondet)
2488 }
2489
2490 pub fn make_exactly_once(self) -> Stream<T, L, B, O, ExactlyOnce>
2493 where
2494 R: IsExactlyOnce,
2495 {
2496 self.assume_retries_trusted(nondet!())
2497 }
2498
2499 pub fn make_bounded(self) -> Stream<T, L, Bounded, O, R>
2502 where
2503 B: IsBounded,
2504 {
2505 self.weaken_boundedness()
2506 }
2507
2508 pub fn weaken_boundedness<B2: Boundedness>(self) -> Stream<T, L, B2, O, R> {
2511 if B::BOUNDED == B2::BOUNDED {
2512 Stream::new(
2513 self.location.clone(),
2514 self.ir_node.replace(HydroNode::Placeholder),
2515 )
2516 } else {
2517 Stream::new(
2519 self.location.clone(),
2520 HydroNode::Cast {
2521 inner: Box::new(self.ir_node.replace(HydroNode::Placeholder)),
2522 metadata: self
2523 .location
2524 .new_node_metadata(Stream::<T, L, B2, O, R>::collection_kind()),
2525 },
2526 )
2527 }
2528 }
2529}
2530
2531impl<'a, T, L, B: Boundedness, O: Ordering, R: Retries> Stream<&T, L, B, O, R>
2532where
2533 L: Location<'a>,
2534{
2535 pub fn cloned(self) -> Stream<T, L, B, O, R>
2553 where
2554 T: Clone,
2555 {
2556 self.map(q!(|d| d.clone()))
2557 }
2558}
2559
2560impl<'a, T, L, B: Boundedness, O: Ordering> Stream<T, L, B, O, ExactlyOnce>
2561where
2562 L: Location<'a>,
2563{
2564 pub fn count(self) -> Singleton<usize, L, B::StreamToMonotone> {
2583 self.assume_ordering_trusted::<TotalOrder>(nondet!(
2584 ))
2586 .fold(
2587 q!(|| 0usize),
2588 q!(
2589 |count, _| *count += 1,
2590 monotone = manual_proof!()
2591 ),
2592 )
2593 }
2594}
2595
2596impl<'a, T, L: Location<'a>, O: Ordering, R: Retries> Stream<T, L, Unbounded, O, R> {
2597 pub fn merge_unordered<O2: Ordering, R2: Retries>(
2621 self,
2622 other: Stream<T, L, Unbounded, O2, R2>,
2623 ) -> Stream<T, L, Unbounded, NoOrder, <R as MinRetries<R2>>::Min>
2624 where
2625 R: MinRetries<R2>,
2626 {
2627 Stream::new(
2628 self.location.clone(),
2629 HydroNode::Chain {
2630 first: Box::new(self.ir_node.replace(HydroNode::Placeholder)),
2631 second: Box::new(other.ir_node.replace(HydroNode::Placeholder)),
2632 metadata: self.location.new_node_metadata(Stream::<
2633 T,
2634 L,
2635 Unbounded,
2636 NoOrder,
2637 <R as MinRetries<R2>>::Min,
2638 >::collection_kind()),
2639 },
2640 )
2641 }
2642
2643 #[deprecated(note = "use `merge_unordered` instead")]
2645 pub fn interleave<O2: Ordering, R2: Retries>(
2646 self,
2647 other: Stream<T, L, Unbounded, O2, R2>,
2648 ) -> Stream<T, L, Unbounded, NoOrder, <R as MinRetries<R2>>::Min>
2649 where
2650 R: MinRetries<R2>,
2651 {
2652 self.merge_unordered(other)
2653 }
2654}
2655
2656impl<'a, T, L: Location<'a>, B: Boundedness, R: Retries> Stream<T, L, B, TotalOrder, R> {
2657 pub fn merge_ordered<R2: Retries>(
2689 self,
2690 other: Stream<T, L, B, TotalOrder, R2>,
2691 mut nondet: NonDet<Option<crate::sim_hooks::MergeOrderedHook<T, B>>>,
2692 ) -> Stream<T, L::DropConsistency, B, TotalOrder, <R as MinRetries<R2>>::Min>
2693 where
2694 R: MinRetries<R2>,
2695 {
2696 let target_location = self.location().drop_consistency();
2697 let mut metadata = target_location.new_node_metadata(Stream::<
2698 T,
2699 L::DropConsistency,
2700 B,
2701 TotalOrder,
2702 <R as MinRetries<R2>>::Min,
2703 >::collection_kind());
2704 metadata.op.sim_hook_id = nondet.take_hook().map(|hook| hook.id);
2705 Stream::new(
2706 target_location,
2707 HydroNode::MergeOrdered {
2708 first: Box::new(self.ir_node.replace(HydroNode::Placeholder)),
2709 second: Box::new(other.ir_node.replace(HydroNode::Placeholder)),
2710 metadata,
2711 },
2712 )
2713 }
2714}
2715
2716impl<'a, T, L, B: Boundedness, O: Ordering, R: Retries> Stream<T, L, B, O, R>
2717where
2718 L: Location<'a>,
2719{
2720 pub fn sort(self) -> Stream<T, L, Bounded, TotalOrder, R>
2746 where
2747 B: IsBounded,
2748 T: Ord,
2749 {
2750 let this = self.make_bounded();
2751 Stream::new(
2752 this.location.clone(),
2753 HydroNode::Sort {
2754 input: Box::new(this.ir_node.replace(HydroNode::Placeholder)),
2755 metadata: this
2756 .location
2757 .new_node_metadata(Stream::<T, L, Bounded, TotalOrder, R>::collection_kind()),
2758 },
2759 )
2760 }
2761
2762 pub fn chain<O2: Ordering, R2: Retries, B2: Boundedness>(
2790 self,
2791 other: Stream<T, L, B2, O2, R2>,
2792 ) -> Stream<T, L, B2, <O as MinOrder<O2>>::Min, <R as MinRetries<R2>>::Min>
2793 where
2794 B: IsBounded,
2795 O: MinOrder<O2>,
2796 R: MinRetries<R2>,
2797 {
2798 check_matching_location(&self.location, &other.location);
2799
2800 Stream::new(
2801 self.location.clone(),
2802 HydroNode::Chain {
2803 first: Box::new(self.ir_node.replace(HydroNode::Placeholder)),
2804 second: Box::new(other.ir_node.replace(HydroNode::Placeholder)),
2805 metadata: self.location.new_node_metadata(Stream::<
2806 T,
2807 L,
2808 B2,
2809 <O as MinOrder<O2>>::Min,
2810 <R as MinRetries<R2>>::Min,
2811 >::collection_kind()),
2812 },
2813 )
2814 }
2815
2816 pub fn cross_product_nested_loop<T2, O2: Ordering + MinOrder<O>, R2: Retries>(
2820 self,
2821 other: Stream<T2, L, Bounded, O2, R2>,
2822 ) -> Stream<(T, T2), L, Bounded, <O2 as MinOrder<O>>::Min, <R as MinRetries<R2>>::Min>
2823 where
2824 B: IsBounded,
2825 T: Clone,
2826 T2: Clone,
2827 R: MinRetries<R2>,
2828 {
2829 let this = self.make_bounded();
2830 check_matching_location(&this.location, &other.location);
2831
2832 Stream::new(
2833 this.location.clone(),
2834 HydroNode::CrossProduct {
2835 left: Box::new(this.ir_node.replace(HydroNode::Placeholder)),
2836 right: Box::new(other.ir_node.replace(HydroNode::Placeholder)),
2837 metadata: this.location.new_node_metadata(Stream::<
2838 (T, T2),
2839 L,
2840 Bounded,
2841 <O2 as MinOrder<O>>::Min,
2842 <R as MinRetries<R2>>::Min,
2843 >::collection_kind()),
2844 },
2845 )
2846 }
2847
2848 pub fn repeat_with_keys<K, V2>(
2886 self,
2887 keys: KeyedSingleton<K, V2, L, Bounded>,
2888 ) -> KeyedStream<K, T, L, Bounded, O, R>
2889 where
2890 B: IsBounded,
2891 K: Clone,
2892 T: Clone,
2893 {
2894 keys.keys()
2895 .assume_ordering_trusted::<TotalOrder>(
2896 nondet!(),
2897 )
2898 .cross_product_nested_loop(self.make_bounded())
2899 .into_keyed()
2900 }
2901
2902 pub fn resolve_futures_blocking(self) -> Stream<T::Output, L, B, NoOrder, R>
2939 where
2940 T: Future,
2941 {
2942 Stream::new(
2943 self.location.clone(),
2944 HydroNode::ResolveFuturesBlocking {
2945 input: Box::new(self.ir_node.replace(HydroNode::Placeholder)),
2946 metadata: self
2947 .location
2948 .new_node_metadata(Stream::<T::Output, L, B, NoOrder, R>::collection_kind()),
2949 },
2950 )
2951 }
2952
2953 #[expect(clippy::wrong_self_convention, reason = "stream function naming")]
2973 pub fn is_empty(self) -> Singleton<bool, L, Bounded>
2974 where
2975 B: IsBounded,
2976 {
2977 self.make_bounded()
2978 .assume_ordering_trusted::<TotalOrder>(
2979 nondet!(),
2980 )
2981 .first()
2982 .is_none()
2983 }
2984}
2985
2986impl<'a, K, V1, L, B: Boundedness, O: Ordering, R: Retries> Stream<(K, V1), L, B, O, R>
2987where
2988 L: Location<'a>,
2989{
2990 pub fn join<V2, B2: Boundedness, O2: Ordering, R2: Retries>(
3015 self,
3016 n: Stream<(K, V2), L, B2, O2, R2>,
3017 ) -> Stream<(K, (V1, V2)), L, B, B2::PreserveOrderIfBounded<O>, <R as MinRetries<R2>>::Min>
3018 where
3019 K: Eq + Hash + Clone,
3020 R: MinRetries<R2>,
3021 V1: Clone,
3022 V2: Clone,
3023 {
3024 check_matching_location(&self.location, &n.location);
3025
3026 let ir_node = if B2::BOUNDED {
3027 HydroNode::JoinHalf {
3028 left: Box::new(self.ir_node.replace(HydroNode::Placeholder)),
3029 right: Box::new(n.ir_node.replace(HydroNode::Placeholder)),
3030 metadata: self.location.new_node_metadata(Stream::<
3031 (K, (V1, V2)),
3032 L,
3033 B,
3034 B2::PreserveOrderIfBounded<O>,
3035 <R as MinRetries<R2>>::Min,
3036 >::collection_kind()),
3037 }
3038 } else {
3039 HydroNode::Join {
3040 left: Box::new(self.ir_node.replace(HydroNode::Placeholder)),
3041 right: Box::new(n.ir_node.replace(HydroNode::Placeholder)),
3042 metadata: self.location.new_node_metadata(Stream::<
3043 (K, (V1, V2)),
3044 L,
3045 B,
3046 B2::PreserveOrderIfBounded<O>,
3047 <R as MinRetries<R2>>::Min,
3048 >::collection_kind()),
3049 }
3050 };
3051
3052 Stream::new(self.location.clone(), ir_node)
3053 }
3054
3055 pub fn anti_join<O2: Ordering, R2: Retries>(
3081 self,
3082 n: Stream<K, L, Bounded, O2, R2>,
3083 ) -> Stream<(K, V1), L, B, O, R>
3084 where
3085 K: Eq + Hash,
3086 {
3087 check_matching_location(&self.location, &n.location);
3088
3089 Stream::new(
3090 self.location.clone(),
3091 HydroNode::AntiJoin {
3092 pos: Box::new(self.ir_node.replace(HydroNode::Placeholder)),
3093 neg: Box::new(n.ir_node.replace(HydroNode::Placeholder)),
3094 metadata: self
3095 .location
3096 .new_node_metadata(Stream::<(K, V1), L, B, O, R>::collection_kind()),
3097 },
3098 )
3099 }
3100}
3101
3102impl<'a, K, V, L: Location<'a>, B: Boundedness, O: Ordering, R: Retries>
3103 Stream<(K, V), L, B, O, R>
3104{
3105 pub fn into_keyed(self) -> KeyedStream<K, V, L, B, O, R> {
3132 KeyedStream::new(
3133 self.location.clone(),
3134 HydroNode::Cast {
3135 inner: Box::new(self.ir_node.replace(HydroNode::Placeholder)),
3136 metadata: self
3137 .location
3138 .new_node_metadata(KeyedStream::<K, V, L, B, O, R>::collection_kind()),
3139 },
3140 )
3141 }
3142}
3143
3144impl<'a, K, V, L, O: Ordering, R: Retries> Stream<(K, V), Tick<L>, Bounded, O, R>
3145where
3146 K: Eq + Hash,
3147 L: Location<'a>,
3148{
3149 pub fn keys(self) -> Stream<K, Tick<L>, Bounded, NoOrder, ExactlyOnce> {
3168 self.into_keyed()
3169 .fold(
3170 q!(|| ()),
3171 q!(
3172 |_, _| {},
3173 commutative = manual_proof!(),
3174 idempotent = manual_proof!()
3175 ),
3176 )
3177 .keys()
3178 }
3179}
3180
3181impl<'a, T, L, B: Boundedness, O: Ordering, R: Retries> Stream<T, Atomic<L>, B, O, R>
3182where
3183 L: Location<'a>,
3184{
3185 pub fn batch_atomic<L2: Location<'a, DropConsistency = L::DropConsistency>>(
3192 self,
3193 tick: &Tick<L2>,
3194 mut nondet: NonDet<Option<crate::sim_hooks::BatchHook<T, O, R>>>,
3195 ) -> Stream<T, Tick<L::DropConsistency>, Bounded, O, R> {
3196 assert_eq!(
3197 Location::id(tick.parent_location()),
3198 Location::id(self.location.tick.parent_location())
3199 );
3200
3201 let mut metadata =
3202 tick.new_node_metadata(Stream::<T, Tick<L>, Bounded, O, R>::collection_kind());
3203
3204 metadata.op.sim_hook_id = nondet.take_hook().map(|h| h.id);
3205 Stream::new(
3206 tick.drop_consistency(),
3207 HydroNode::Batch {
3208 inner: Box::new(self.ir_node.replace(HydroNode::Placeholder)),
3209 metadata,
3210 },
3211 )
3212 }
3213
3214 pub fn end_atomic(self) -> Stream<T, L, B, O, R> {
3217 Stream::new(
3218 self.location.tick.l.clone(),
3219 HydroNode::EndAtomic {
3220 inner: Box::new(self.ir_node.replace(HydroNode::Placeholder)),
3221 metadata: self
3222 .location
3223 .tick
3224 .l
3225 .new_node_metadata(Stream::<T, L, B, O, R>::collection_kind()),
3226 },
3227 )
3228 }
3229}
3230
3231impl<'a, F, T, L, B: Boundedness, O: Ordering, R: Retries> Stream<F, L, B, O, R>
3232where
3233 L: TopLevel<'a>,
3234 F: Future<Output = T>,
3235{
3236 pub fn resolve_futures(self) -> Stream<T, L, Unbounded, NoOrder, R> {
3267 Stream::new(
3268 self.location.clone(),
3269 HydroNode::ResolveFutures {
3270 input: Box::new(self.ir_node.replace(HydroNode::Placeholder)),
3271 metadata: self
3272 .location
3273 .new_node_metadata(Stream::<T, L, Unbounded, NoOrder, R>::collection_kind()),
3274 },
3275 )
3276 }
3277
3278 pub fn resolve_futures_ordered(self) -> Stream<T, L, Unbounded, O, R> {
3309 Stream::new(
3310 self.location.clone(),
3311 HydroNode::ResolveFuturesOrdered {
3312 input: Box::new(self.ir_node.replace(HydroNode::Placeholder)),
3313 metadata: self
3314 .location
3315 .new_node_metadata(Stream::<T, L, Unbounded, O, R>::collection_kind()),
3316 },
3317 )
3318 }
3319}
3320
3321impl<'a, T, L, O: Ordering, R: Retries> Stream<T, Tick<L>, Bounded, O, R>
3322where
3323 L: Location<'a>,
3324{
3325 pub fn all_ticks(self) -> Stream<T, L, Unbounded, O, R> {
3328 Stream::new(
3329 self.location.parent_location().clone(),
3330 HydroNode::YieldConcat {
3331 inner: Box::new(self.ir_node.replace(HydroNode::Placeholder)),
3332 metadata: self.location.parent_location().new_node_metadata(Stream::<
3333 T,
3334 L,
3335 Unbounded,
3336 O,
3337 R,
3338 >::collection_kind(
3339 )),
3340 },
3341 )
3342 }
3343
3344 pub fn all_ticks_atomic(self) -> Stream<T, Atomic<L>, Unbounded, O, R> {
3351 let out_location = Atomic {
3352 tick: self.location.clone(),
3353 };
3354
3355 Stream::new(
3356 out_location.clone(),
3357 HydroNode::YieldConcat {
3358 inner: Box::new(self.ir_node.replace(HydroNode::Placeholder)),
3359 metadata: out_location
3360 .new_node_metadata(Stream::<T, Atomic<L>, Unbounded, O, R>::collection_kind()),
3361 },
3362 )
3363 }
3364
3365 pub fn across_ticks<Out: BatchAtomic<'a>>(
3400 self,
3401 thunk: impl FnOnce(Stream<T, Atomic<L>, Unbounded, O, R>) -> Out,
3402 ) -> Out::Batched {
3403 thunk(self.all_ticks_atomic()).batched_atomic()
3404 }
3405
3406 pub fn defer_tick(self) -> Stream<T, Tick<L>, Bounded, O, R> {
3445 Stream::new(
3446 self.location.clone(),
3447 HydroNode::DeferTick {
3448 input: Box::new(self.ir_node.replace(HydroNode::Placeholder)),
3449 metadata: self
3450 .location
3451 .new_node_metadata(Stream::<T, Tick<L>, Bounded, O, R>::collection_kind()),
3452 },
3453 )
3454 }
3455}
3456
3457#[cfg(test)]
3458mod tests {
3459 #[cfg(feature = "deploy")]
3460 use futures::{SinkExt, StreamExt};
3461 #[cfg(feature = "deploy")]
3462 use hydro_deploy::Deployment;
3463 #[cfg(feature = "deploy")]
3464 use serde::{Deserialize, Serialize};
3465 #[cfg(any(feature = "deploy", feature = "sim"))]
3466 use stageleft::q;
3467
3468 #[cfg(any(feature = "deploy", feature = "sim"))]
3469 use crate::compile::builder::FlowBuilder;
3470 #[cfg(feature = "deploy")]
3471 use crate::live_collections::sliced::sliced;
3472 #[cfg(feature = "deploy")]
3473 use crate::live_collections::stream::ExactlyOnce;
3474 #[cfg(feature = "sim")]
3475 use crate::live_collections::stream::NoOrder;
3476 #[cfg(any(feature = "deploy", feature = "sim"))]
3477 use crate::live_collections::stream::TotalOrder;
3478 #[cfg(any(feature = "deploy", feature = "sim"))]
3479 use crate::location::Location;
3480 #[cfg(feature = "sim")]
3481 use crate::networking::TCP;
3482 #[cfg(any(feature = "deploy", feature = "sim"))]
3483 use crate::nondet::nondet;
3484
3485 mod backtrace_chained_ops;
3486
3487 #[cfg(feature = "deploy")]
3488 struct P1 {}
3489 #[cfg(feature = "deploy")]
3490 struct P2 {}
3491
3492 #[cfg(feature = "deploy")]
3493 #[derive(Serialize, Deserialize, Debug)]
3494 struct SendOverNetwork {
3495 n: u32,
3496 }
3497
3498 #[cfg(feature = "deploy")]
3499 #[tokio::test]
3500 async fn first_ten_distributed() {
3501 use crate::networking::TCP;
3502
3503 let mut deployment = Deployment::new();
3504
3505 let mut flow = FlowBuilder::new();
3506 let first_node = flow.process::<P1>();
3507 let second_node = flow.process::<P2>();
3508 let external = flow.external::<P2>();
3509
3510 let numbers = first_node.source_iter(q!(0..10));
3511 let out_port = numbers
3512 .map(q!(|n| SendOverNetwork { n }))
3513 .send(&second_node, TCP.fail_stop().bincode())
3514 .send_bincode_external(&external);
3515
3516 let nodes = flow
3517 .with_process(&first_node, deployment.Localhost())
3518 .with_process(&second_node, deployment.Localhost())
3519 .with_external(&external, deployment.Localhost())
3520 .deploy(&mut deployment);
3521
3522 deployment.deploy().await.unwrap();
3523
3524 let mut external_out = nodes.connect(out_port).await;
3525
3526 deployment.start().await.unwrap();
3527
3528 for i in 0..10 {
3529 assert_eq!(external_out.next().await.unwrap().n, i);
3530 }
3531 }
3532
3533 #[cfg(feature = "deploy")]
3534 #[tokio::test]
3535 async fn first_cardinality() {
3536 let mut deployment = Deployment::new();
3537
3538 let mut flow = FlowBuilder::new();
3539 let node = flow.process::<()>();
3540 let external = flow.external::<()>();
3541
3542 let node_tick = node.tick();
3543 let count = node_tick
3544 .singleton(q!([1, 2, 3]))
3545 .into_stream()
3546 .flatten_ordered()
3547 .first()
3548 .into_stream()
3549 .count()
3550 .all_ticks()
3551 .send_bincode_external(&external);
3552
3553 let nodes = flow
3554 .with_process(&node, deployment.Localhost())
3555 .with_external(&external, deployment.Localhost())
3556 .deploy(&mut deployment);
3557
3558 deployment.deploy().await.unwrap();
3559
3560 let mut external_out = nodes.connect(count).await;
3561
3562 deployment.start().await.unwrap();
3563
3564 assert_eq!(external_out.next().await.unwrap(), 1);
3565 }
3566
3567 #[cfg(feature = "deploy")]
3568 #[tokio::test]
3569 async fn unbounded_reduce_remembers_state() {
3570 let mut deployment = Deployment::new();
3571
3572 let mut flow = FlowBuilder::new();
3573 let node = flow.process::<()>();
3574 let external = flow.external::<()>();
3575
3576 let (input_port, input) = node.source_external_bincode(&external);
3577 let out = input
3578 .reduce(q!(|acc, v| *acc += v))
3579 .sample_eager(nondet!())
3580 .send_bincode_external(&external);
3581
3582 let nodes = flow
3583 .with_process(&node, deployment.Localhost())
3584 .with_external(&external, deployment.Localhost())
3585 .deploy(&mut deployment);
3586
3587 deployment.deploy().await.unwrap();
3588
3589 let mut external_in = nodes.connect(input_port).await;
3590 let mut external_out = nodes.connect(out).await;
3591
3592 deployment.start().await.unwrap();
3593
3594 external_in.send(1).await.unwrap();
3595 assert_eq!(external_out.next().await.unwrap(), 1);
3596
3597 external_in.send(2).await.unwrap();
3598 assert_eq!(external_out.next().await.unwrap(), 3);
3599 }
3600
3601 #[cfg(feature = "deploy")]
3602 #[tokio::test]
3603 async fn top_level_bounded_cross_singleton() {
3604 let mut deployment = Deployment::new();
3605
3606 let mut flow = FlowBuilder::new();
3607 let node = flow.process::<()>();
3608 let external = flow.external::<()>();
3609
3610 let (input_port, input) =
3611 node.source_external_bincode::<_, _, TotalOrder, ExactlyOnce>(&external);
3612
3613 let out = input
3614 .cross_singleton(
3615 node.source_iter(q!(vec![1, 2, 3]))
3616 .fold(q!(|| 0), q!(|acc, v| *acc += v)),
3617 )
3618 .send_bincode_external(&external);
3619
3620 let nodes = flow
3621 .with_process(&node, deployment.Localhost())
3622 .with_external(&external, deployment.Localhost())
3623 .deploy(&mut deployment);
3624
3625 deployment.deploy().await.unwrap();
3626
3627 let mut external_in = nodes.connect(input_port).await;
3628 let mut external_out = nodes.connect(out).await;
3629
3630 deployment.start().await.unwrap();
3631
3632 external_in.send(1).await.unwrap();
3633 assert_eq!(external_out.next().await.unwrap(), (1, 6));
3634
3635 external_in.send(2).await.unwrap();
3636 assert_eq!(external_out.next().await.unwrap(), (2, 6));
3637 }
3638
3639 #[cfg(feature = "deploy")]
3640 #[tokio::test]
3641 async fn top_level_bounded_reduce_cardinality() {
3642 let mut deployment = Deployment::new();
3643
3644 let mut flow = FlowBuilder::new();
3645 let node = flow.process::<()>();
3646 let external = flow.external::<()>();
3647
3648 let (input_port, input) =
3649 node.source_external_bincode::<_, _, TotalOrder, ExactlyOnce>(&external);
3650
3651 let out = sliced! {
3652 let input = use::batch(input, nondet!());
3653 let v = use::snapshot(node.source_iter(q!(vec![1, 2, 3])).reduce(q!(|acc, v| *acc += v)), nondet!());
3654 input.cross_singleton(v.into_stream().count())
3655 }
3656 .send_bincode_external(&external);
3657
3658 let nodes = flow
3659 .with_process(&node, deployment.Localhost())
3660 .with_external(&external, deployment.Localhost())
3661 .deploy(&mut deployment);
3662
3663 deployment.deploy().await.unwrap();
3664
3665 let mut external_in = nodes.connect(input_port).await;
3666 let mut external_out = nodes.connect(out).await;
3667
3668 deployment.start().await.unwrap();
3669
3670 external_in.send(1).await.unwrap();
3671 assert_eq!(external_out.next().await.unwrap(), (1, 1));
3672
3673 external_in.send(2).await.unwrap();
3674 assert_eq!(external_out.next().await.unwrap(), (2, 1));
3675 }
3676
3677 #[cfg(feature = "deploy")]
3678 #[tokio::test]
3679 async fn top_level_bounded_into_singleton_cardinality() {
3680 let mut deployment = Deployment::new();
3681
3682 let mut flow = FlowBuilder::new();
3683 let node = flow.process::<()>();
3684 let external = flow.external::<()>();
3685
3686 let (input_port, input) =
3687 node.source_external_bincode::<_, _, TotalOrder, ExactlyOnce>(&external);
3688
3689 let out = sliced! {
3690 let input = use::batch(input, nondet!());
3691 let v = use::snapshot(node.source_iter(q!(vec![1, 2, 3])).reduce(q!(|acc, v| *acc += v)).into_singleton(), nondet!());
3692 input.cross_singleton(v.into_stream().count())
3693 }
3694 .send_bincode_external(&external);
3695
3696 let nodes = flow
3697 .with_process(&node, deployment.Localhost())
3698 .with_external(&external, deployment.Localhost())
3699 .deploy(&mut deployment);
3700
3701 deployment.deploy().await.unwrap();
3702
3703 let mut external_in = nodes.connect(input_port).await;
3704 let mut external_out = nodes.connect(out).await;
3705
3706 deployment.start().await.unwrap();
3707
3708 external_in.send(1).await.unwrap();
3709 assert_eq!(external_out.next().await.unwrap(), (1, 1));
3710
3711 external_in.send(2).await.unwrap();
3712 assert_eq!(external_out.next().await.unwrap(), (2, 1));
3713 }
3714
3715 #[cfg(feature = "deploy")]
3716 #[tokio::test]
3717 async fn atomic_fold_replays_each_tick() {
3718 let mut deployment = Deployment::new();
3719
3720 let mut flow = FlowBuilder::new();
3721 let node = flow.process::<()>();
3722 let external = flow.external::<()>();
3723
3724 let (input_port, input) =
3725 node.source_external_bincode::<_, _, TotalOrder, ExactlyOnce>(&external);
3726 let tick = node.tick();
3727
3728 let out = input
3729 .batch(&tick, nondet!())
3730 .cross_singleton(
3731 node.source_iter(q!(vec![1, 2, 3]))
3732 .atomic()
3733 .fold(q!(|| 0), q!(|acc, v| *acc += v))
3734 .snapshot_atomic(&tick, nondet!()),
3735 )
3736 .all_ticks()
3737 .send_bincode_external(&external);
3738
3739 let nodes = flow
3740 .with_process(&node, deployment.Localhost())
3741 .with_external(&external, deployment.Localhost())
3742 .deploy(&mut deployment);
3743
3744 deployment.deploy().await.unwrap();
3745
3746 let mut external_in = nodes.connect(input_port).await;
3747 let mut external_out = nodes.connect(out).await;
3748
3749 deployment.start().await.unwrap();
3750
3751 external_in.send(1).await.unwrap();
3752 assert_eq!(external_out.next().await.unwrap(), (1, 6));
3753
3754 external_in.send(2).await.unwrap();
3755 assert_eq!(external_out.next().await.unwrap(), (2, 6));
3756 }
3757
3758 #[cfg(feature = "deploy")]
3759 #[tokio::test]
3760 async fn unbounded_scan_remembers_state() {
3761 let mut deployment = Deployment::new();
3762
3763 let mut flow = FlowBuilder::new();
3764 let node = flow.process::<()>();
3765 let external = flow.external::<()>();
3766
3767 let (input_port, input) = node.source_external_bincode(&external);
3768 let out = input
3769 .scan(
3770 q!(|| 0),
3771 q!(|acc, v| {
3772 *acc += v;
3773 Some(*acc)
3774 }),
3775 )
3776 .send_bincode_external(&external);
3777
3778 let nodes = flow
3779 .with_process(&node, deployment.Localhost())
3780 .with_external(&external, deployment.Localhost())
3781 .deploy(&mut deployment);
3782
3783 deployment.deploy().await.unwrap();
3784
3785 let mut external_in = nodes.connect(input_port).await;
3786 let mut external_out = nodes.connect(out).await;
3787
3788 deployment.start().await.unwrap();
3789
3790 external_in.send(1).await.unwrap();
3791 assert_eq!(external_out.next().await.unwrap(), 1);
3792
3793 external_in.send(2).await.unwrap();
3794 assert_eq!(external_out.next().await.unwrap(), 3);
3795 }
3796
3797 #[cfg(feature = "deploy")]
3798 #[tokio::test]
3799 async fn unbounded_enumerate_remembers_state() {
3800 let mut deployment = Deployment::new();
3801
3802 let mut flow = FlowBuilder::new();
3803 let node = flow.process::<()>();
3804 let external = flow.external::<()>();
3805
3806 let (input_port, input) = node.source_external_bincode(&external);
3807 let out = input.enumerate().send_bincode_external(&external);
3808
3809 let nodes = flow
3810 .with_process(&node, deployment.Localhost())
3811 .with_external(&external, deployment.Localhost())
3812 .deploy(&mut deployment);
3813
3814 deployment.deploy().await.unwrap();
3815
3816 let mut external_in = nodes.connect(input_port).await;
3817 let mut external_out = nodes.connect(out).await;
3818
3819 deployment.start().await.unwrap();
3820
3821 external_in.send(1).await.unwrap();
3822 assert_eq!(external_out.next().await.unwrap(), (0, 1));
3823
3824 external_in.send(2).await.unwrap();
3825 assert_eq!(external_out.next().await.unwrap(), (1, 2));
3826 }
3827
3828 #[cfg(feature = "deploy")]
3829 #[tokio::test]
3830 async fn unbounded_unique_remembers_state() {
3831 let mut deployment = Deployment::new();
3832
3833 let mut flow = FlowBuilder::new();
3834 let node = flow.process::<()>();
3835 let external = flow.external::<()>();
3836
3837 let (input_port, input) =
3838 node.source_external_bincode::<_, _, TotalOrder, ExactlyOnce>(&external);
3839 let out = input.unique().send_bincode_external(&external);
3840
3841 let nodes = flow
3842 .with_process(&node, deployment.Localhost())
3843 .with_external(&external, deployment.Localhost())
3844 .deploy(&mut deployment);
3845
3846 deployment.deploy().await.unwrap();
3847
3848 let mut external_in = nodes.connect(input_port).await;
3849 let mut external_out = nodes.connect(out).await;
3850
3851 deployment.start().await.unwrap();
3852
3853 external_in.send(1).await.unwrap();
3854 assert_eq!(external_out.next().await.unwrap(), 1);
3855
3856 external_in.send(2).await.unwrap();
3857 assert_eq!(external_out.next().await.unwrap(), 2);
3858
3859 external_in.send(1).await.unwrap();
3860 external_in.send(3).await.unwrap();
3861 assert_eq!(external_out.next().await.unwrap(), 3);
3862 }
3863
3864 #[cfg(feature = "sim")]
3865 #[test]
3866 #[should_panic]
3867 fn sim_batch_nondet_size() {
3868 let mut flow = FlowBuilder::new();
3869 let node = flow.process::<()>();
3870
3871 let (in_send, input) = node.sim_input::<_, TotalOrder, _>();
3872
3873 let tick = node.tick();
3874 let out_recv = input
3875 .batch(&tick, nondet!())
3876 .count()
3877 .all_ticks()
3878 .sim_output();
3879
3880 flow.sim().exhaustive(async || {
3881 in_send.send(());
3882 in_send.send(());
3883 in_send.send(());
3884
3885 assert_eq!(out_recv.next().await, 3); });
3887 }
3888
3889 #[cfg(feature = "sim")]
3890 #[test]
3891 fn sim_batch_preserves_order() {
3892 let mut flow = FlowBuilder::new();
3893 let node = flow.process::<()>();
3894
3895 let (in_send, input) = node.sim_input();
3896
3897 let tick = node.tick();
3898 let out_recv = input
3899 .batch(&tick, nondet!())
3900 .all_ticks()
3901 .sim_output();
3902
3903 flow.sim().exhaustive(async || {
3904 in_send.send(1);
3905 in_send.send(2);
3906 in_send.send(3);
3907
3908 out_recv.assert_yields_only([1, 2, 3]).await;
3909 });
3910 }
3911
3912 #[cfg(feature = "sim")]
3913 #[test]
3914 #[should_panic]
3915 fn sim_batch_unordered_shuffles() {
3916 let mut flow = FlowBuilder::new();
3917 let node = flow.process::<()>();
3918
3919 let (in_send, input) = node.sim_input::<_, NoOrder, _>();
3920
3921 let tick = node.tick();
3922 let batch = input.batch(&tick, nondet!());
3923 let out_recv = batch
3924 .clone()
3925 .min()
3926 .zip(batch.max())
3927 .all_ticks()
3928 .sim_output();
3929
3930 flow.sim().exhaustive(async || {
3931 in_send.send_many_unordered([1, 2, 3]);
3932
3933 if out_recv.collect::<Vec<_>>().await == vec![(1, 3), (2, 2)] {
3934 panic!("saw both (1, 3) and (2, 2), so batching must have shuffled the order");
3935 }
3936 });
3937 }
3938
3939 #[cfg(feature = "sim")]
3940 #[test]
3941 fn sim_batch_unordered_shuffles_count() {
3942 let mut flow = FlowBuilder::new();
3943 let node = flow.process::<()>();
3944
3945 let (in_send, input) = node.sim_input::<_, NoOrder, _>();
3946
3947 let tick = node.tick();
3948 let batch = input.batch(&tick, nondet!());
3949 let out_recv = batch.all_ticks().sim_output();
3950
3951 let instance_count = flow.sim().exhaustive(async || {
3952 in_send.send_many_unordered([1, 2, 3, 4]);
3953 out_recv.assert_yields_only_unordered([1, 2, 3, 4]).await;
3954 });
3955
3956 assert_eq!(
3957 instance_count,
3958 75 )
3960 }
3961
3962 #[cfg(feature = "sim")]
3963 #[test]
3964 #[should_panic]
3965 fn sim_observe_order_batched() {
3966 let mut flow = FlowBuilder::new();
3967 let node = flow.process::<()>();
3968
3969 let (in_send, input) = node.sim_input::<_, NoOrder, _>();
3970
3971 let tick = node.tick();
3972 let batch = input.batch(&tick, nondet!());
3973 let out_recv = batch
3974 .assume_ordering::<TotalOrder>(nondet!())
3975 .all_ticks()
3976 .sim_output();
3977
3978 flow.sim().exhaustive(async || {
3979 in_send.send_many_unordered([1, 2, 3, 4]);
3980 out_recv.assert_yields_only([1, 2, 3, 4]).await; });
3982 }
3983
3984 #[cfg(feature = "sim")]
3985 #[test]
3986 fn sim_observe_order_batched_count() {
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 .assume_ordering::<TotalOrder>(nondet!())
3996 .all_ticks()
3997 .sim_output();
3998
3999 let instance_count = flow.sim().exhaustive(async || {
4000 in_send.send_many_unordered([1, 2, 3, 4]);
4001 let _ = out_recv.collect::<Vec<_>>().await;
4002 });
4003
4004 assert_eq!(
4005 instance_count,
4006 192 )
4008 }
4009
4010 #[cfg(feature = "sim")]
4011 #[test]
4012 fn sim_unordered_count_instance_count() {
4013 let mut flow = FlowBuilder::new();
4014 let node = flow.process::<()>();
4015
4016 let (in_send, input) = node.sim_input::<_, NoOrder, _>();
4017
4018 let tick = node.tick();
4019 let out_recv = input
4020 .count()
4021 .snapshot(&tick, nondet!())
4022 .all_ticks()
4023 .sim_output();
4024
4025 let instance_count = flow.sim().exhaustive(async || {
4026 in_send.send_many_unordered([1, 2, 3, 4]);
4027 assert!(out_recv.collect::<Vec<_>>().await.last().unwrap() == &4);
4028 });
4029
4030 assert_eq!(
4031 instance_count,
4032 16 )
4034 }
4035
4036 #[cfg(feature = "sim")]
4037 #[test]
4038 fn sim_top_level_assume_ordering() {
4039 let mut flow = FlowBuilder::new();
4040 let node = flow.process::<()>();
4041
4042 let (in_send, input) = node.sim_input::<_, NoOrder, _>();
4043
4044 let out_recv = input
4045 .assume_ordering::<TotalOrder>(nondet!())
4046 .sim_output();
4047
4048 let instance_count = flow.sim().exhaustive(async || {
4049 in_send.send_many_unordered([1, 2, 3]);
4050 let mut out = out_recv.collect::<Vec<_>>().await;
4051 out.sort();
4052 assert_eq!(out, vec![1, 2, 3]);
4053 });
4054
4055 assert_eq!(instance_count, 6)
4056 }
4057
4058 #[cfg(feature = "sim")]
4059 #[test]
4060 fn sim_top_level_assume_ordering_cycle_back() {
4061 let mut flow = FlowBuilder::new();
4062 let node = flow.process::<()>();
4063 let node2 = flow.process::<()>();
4064
4065 let (in_send, input) = node.sim_input::<_, NoOrder, _>();
4066
4067 let (complete_cycle_back, cycle_back) =
4068 node.forward_ref::<super::Stream<_, _, _, NoOrder>>();
4069 let ordered = input
4070 .merge_unordered(cycle_back)
4071 .assume_ordering::<TotalOrder>(nondet!());
4072 complete_cycle_back.complete(
4073 ordered
4074 .clone()
4075 .map(q!(|v| v + 1))
4076 .filter(q!(|v| v % 2 == 1))
4077 .send(&node2, TCP.fail_stop().bincode())
4078 .send(&node, TCP.fail_stop().bincode()),
4079 );
4080
4081 let out_recv = ordered.sim_output();
4082
4083 let mut saw = false;
4084 let instance_count = flow.sim().exhaustive(async || {
4085 in_send.send_many_unordered([0, 2]);
4086 let out = out_recv.collect::<Vec<_>>().await;
4087
4088 if out.starts_with(&[0, 1, 2]) {
4089 saw = true;
4090 }
4091 });
4092
4093 assert!(saw, "did not see an instance with 0, 1, 2 in order");
4094 assert_eq!(instance_count, 6);
4095 }
4096
4097 #[cfg(feature = "sim")]
4098 #[test]
4099 fn sim_top_level_assume_ordering_cycle_back_tick() {
4100 let mut flow = FlowBuilder::new();
4101 let node = flow.process::<()>();
4102 let node2 = flow.process::<()>();
4103
4104 let (in_send, input) = node.sim_input::<_, NoOrder, _>();
4105
4106 let (complete_cycle_back, cycle_back) =
4107 node.forward_ref::<super::Stream<_, _, _, NoOrder>>();
4108 let ordered = input
4109 .merge_unordered(cycle_back)
4110 .assume_ordering::<TotalOrder>(nondet!());
4111 complete_cycle_back.complete(
4112 ordered
4113 .clone()
4114 .batch(&node.tick(), nondet!())
4115 .all_ticks()
4116 .map(q!(|v| v + 1))
4117 .filter(q!(|v| v % 2 == 1))
4118 .send(&node2, TCP.fail_stop().bincode())
4119 .send(&node, TCP.fail_stop().bincode()),
4120 );
4121
4122 let out_recv = ordered.sim_output();
4123
4124 let mut saw = false;
4125 let instance_count = flow.sim().exhaustive(async || {
4126 in_send.send_many_unordered([0, 2]);
4127 let out = out_recv.collect::<Vec<_>>().await;
4128
4129 if out.starts_with(&[0, 1, 2]) {
4130 saw = true;
4131 }
4132 });
4133
4134 assert!(saw, "did not see an instance with 0, 1, 2 in order");
4135 assert_eq!(instance_count, 58);
4136 }
4137
4138 #[cfg(feature = "sim")]
4139 #[test]
4140 fn sim_top_level_assume_ordering_multiple() {
4141 let mut flow = FlowBuilder::new();
4142 let node = flow.process::<()>();
4143 let node2 = flow.process::<()>();
4144
4145 let (in_send, input) = node.sim_input::<_, NoOrder, _>();
4146 let (_, input2) = node.sim_input::<_, NoOrder, _>();
4147
4148 let (complete_cycle_back, cycle_back) =
4149 node.forward_ref::<super::Stream<_, _, _, NoOrder>>();
4150 let input1_ordered = input
4151 .clone()
4152 .merge_unordered(cycle_back)
4153 .assume_ordering::<TotalOrder>(nondet!());
4154 let foo = input1_ordered
4155 .clone()
4156 .map(q!(|v| v + 3))
4157 .weaken_ordering::<NoOrder>()
4158 .merge_unordered(input2)
4159 .assume_ordering::<TotalOrder>(nondet!());
4160
4161 complete_cycle_back.complete(
4162 foo.filter(q!(|v| *v == 3))
4163 .send(&node2, TCP.fail_stop().bincode())
4164 .send(&node, TCP.fail_stop().bincode()),
4165 );
4166
4167 let out_recv = input1_ordered.sim_output();
4168
4169 let mut saw = false;
4170 let instance_count = flow.sim().exhaustive(async || {
4171 in_send.send_many_unordered([0, 1]);
4172 let out = out_recv.collect::<Vec<_>>().await;
4173
4174 if out.starts_with(&[0, 3, 1]) {
4175 saw = true;
4176 }
4177 });
4178
4179 assert!(saw, "did not see an instance with 0, 3, 1 in order");
4180 assert_eq!(instance_count, 15);
4181 }
4182
4183 #[cfg(feature = "sim")]
4184 #[test]
4185 fn sim_atomic_assume_ordering_cycle_back() {
4186 let mut flow = FlowBuilder::new();
4187 let node = flow.process::<()>();
4188 let node2 = flow.process::<()>();
4189
4190 let (in_send, input) = node.sim_input::<_, NoOrder, _>();
4191
4192 let (complete_cycle_back, cycle_back) =
4193 node.forward_ref::<super::Stream<_, _, _, NoOrder>>();
4194 let ordered = input
4195 .merge_unordered(cycle_back)
4196 .atomic()
4197 .assume_ordering::<TotalOrder>(nondet!())
4198 .end_atomic();
4199 complete_cycle_back.complete(
4200 ordered
4201 .clone()
4202 .map(q!(|v| v + 1))
4203 .filter(q!(|v| v % 2 == 1))
4204 .send(&node2, TCP.fail_stop().bincode())
4205 .send(&node, TCP.fail_stop().bincode()),
4206 );
4207
4208 let out_recv = ordered.sim_output();
4209
4210 let instance_count = flow.sim().exhaustive(async || {
4211 in_send.send_many_unordered([0, 2]);
4212 let out = out_recv.collect::<Vec<_>>().await;
4213 assert_eq!(out.len(), 4);
4214 });
4215 assert_eq!(instance_count, 22);
4216 }
4217
4218 #[cfg(feature = "deploy")]
4219 #[tokio::test]
4220 async fn partition_evens_odds() {
4221 let mut deployment = Deployment::new();
4222
4223 let mut flow = FlowBuilder::new();
4224 let node = flow.process::<()>();
4225 let external = flow.external::<()>();
4226
4227 let numbers = node.source_iter(q!(vec![1i32, 2, 3, 4, 5, 6]));
4228 let (evens, odds) = numbers.partition(q!(|x: &i32| x % 2 == 0));
4229 let evens_port = evens.send_bincode_external(&external);
4230 let odds_port = odds.send_bincode_external(&external);
4231
4232 let nodes = flow
4233 .with_process(&node, deployment.Localhost())
4234 .with_external(&external, deployment.Localhost())
4235 .deploy(&mut deployment);
4236
4237 deployment.deploy().await.unwrap();
4238
4239 let mut evens_out = nodes.connect(evens_port).await;
4240 let mut odds_out = nodes.connect(odds_port).await;
4241
4242 deployment.start().await.unwrap();
4243
4244 let mut even_results = Vec::new();
4245 for _ in 0..3 {
4246 even_results.push(evens_out.next().await.unwrap());
4247 }
4248 even_results.sort();
4249 assert_eq!(even_results, vec![2, 4, 6]);
4250
4251 let mut odd_results = Vec::new();
4252 for _ in 0..3 {
4253 odd_results.push(odds_out.next().await.unwrap());
4254 }
4255 odd_results.sort();
4256 assert_eq!(odd_results, vec![1, 3, 5]);
4257 }
4258
4259 #[cfg(feature = "deploy")]
4260 #[tokio::test]
4261 async fn unconsumed_inspect_still_runs() {
4262 use crate::deploy::DeployCrateWrapper;
4263
4264 let mut deployment = Deployment::new();
4265
4266 let mut flow = FlowBuilder::new();
4267 let node = flow.process::<()>();
4268
4269 node.source_iter(q!(0..5))
4272 .inspect(q!(|x| println!("inspect: {}", x)));
4273
4274 let nodes = flow
4275 .with_process(&node, deployment.Localhost())
4276 .deploy(&mut deployment);
4277
4278 deployment.deploy().await.unwrap();
4279
4280 let mut stdout = nodes.get_process(&node).stdout();
4281
4282 deployment.start().await.unwrap();
4283
4284 let mut lines = Vec::new();
4285 for _ in 0..5 {
4286 lines.push(stdout.recv().await.unwrap());
4287 }
4288 lines.sort();
4289 assert_eq!(
4290 lines,
4291 vec![
4292 "inspect: 0",
4293 "inspect: 1",
4294 "inspect: 2",
4295 "inspect: 3",
4296 "inspect: 4",
4297 ]
4298 );
4299 }
4300
4301 #[cfg(feature = "deploy")]
4302 #[tokio::test]
4303 async fn unconsumed_inspect_alive_at_deploy_still_runs() {
4304 use crate::deploy::DeployCrateWrapper;
4305
4306 let mut deployment = Deployment::new();
4307
4308 let mut flow = FlowBuilder::new();
4309 let node = flow.process::<()>();
4310
4311 let _inspected = node
4316 .source_iter(q!(0..5))
4317 .inspect(q!(|x| println!("inspect: {}", x)));
4318
4319 let nodes = flow
4320 .with_process(&node, deployment.Localhost())
4321 .deploy(&mut deployment);
4322
4323 deployment.deploy().await.unwrap();
4324
4325 let mut stdout = nodes.get_process(&node).stdout();
4326
4327 deployment.start().await.unwrap();
4328
4329 let mut lines = Vec::new();
4330 for _ in 0..5 {
4331 lines.push(stdout.recv().await.unwrap());
4332 }
4333 lines.sort();
4334 assert_eq!(
4335 lines,
4336 vec![
4337 "inspect: 0",
4338 "inspect: 1",
4339 "inspect: 2",
4340 "inspect: 3",
4341 "inspect: 4",
4342 ]
4343 );
4344 }
4345
4346 #[cfg(feature = "sim")]
4347 #[test]
4348 fn sim_limit() {
4349 let mut flow = FlowBuilder::new();
4350 let node = flow.process::<()>();
4351
4352 let (in_send, input) = node.sim_input();
4353
4354 let out_recv = input.limit(q!(3)).sim_output();
4355
4356 flow.sim().exhaustive(async || {
4357 in_send.send(1);
4358 in_send.send(2);
4359 in_send.send(3);
4360 in_send.send(4);
4361 in_send.send(5);
4362
4363 out_recv.assert_yields_only([1, 2, 3]).await;
4364 });
4365 }
4366
4367 #[cfg(feature = "sim")]
4368 #[test]
4369 fn sim_limit_zero() {
4370 let mut flow = FlowBuilder::new();
4371 let node = flow.process::<()>();
4372
4373 let (in_send, input) = node.sim_input();
4374
4375 let out_recv = input.limit(q!(0)).sim_output();
4376
4377 flow.sim().exhaustive(async || {
4378 in_send.send(1);
4379 in_send.send(2);
4380
4381 out_recv.assert_yields_only::<i32, _>([]).await;
4382 });
4383 }
4384
4385 #[cfg(feature = "sim")]
4386 #[test]
4387 fn sim_merge_ordered() {
4388 let mut flow = FlowBuilder::new();
4389 let node = flow.process::<()>();
4390
4391 let (in_send, input) = node.sim_input();
4392 let (in_send2, input2) = node.sim_input();
4393
4394 let out_recv = input
4395 .merge_ordered(input2, nondet!())
4396 .sim_output();
4397
4398 let mut saw_out_of_order = false;
4399 let instances = flow.sim().exhaustive(async || {
4400 in_send.send(1);
4401 in_send.send(2);
4402 in_send2.send(3);
4403 in_send2.send(4);
4404
4405 let out = out_recv.collect::<Vec<_>>().await;
4406
4407 if out == [1, 3, 2, 4] {
4408 saw_out_of_order = true;
4409 }
4410
4411 let mut first_elements = out.iter().filter(|v| **v <= 2).copied().collect::<Vec<_>>();
4414 let mut second_elements = out.iter().filter(|v| **v > 2).copied().collect::<Vec<_>>();
4415 assert_eq!(
4416 first_elements,
4417 vec![1, 2],
4418 "first input order violated: {:?}",
4419 out
4420 );
4421 assert_eq!(
4422 second_elements,
4423 vec![3, 4],
4424 "second input order violated: {:?}",
4425 out
4426 );
4427
4428 first_elements.append(&mut second_elements);
4429 first_elements.sort();
4430 assert_eq!(first_elements, vec![1, 2, 3, 4]);
4431 });
4432
4433 assert!(saw_out_of_order);
4434 assert_eq!(instances, 6);
4435 }
4436
4437 #[cfg(feature = "sim")]
4440 #[test]
4441 fn sim_merge_ordered_one_empty() {
4442 let mut flow = FlowBuilder::new();
4443 let node = flow.process::<()>();
4444
4445 let (in_send, input) = node.sim_input();
4446 let (_in_send2, input2) = node.sim_input();
4447
4448 let out_recv = input
4449 .merge_ordered(input2, nondet!())
4450 .sim_output();
4451
4452 let instances = flow.sim().exhaustive(async || {
4453 in_send.send(1);
4454 in_send.send(2);
4455
4456 let out = out_recv.collect::<Vec<_>>().await;
4457 assert_eq!(out, vec![1, 2]);
4458 });
4459
4460 assert_eq!(instances, 1);
4462 }
4463
4464 #[cfg(feature = "sim")]
4470 #[test]
4471 fn sim_merge_ordered_cycle_back() {
4472 let mut flow = FlowBuilder::new();
4473 let node = flow.process::<()>();
4474
4475 let (in_send, input) = node.sim_input();
4476
4477 let (complete_cycle_back, cycle_back) =
4479 node.forward_ref::<super::Stream<_, _, _, TotalOrder>>();
4480
4481 let merged = input.merge_ordered(cycle_back, nondet!());
4483
4484 complete_cycle_back.complete(merged.clone().filter(q!(|v| *v == 1)).map(q!(|v| v * 10)));
4486
4487 let out_recv = merged.sim_output();
4488
4489 let mut saw_cycle_before_second = false;
4492 flow.sim().exhaustive(async || {
4493 in_send.send(1);
4494 in_send.send(2);
4495
4496 let out = out_recv.collect::<Vec<_>>().await;
4497
4498 let pos_1 = out.iter().position(|v| *v == 1).unwrap();
4500 let pos_10 = out.iter().position(|v| *v == 10).unwrap();
4501 assert!(pos_1 < pos_10, "causal order violated: {:?}", out);
4502
4503 if out == [1, 10, 2] {
4505 saw_cycle_before_second = true;
4506 }
4507
4508 let mut sorted = out;
4509 sorted.sort();
4510 assert_eq!(sorted, vec![1, 2, 10]);
4511 });
4512
4513 assert!(
4514 saw_cycle_before_second,
4515 "never saw the cycled element arrive before the second input element"
4516 );
4517 }
4518
4519 #[cfg(feature = "sim")]
4523 #[test]
4524 fn sim_merge_ordered_delayed() {
4525 let mut flow = FlowBuilder::new();
4526 let node = flow.process::<()>();
4527
4528 let (in_send, input) = node.sim_input();
4529 let (in_send2, input2) = node.sim_input();
4530
4531 let out_recv = input
4532 .merge_ordered(input2, nondet!())
4533 .sim_output();
4534
4535 let mut saw_delayed_interleaving = false;
4536 flow.sim().exhaustive(async || {
4537 in_send.send(1);
4539 in_send2.send(3);
4540 in_send2.send(4);
4541
4542 let first_batch = out_recv.collect::<Vec<_>>().await;
4544
4545 in_send.send(2);
4547 let second_batch = out_recv.collect::<Vec<_>>().await;
4548
4549 let mut all: Vec<_> = first_batch
4550 .iter()
4551 .chain(second_batch.iter())
4552 .copied()
4553 .collect();
4554
4555 if all == [1, 3, 4, 2] {
4557 saw_delayed_interleaving = true;
4558 }
4559
4560 all.sort();
4561 assert_eq!(all, vec![1, 2, 3, 4]);
4562 });
4563
4564 assert!(saw_delayed_interleaving);
4565 }
4566
4567 #[cfg(feature = "deploy")]
4572 #[tokio::test]
4573 async fn deploy_merge_ordered_delayed() {
4574 let mut deployment = Deployment::new();
4575
4576 let mut flow = FlowBuilder::new();
4577 let node = flow.process::<()>();
4578 let external = flow.external::<()>();
4579
4580 let (input_a_port, input_a) = node.source_external_bincode(&external);
4581 let (input_b_port, input_b) = node.source_external_bincode(&external);
4582
4583 let out = input_a
4584 .assume_ordering(nondet!())
4585 .merge_ordered(
4586 input_b.assume_ordering(nondet!()),
4587 nondet!(),
4588 )
4589 .send_bincode_external(&external);
4590
4591 let nodes = flow
4592 .with_process(&node, deployment.Localhost())
4593 .with_external(&external, deployment.Localhost())
4594 .deploy(&mut deployment);
4595
4596 deployment.deploy().await.unwrap();
4597
4598 let mut ext_a = nodes.connect(input_a_port).await;
4599 let mut ext_b = nodes.connect(input_b_port).await;
4600 let mut ext_out = nodes.connect(out).await;
4601
4602 deployment.start().await.unwrap();
4603
4604 ext_a.send(1).await.unwrap();
4606 ext_b.send(3).await.unwrap();
4607 ext_b.send(4).await.unwrap();
4608
4609 let mut received = Vec::new();
4611 for _ in 0..3 {
4612 received.push(ext_out.next().await.unwrap());
4613 }
4614
4615 ext_a.send(2).await.unwrap();
4617 received.push(ext_out.next().await.unwrap());
4618
4619 received.sort();
4621 assert_eq!(received, vec![1, 2, 3, 4]);
4622 }
4623
4624 #[cfg(feature = "deploy")]
4625 #[tokio::test]
4626 async fn monotone_fold_threshold() {
4627 use crate::properties::manual_proof;
4628
4629 let mut deployment = Deployment::new();
4630
4631 let mut flow = FlowBuilder::new();
4632 let node = flow.process::<()>();
4633 let external = flow.external::<()>();
4634
4635 let in_unbounded: super::Stream<_, _> =
4636 node.source_iter(q!(vec![1i32, 2, 3, 4, 5, 6])).into();
4637 let sum = in_unbounded.fold(
4638 q!(|| 0),
4639 q!(
4640 |sum, v| {
4641 *sum += v;
4642 },
4643 monotone = manual_proof!()
4644 ),
4645 );
4646
4647 let threshold_out = sum
4648 .threshold_greater_or_equal(node.singleton(q!(7)))
4649 .send_bincode_external(&external);
4650
4651 let nodes = flow
4652 .with_process(&node, deployment.Localhost())
4653 .with_external(&external, deployment.Localhost())
4654 .deploy(&mut deployment);
4655
4656 deployment.deploy().await.unwrap();
4657
4658 let mut threshold_out = nodes.connect(threshold_out).await;
4659
4660 deployment.start().await.unwrap();
4661
4662 assert_eq!(threshold_out.next().await.unwrap(), 7);
4663 }
4664
4665 #[cfg(feature = "deploy")]
4666 #[tokio::test]
4667 async fn monotone_count_threshold() {
4668 let mut deployment = Deployment::new();
4669
4670 let mut flow = FlowBuilder::new();
4671 let node = flow.process::<()>();
4672 let external = flow.external::<()>();
4673
4674 let in_unbounded: super::Stream<_, _> =
4675 node.source_iter(q!(vec![1i32, 2, 3, 4, 5, 6])).into();
4676 let sum = in_unbounded.count();
4677
4678 let threshold_out = sum
4679 .threshold_greater_or_equal(node.singleton(q!(3)))
4680 .send_bincode_external(&external);
4681
4682 let nodes = flow
4683 .with_process(&node, deployment.Localhost())
4684 .with_external(&external, deployment.Localhost())
4685 .deploy(&mut deployment);
4686
4687 deployment.deploy().await.unwrap();
4688
4689 let mut threshold_out = nodes.connect(threshold_out).await;
4690
4691 deployment.start().await.unwrap();
4692
4693 assert_eq!(threshold_out.next().await.unwrap(), 3);
4694 }
4695
4696 #[cfg(feature = "deploy")]
4697 #[tokio::test]
4698 async fn monotone_map_order_preserving_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 doubled = sum.map(q!(
4721 |v| v * 2,
4722 order_preserving = manual_proof!()
4723 ));
4724
4725 let threshold_out = doubled
4726 .threshold_greater_or_equal(node.singleton(q!(14)))
4727 .send_bincode_external(&external);
4728
4729 let nodes = flow
4730 .with_process(&node, deployment.Localhost())
4731 .with_external(&external, deployment.Localhost())
4732 .deploy(&mut deployment);
4733
4734 deployment.deploy().await.unwrap();
4735
4736 let mut threshold_out = nodes.connect(threshold_out).await;
4737
4738 deployment.start().await.unwrap();
4739
4740 assert_eq!(threshold_out.next().await.unwrap(), 14);
4741 }
4742
4743 #[cfg(any(feature = "deploy", feature = "sim"))]
4746 mod join_ordering_type_tests {
4747 use crate::live_collections::boundedness::{Bounded, Unbounded};
4748 use crate::live_collections::stream::{ExactlyOnce, NoOrder, Stream, TotalOrder};
4749 use crate::location::{Location, Process};
4750
4751 #[expect(dead_code, reason = "compile-time type test")]
4752 fn join_unbounded_with_bounded_preserves_order<'a>(
4753 left: Stream<(i32, char), Process<'a>, Unbounded, TotalOrder, ExactlyOnce>,
4754 right: Stream<(i32, char), Process<'a>, Bounded, TotalOrder, ExactlyOnce>,
4755 ) -> Stream<(i32, (char, char)), Process<'a>, Unbounded, TotalOrder, ExactlyOnce> {
4756 left.join(right)
4757 }
4758
4759 #[expect(dead_code, reason = "compile-time type test")]
4760 fn join_unbounded_with_unbounded_is_no_order<'a>(
4761 left: Stream<(i32, char), Process<'a>, Unbounded, TotalOrder, ExactlyOnce>,
4762 right: Stream<(i32, char), Process<'a>, Unbounded, TotalOrder, ExactlyOnce>,
4763 ) -> Stream<(i32, (char, char)), Process<'a>, Unbounded, NoOrder, ExactlyOnce> {
4764 left.join(right)
4765 }
4766
4767 #[expect(dead_code, reason = "compile-time type test")]
4768 fn join_bounded_with_bounded_preserves_order<'a, L: Location<'a>>(
4769 left: Stream<(i32, char), L, Bounded, TotalOrder, ExactlyOnce>,
4770 right: Stream<(i32, char), L, Bounded, TotalOrder, ExactlyOnce>,
4771 ) -> Stream<(i32, (char, char)), L, Bounded, TotalOrder, ExactlyOnce> {
4772 left.join(right)
4773 }
4774
4775 #[expect(dead_code, reason = "compile-time type test")]
4776 fn join_unbounded_noorder_with_bounded<'a>(
4777 left: Stream<(i32, char), Process<'a>, Unbounded, NoOrder, ExactlyOnce>,
4778 right: Stream<(i32, char), Process<'a>, Bounded, NoOrder, ExactlyOnce>,
4779 ) -> Stream<(i32, (char, char)), Process<'a>, Unbounded, NoOrder, ExactlyOnce> {
4780 left.join(right)
4781 }
4782
4783 #[expect(dead_code, reason = "compile-time type test")]
4786 fn cross_product_unbounded_with_bounded_preserves_order<'a>(
4787 left: Stream<i32, Process<'a>, Unbounded, TotalOrder, ExactlyOnce>,
4788 right: Stream<char, Process<'a>, Bounded, TotalOrder, ExactlyOnce>,
4789 ) -> Stream<(i32, char), Process<'a>, Unbounded, TotalOrder, ExactlyOnce> {
4790 left.cross_product(right)
4791 }
4792
4793 #[expect(dead_code, reason = "compile-time type test")]
4794 fn cross_product_bounded_with_bounded_preserves_order<'a>(
4795 left: Stream<i32, Process<'a>, Bounded, TotalOrder, ExactlyOnce>,
4796 right: Stream<char, Process<'a>, Bounded, TotalOrder, ExactlyOnce>,
4797 ) -> Stream<(i32, char), Process<'a>, Bounded, TotalOrder, ExactlyOnce> {
4798 left.cross_product(right)
4799 }
4800
4801 #[expect(dead_code, reason = "compile-time type test")]
4802 fn cross_product_unbounded_with_unbounded_is_no_order<'a>(
4803 left: Stream<i32, Process<'a>, Unbounded, TotalOrder, ExactlyOnce>,
4804 right: Stream<char, Process<'a>, Unbounded, TotalOrder, ExactlyOnce>,
4805 ) -> Stream<(i32, char), Process<'a>, Unbounded, NoOrder, ExactlyOnce> {
4806 left.cross_product(right)
4807 }
4808 } #[cfg(feature = "sim")]
4813 #[test]
4814 fn cross_product_mixed_boundedness_correctness() {
4815 use stageleft::q;
4816
4817 use crate::compile::builder::FlowBuilder;
4818 use crate::nondet::nondet;
4819
4820 let mut flow = FlowBuilder::new();
4821 let process = flow.process::<()>();
4822 let tick = process.tick();
4823
4824 let left = process.source_iter(q!(vec![1, 2]));
4825 let right = process
4826 .source_iter(q!(vec!['a', 'b']))
4827 .batch(&tick, nondet!())
4828 .all_ticks();
4829
4830 let out = left.cross_product(right).sim_output();
4831
4832 flow.sim().exhaustive(async || {
4833 out.assert_yields_only_unordered(vec![(1, 'a'), (1, 'b'), (2, 'a'), (2, 'b')])
4834 .await;
4835 });
4836 }
4837
4838 #[cfg(feature = "sim")]
4839 #[test]
4840 fn join_mixed_boundedness_correctness() {
4841 use stageleft::q;
4842
4843 use crate::compile::builder::FlowBuilder;
4844 use crate::nondet::nondet;
4845
4846 let mut flow = FlowBuilder::new();
4847 let process = flow.process::<()>();
4848 let tick = process.tick();
4849
4850 let left = process.source_iter(q!(vec![(1, 'a'), (2, 'b')]));
4851 let right = process
4852 .source_iter(q!(vec![(1, 'x'), (2, 'y')]))
4853 .batch(&tick, nondet!())
4854 .all_ticks();
4855
4856 let out = left.join(right).sim_output();
4857
4858 flow.sim().exhaustive(async || {
4859 out.assert_yields_only_unordered(vec![(1, ('a', 'x')), (2, ('b', 'y'))])
4860 .await;
4861 });
4862 }
4863
4864 #[cfg(feature = "sim")]
4865 #[test]
4866 fn sim_merge_unordered_independent_atomics() {
4867 let mut flow = FlowBuilder::new();
4868 let node = flow.process::<()>();
4869
4870 let (in1_send, input1) = node.sim_input::<_, TotalOrder, _>();
4871 let (in2_send, input2) = node.sim_input::<_, TotalOrder, _>();
4872
4873 let out = input1
4874 .atomic()
4875 .merge_unordered(input2.atomic())
4876 .end_atomic()
4877 .sim_output();
4878
4879 flow.sim().exhaustive(async || {
4880 in1_send.send(1);
4881 in2_send.send(2);
4882
4883 out.assert_yields_only_unordered(vec![1, 2]).await;
4884 });
4885 }
4886
4887 #[cfg(feature = "deploy")]
4888 #[tokio::test]
4889 async fn test_stream_ref() {
4890 let mut deployment = Deployment::new();
4891
4892 let mut flow = FlowBuilder::new();
4893 let external = flow.external::<()>();
4894 let p1 = flow.process::<()>();
4895
4896 let my_stream = p1.source_iter(q!(1..=5i32));
4898
4899 let stream_ref = my_stream.by_ref();
4900
4901 let out_port = p1
4903 .source_iter(q!([()]))
4904 .map(q!(|_| stream_ref.len() as i32))
4905 .send_bincode_external(&external);
4906
4907 my_stream.for_each(q!(|_| {}));
4909
4910 let nodes = flow
4911 .with_default_optimize()
4912 .with_process(&p1, deployment.Localhost())
4913 .with_external(&external, deployment.Localhost())
4914 .deploy(&mut deployment);
4915
4916 deployment.deploy().await.unwrap();
4917
4918 let mut out_recv = nodes.connect(out_port).await;
4919
4920 deployment.start().await.unwrap();
4921
4922 let result = out_recv.next().await.unwrap();
4923 assert_eq!(result, 5);
4925 }
4926
4927 #[cfg(feature = "deploy")]
4928 #[tokio::test]
4929 async fn test_stream_ref_contents() {
4930 let mut deployment = Deployment::new();
4931
4932 let mut flow = FlowBuilder::new();
4933 let external = flow.external::<()>();
4934 let p1 = flow.process::<()>();
4935
4936 let my_stream = p1.source_iter(q!(1..=3i32));
4938
4939 let stream_ref = my_stream.by_ref();
4940
4941 let out_port = p1
4943 .source_iter(q!([()]))
4944 .map(q!(|_| stream_ref.iter().sum::<i32>()))
4945 .send_bincode_external(&external);
4946
4947 my_stream.for_each(q!(|_| {}));
4948
4949 let nodes = flow
4950 .with_default_optimize()
4951 .with_process(&p1, deployment.Localhost())
4952 .with_external(&external, deployment.Localhost())
4953 .deploy(&mut deployment);
4954
4955 deployment.deploy().await.unwrap();
4956
4957 let mut out_recv = nodes.connect(out_port).await;
4958
4959 deployment.start().await.unwrap();
4960
4961 let result = out_recv.next().await.unwrap();
4962 assert_eq!(result, 6);
4964 }
4965
4966 #[cfg(feature = "deploy")]
4967 #[tokio::test]
4968 async fn test_stream_ref_no_consumer() {
4969 let mut deployment = Deployment::new();
4970
4971 let mut flow = FlowBuilder::new();
4972 let external = flow.external::<()>();
4973 let p1 = flow.process::<()>();
4974
4975 let my_stream = p1.source_iter(q!(1..=4i32));
4977
4978 let stream_ref = my_stream.by_ref();
4979
4980 let out_port = p1
4981 .source_iter(q!([()]))
4982 .map(q!(|_| stream_ref.len() as i32))
4983 .send_bincode_external(&external);
4984
4985 let nodes = flow
4986 .with_default_optimize()
4987 .with_process(&p1, deployment.Localhost())
4988 .with_external(&external, deployment.Localhost())
4989 .deploy(&mut deployment);
4990
4991 deployment.deploy().await.unwrap();
4992
4993 let mut out_recv = nodes.connect(out_port).await;
4994
4995 deployment.start().await.unwrap();
4996
4997 let result = out_recv.next().await.unwrap();
4998 assert_eq!(result, 4);
4999 }
5000
5001 #[cfg(feature = "deploy")]
5002 #[tokio::test]
5003 async fn test_stream_mut() {
5004 let mut deployment = Deployment::new();
5005
5006 let mut flow = FlowBuilder::new();
5007 let external = flow.external::<()>();
5008 let p1 = flow.process::<()>();
5009
5010 let my_stream = p1.source_iter(q!(1..=5i32));
5012
5013 let stream_mut = my_stream.by_mut();
5014
5015 let out_port = p1
5017 .source_iter(q!([()]))
5018 .map(q!(|_| {
5019 stream_mut.retain(|x| *x > 3);
5020 stream_mut.len() as i32
5021 }))
5022 .send_bincode_external(&external);
5023
5024 my_stream.for_each(q!(|_| {}));
5025
5026 let nodes = flow
5027 .with_default_optimize()
5028 .with_process(&p1, deployment.Localhost())
5029 .with_external(&external, deployment.Localhost())
5030 .deploy(&mut deployment);
5031
5032 deployment.deploy().await.unwrap();
5033
5034 let mut out_recv = nodes.connect(out_port).await;
5035
5036 deployment.start().await.unwrap();
5037
5038 let result = out_recv.next().await.unwrap();
5039 assert_eq!(result, 2);
5041 }
5042
5043 #[cfg(feature = "sim")]
5047 #[test]
5048 fn sim_map_with_mut_on_unordered_explores_multiple_states() {
5049 use crate::live_collections::sliced::sliced;
5050 use crate::live_collections::stream::ExactlyOnce;
5051 use crate::properties::manual_proof;
5052
5053 let mut flow = FlowBuilder::new();
5054 let node = flow.process::<()>();
5055
5056 let (trigger_send, trigger) = node.sim_input::<i32, TotalOrder, ExactlyOnce>();
5057
5058 let out_recv = sliced! {
5059 let batch = use::batch(trigger, nondet!());
5060 let counter = batch.location().source_iter(q!(vec![0i32]))
5061 .fold(q!(|| 0i32), q!(|acc, v| *acc += v));
5062 let counter_mut = counter.by_mut();
5063 let items = batch.location().source_iter(q!(vec![1i32, 2])).weaken_ordering::<NoOrder>();
5064 items.map(q!(
5065 |x| {
5066 *counter_mut += x;
5067 *counter_mut
5068 },
5069 commutative = manual_proof!()
5070 ))
5071 }
5072 .sim_output();
5073
5074 let count = flow.sim().exhaustive(async || {
5075 trigger_send.send(1);
5076 let _all: Vec<i32> = out_recv.collect_sorted().await;
5077 });
5078
5079 assert_eq!(
5080 count, 2,
5081 "Expected 2 simulation instances due to mut on unordered input, got {}",
5082 count
5083 );
5084 }
5085
5086 #[cfg(feature = "sim")]
5090 #[test]
5091 fn sim_scan_with_ref_capture() {
5092 use crate::live_collections::sliced::sliced;
5093 use crate::live_collections::stream::ExactlyOnce;
5094
5095 let mut flow = FlowBuilder::new();
5096 let node = flow.process::<()>();
5097
5098 let (trigger_send, trigger) = node.sim_input::<i32, TotalOrder, ExactlyOnce>();
5099
5100 let out_recv = sliced! {
5101 let batch = use::batch(trigger, nondet!());
5102 let offset = batch
5103 .location()
5104 .source_iter(q!(vec![10i32]))
5105 .fold(q!(|| 0i32), q!(|acc, v| *acc += v));
5106 let offset_ref = offset.by_ref();
5107 batch
5108 .location()
5109 .source_iter(q!(vec![1i32, 2, 3]))
5110 .scan(
5111 q!(|| 0i32),
5112 q!(move |acc: &mut i32, x| {
5113 *acc += x + *offset_ref;
5114 Some(*acc)
5115 }),
5116 )
5117 }
5118 .sim_output();
5119
5120 let count = flow.sim().exhaustive(async || {
5121 trigger_send.send(1);
5122 let all: Vec<i32> = out_recv.collect().await;
5123 assert_eq!(all, vec![11, 23, 36]);
5128 });
5129
5130 assert_eq!(
5131 count, 1,
5132 "Expected a single simulation instance for a totally-ordered scan, got {}",
5133 count
5134 );
5135 }
5136
5137 #[cfg(feature = "sim")]
5141 #[test]
5142 #[ignore = "observe_nondet not yet supported for top-level bounded inputs (https://github.com/hydro-project/hydro/issues/2950)"]
5143 fn sim_map_with_mut_on_unordered_top_level() {
5144 use crate::properties::manual_proof;
5145
5146 let mut flow = FlowBuilder::new();
5147 let node = flow.process::<()>();
5148
5149 let counter = node
5150 .source_iter(q!(vec![0i32]))
5151 .fold(q!(|| 0i32), q!(|acc, v| *acc += v));
5152 let counter_mut = counter.by_mut();
5153
5154 let out_recv = node
5155 .source_iter(q!(vec![1i32, 2]))
5156 .weaken_ordering::<NoOrder>()
5157 .map(q!(
5158 |x| {
5159 *counter_mut += x;
5160 *counter_mut
5161 },
5162 commutative = manual_proof!()
5163 ))
5164 .assume_ordering::<TotalOrder>(nondet!())
5165 .sim_output();
5166
5167 counter.into_stream().for_each(q!(|_| {}));
5168
5169 let count = flow.sim().exhaustive(async || {
5170 let _all: Vec<i32> = out_recv.collect().await;
5171 });
5172
5173 assert_eq!(
5174 count, 2,
5175 "Expected 2 simulation instances due to mut on unordered input, got {}",
5176 count
5177 );
5178 }
5179}