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>(
584 self,
585 f: impl IntoQuotedMut<'a, F, OperatorContext<L, B>, StreamMapFuncAlgebra<T, B, C, I>>,
586 ) -> Stream<U, L, B, O, R>
587 where
588 F: FnMut(T) -> U + 'a,
589 C: ValidMutCommutativityFor<F, T, U, O, WAS_MUT>,
590 I: ValidMutIdempotenceFor<F, T, U, R, WAS_MUT>,
591 {
592 let f = crate::handoff_ref::with_ref_capture(|| {
593 let (expr, proof) =
594 f.splice_fnmut1_ctx_props(&OperatorContext::<L, B>::new(&self.location));
595 proof.register_proof(&expr);
596 expr.into()
597 });
598 Stream::new(
599 self.location.clone(),
600 HydroNode::Map {
601 f,
602 input: Box::new(self.ir_node.replace(HydroNode::Placeholder)),
603 metadata: self
604 .location
605 .new_node_metadata(Stream::<U, L, B, O, R>::collection_kind()),
606 },
607 )
608 }
609
610 pub fn flat_map_ordered<U, I, F, C, Idemp, const WAS_MUT: bool>(
635 self,
636 f: impl IntoQuotedMut<'a, F, OperatorContext<L, B>, StreamMapFuncAlgebra<T, B, C, Idemp>>,
637 ) -> Stream<U, L, B, O, R>
638 where
639 I: IntoIterator<Item = U>,
640 F: FnMut(T) -> I + 'a,
641 C: ValidMutCommutativityFor<F, T, I, O, WAS_MUT>,
642 Idemp: ValidMutIdempotenceFor<F, T, I, R, WAS_MUT>,
643 {
644 let f = crate::handoff_ref::with_ref_capture(|| {
645 let (expr, proof) =
646 f.splice_fnmut1_ctx_props(&OperatorContext::<L, B>::new(&self.location));
647 proof.register_proof(&expr);
648 expr.into()
649 });
650 Stream::new(
651 self.location.clone(),
652 HydroNode::FlatMap {
653 f,
654 input: Box::new(self.ir_node.replace(HydroNode::Placeholder)),
655 metadata: self
656 .location
657 .new_node_metadata(Stream::<U, L, B, O, R>::collection_kind()),
658 },
659 )
660 }
661
662 pub fn flat_map_unordered<U, I, F, C, Idemp, const WAS_MUT: bool>(
689 self,
690 f: impl IntoQuotedMut<'a, F, OperatorContext<L, B>, StreamMapFuncAlgebra<T, B, C, Idemp>>,
691 ) -> Stream<U, L, B, NoOrder, R>
692 where
693 I: IntoIterator<Item = U>,
694 F: FnMut(T) -> I + 'a,
695 C: ValidMutCommutativityFor<F, T, I, O, WAS_MUT>,
696 Idemp: ValidMutIdempotenceFor<F, T, I, R, WAS_MUT>,
697 {
698 let f = crate::handoff_ref::with_ref_capture(|| {
699 let (expr, proof) =
700 f.splice_fnmut1_ctx_props(&OperatorContext::<L, B>::new(&self.location));
701 proof.register_proof(&expr);
702 expr.into()
703 });
704 Stream::new(
705 self.location.clone(),
706 HydroNode::FlatMap {
707 f,
708 input: Box::new(self.ir_node.replace(HydroNode::Placeholder)),
709 metadata: self
710 .location
711 .new_node_metadata(Stream::<U, L, B, NoOrder, R>::collection_kind()),
712 },
713 )
714 }
715
716 pub fn flatten_ordered<U>(self) -> Stream<U, L, B, O, R>
739 where
740 T: IntoIterator<Item = U>,
741 {
742 self.flat_map_ordered(q!(|d| d))
743 }
744
745 pub fn flatten_unordered<U>(self) -> Stream<U, L, B, NoOrder, R>
772 where
773 T: IntoIterator<Item = U>,
774 {
775 self.flat_map_unordered(q!(|d| d))
776 }
777
778 pub fn flat_map_stream_blocking<U, S, F, C, Idemp, const WAS_MUT: bool>(
782 self,
783 f: impl IntoQuotedMut<'a, F, OperatorContext<L, B>, StreamMapFuncAlgebra<T, B, C, Idemp>>,
784 ) -> Stream<U, L, B, O, R>
785 where
786 S: futures::Stream<Item = U>,
787 F: FnMut(T) -> S + 'a,
788 C: ValidMutCommutativityFor<F, T, S, O, WAS_MUT>,
789 Idemp: ValidMutIdempotenceFor<F, T, S, R, WAS_MUT>,
790 {
791 let f = crate::handoff_ref::with_ref_capture(|| {
792 let (expr, proof) =
793 f.splice_fnmut1_ctx_props(&OperatorContext::<L, B>::new(&self.location));
794 proof.register_proof(&expr);
795 expr.into()
796 });
797 Stream::new(
798 self.location.clone(),
799 HydroNode::FlatMapStreamBlocking {
800 f,
801 input: Box::new(self.ir_node.replace(HydroNode::Placeholder)),
802 metadata: self
803 .location
804 .new_node_metadata(Stream::<U, L, B, O, R>::collection_kind()),
805 },
806 )
807 }
808
809 pub fn flatten_stream_blocking<U>(self) -> Stream<U, L, B, O, R>
813 where
814 T: futures::Stream<Item = U>,
815 {
816 self.flat_map_stream_blocking(q!(|d| d))
817 }
818
819 pub fn filter<F, C, Idemp, const WAS_MUT: bool>(
844 self,
845 f: impl IntoQuotedMut<'a, F, OperatorContext<L, B>, StreamMapFuncAlgebra<T, B, C, Idemp>>,
846 ) -> Self
847 where
848 F: FnMut(&T) -> bool + 'a,
849 C: ValidMutBorrowCommutativityFor<F, T, bool, O, WAS_MUT>,
850 Idemp: ValidMutBorrowIdempotenceFor<F, T, bool, R, WAS_MUT>,
851 {
852 let f = crate::handoff_ref::with_ref_capture(|| {
853 let (expr, proof) =
854 f.splice_fnmut1_borrow_ctx_props(&OperatorContext::<L, B>::new(&self.location));
855 proof.register_proof(&expr);
856 expr.into()
857 });
858 Stream::new(
859 self.location.clone(),
860 HydroNode::Filter {
861 f,
862 input: Box::new(self.ir_node.replace(HydroNode::Placeholder)),
863 metadata: self.location.new_node_metadata(Self::collection_kind()),
864 },
865 )
866 }
867
868 pub fn partition<F, C, Idemp, const WAS_MUT: bool>(
903 self,
904 f: impl IntoQuotedMut<'a, F, OperatorContext<L, B>, StreamMapFuncAlgebra<T, B, C, Idemp>>,
905 ) -> (Stream<T, L, B, O, R>, Stream<T, L, B, O, R>)
906 where
907 F: FnMut(&T) -> bool + 'a,
908 C: ValidMutBorrowCommutativityFor<F, T, bool, O, WAS_MUT>,
909 Idemp: ValidMutBorrowIdempotenceFor<F, T, bool, R, WAS_MUT>,
910 {
911 let f = crate::handoff_ref::with_ref_capture(|| {
912 let (expr, proof) =
913 f.splice_fnmut1_borrow_ctx_props(&OperatorContext::<L, B>::new(&self.location));
914 proof.register_proof(&expr);
915 expr.into()
916 });
917 let shared = Rc::new(RefCell::new(HydroNode::PartitionShared {
918 input: Box::new(self.ir_node.replace(HydroNode::Placeholder)),
919 f,
920 metadata: self.location.new_node_metadata(Self::collection_kind()),
921 }));
922
923 let true_stream = Stream::new(
924 self.location.clone(),
925 HydroNode::PartitionSide {
926 inner: SharedNode(Rc::clone(&shared)),
927 is_true: true,
928 metadata: self.location.new_node_metadata(Self::collection_kind()),
929 },
930 );
931
932 let false_stream = Stream::new(
933 self.location.clone(),
934 HydroNode::PartitionSide {
935 inner: SharedNode(shared),
936 is_true: false,
937 metadata: self.location.new_node_metadata(Self::collection_kind()),
938 },
939 );
940
941 (true_stream, false_stream)
942 }
943
944 pub fn filter_map<U, F, C, Idemp, const WAS_MUT: bool>(
964 self,
965 f: impl IntoQuotedMut<'a, F, OperatorContext<L, B>, StreamMapFuncAlgebra<T, B, C, Idemp>>,
966 ) -> Stream<U, L, B, O, R>
967 where
968 F: FnMut(T) -> Option<U> + 'a,
969 C: ValidMutCommutativityFor<F, T, Option<U>, O, WAS_MUT>,
970 Idemp: ValidMutIdempotenceFor<F, T, Option<U>, R, WAS_MUT>,
971 {
972 let f = crate::handoff_ref::with_ref_capture(|| {
973 let (expr, proof) =
974 f.splice_fnmut1_ctx_props(&OperatorContext::<L, B>::new(&self.location));
975 proof.register_proof(&expr);
976 expr.into()
977 });
978 Stream::new(
979 self.location.clone(),
980 HydroNode::FilterMap {
981 f,
982 input: Box::new(self.ir_node.replace(HydroNode::Placeholder)),
983 metadata: self
984 .location
985 .new_node_metadata(Stream::<U, L, B, O, R>::collection_kind()),
986 },
987 )
988 }
989
990 pub fn cross_singleton<O2>(
1015 self,
1016 other: impl Into<Optional<O2, L, Bounded>>,
1017 ) -> Stream<(T, O2), L, B, O, R>
1018 where
1019 O2: Clone,
1020 {
1021 let other: Optional<O2, L, Bounded> = other.into();
1022 check_matching_location(&self.location, &other.location);
1023
1024 Stream::new(
1025 self.location.clone(),
1026 HydroNode::CrossSingleton {
1027 left: Box::new(self.ir_node.replace(HydroNode::Placeholder)),
1028 right: Box::new(other.ir_node.replace(HydroNode::Placeholder)),
1029 metadata: self
1030 .location
1031 .new_node_metadata(Stream::<(T, O2), L, B, O, R>::collection_kind()),
1032 },
1033 )
1034 }
1035
1036 pub fn filter_if(self, signal: Singleton<bool, L, Bounded>) -> Stream<T, L, B, O, R> {
1068 self.cross_singleton(signal.filter(q!(|b| *b)))
1069 .map(q!(|(d, _)| d))
1070 }
1071
1072 #[deprecated(note = "use `filter_if` with `Optional::is_some()` instead")]
1107 pub fn filter_if_some<U>(self, signal: Optional<U, L, Bounded>) -> Stream<T, L, B, O, R> {
1108 self.filter_if(signal.is_some())
1109 }
1110
1111 #[deprecated(note = "use `filter_if` with `!Optional::is_some()` instead")]
1146 pub fn filter_if_none<U>(self, other: Optional<U, L, Bounded>) -> Stream<T, L, B, O, R> {
1147 self.filter_if(other.is_none())
1148 }
1149
1150 pub fn cross_product<T2, B2: Boundedness, O2: Ordering, R2: Retries>(
1175 self,
1176 other: Stream<T2, L, B2, O2, R2>,
1177 ) -> Stream<(T, T2), L, B, B2::PreserveOrderIfBounded<O>, <R as MinRetries<R2>>::Min>
1178 where
1179 T: Clone,
1180 T2: Clone,
1181 R: MinRetries<R2>,
1182 {
1183 self.map(q!(|v| ((), v)))
1184 .join(other.map(q!(|v| ((), v))))
1185 .map(q!(|((), (v1, v2))| (v1, v2)))
1186 }
1187
1188 pub fn unique(self) -> Stream<T, L, B, O, ExactlyOnce>
1207 where
1208 T: Eq + Hash,
1209 {
1210 Stream::new(
1211 self.location.clone(),
1212 HydroNode::Unique {
1213 input: Box::new(self.ir_node.replace(HydroNode::Placeholder)),
1214 metadata: self
1215 .location
1216 .new_node_metadata(Stream::<T, L, B, O, ExactlyOnce>::collection_kind()),
1217 },
1218 )
1219 }
1220
1221 pub fn filter_not_in<O2: Ordering, B2>(self, other: Stream<T, L, B2, O2, R>) -> Self
1247 where
1248 T: Eq + Hash,
1249 B2: IsBounded,
1250 {
1251 check_matching_location(&self.location, &other.location);
1252
1253 Stream::new(
1254 self.location.clone(),
1255 HydroNode::Difference {
1256 pos: Box::new(self.ir_node.replace(HydroNode::Placeholder)),
1257 neg: Box::new(other.ir_node.replace(HydroNode::Placeholder)),
1258 metadata: self
1259 .location
1260 .new_node_metadata(Stream::<T, L, Bounded, O, R>::collection_kind()),
1261 },
1262 )
1263 }
1264
1265 pub fn inspect<F, C, Idemp, const WAS_MUT: bool>(
1286 self,
1287 f: impl IntoQuotedMut<
1288 'a,
1289 F,
1290 OperatorContext<L::DropConsistency, B>,
1291 StreamMapFuncAlgebra<T, B, C, Idemp>,
1292 >,
1293 ) -> Self
1294 where
1295 F: FnMut(&T) + 'a,
1296 C: ValidMutBorrowCommutativityFor<F, T, (), O, WAS_MUT>,
1297 Idemp: ValidMutBorrowIdempotenceFor<F, T, (), R, WAS_MUT>,
1298 {
1299 let f = crate::handoff_ref::with_ref_capture(|| {
1300 let (expr, proof) =
1301 f.splice_fnmut1_borrow_ctx_props(&OperatorContext::<L::DropConsistency, B>::new(
1302 &self.location.drop_consistency(),
1303 ));
1304 proof.register_proof(&expr);
1305 expr.into()
1306 });
1307
1308 Stream::new(
1309 self.location.clone(),
1310 HydroNode::Inspect {
1311 f,
1312 input: Box::new(self.ir_node.replace(HydroNode::Placeholder)),
1313 metadata: self.location.new_node_metadata(Self::collection_kind()),
1314 },
1315 )
1316 }
1317
1318 pub fn for_each<F: FnMut(T) + 'a, C, I>(
1336 self,
1337 f: impl IntoQuotedMut<'a, F, OperatorContext<L, B>, AggFuncAlgebra<T, B, C, I>>,
1338 ) where
1339 C: ValidCommutativityFor<O>,
1340 I: ValidIdempotenceFor<R>,
1341 {
1342 let f = crate::handoff_ref::with_ref_capture(|| {
1343 let (f, proof) =
1344 f.splice_fnmut1_ctx_props(&OperatorContext::<L, B>::new(&self.location));
1345 proof.register_proof(&f);
1346 f.into()
1347 });
1348 self.location
1349 .flow_state()
1350 .borrow_mut()
1351 .push_root(HydroRoot::ForEach {
1352 input: Box::new(self.ir_node.replace(HydroNode::Placeholder)),
1353 f,
1354 op_metadata: HydroIrOpMetadata::new(),
1355 });
1356 }
1357
1358 pub fn dest_sink<S>(self, sink: impl QuotedWithContext<'a, S, L>)
1364 where
1365 O: IsOrdered,
1366 R: IsExactlyOnce,
1367 S: 'a + futures::Sink<T> + Unpin,
1368 {
1369 self.location
1370 .flow_state()
1371 .borrow_mut()
1372 .push_root(HydroRoot::DestSink {
1373 sink: sink.splice_typed_ctx(&self.location).into(),
1374 input: Box::new(self.ir_node.replace(HydroNode::Placeholder)),
1375 op_metadata: HydroIrOpMetadata::new(),
1376 });
1377 }
1378
1379 pub fn enumerate(self) -> Stream<(usize, T), L, B, O, R>
1399 where
1400 O: IsOrdered,
1401 R: IsExactlyOnce,
1402 {
1403 Stream::new(
1404 self.location.clone(),
1405 HydroNode::Enumerate {
1406 input: Box::new(self.ir_node.replace(HydroNode::Placeholder)),
1407 metadata: self.location.new_node_metadata(Stream::<
1408 (usize, T),
1409 L,
1410 B,
1411 TotalOrder,
1412 ExactlyOnce,
1413 >::collection_kind()),
1414 },
1415 )
1416 }
1417
1418 pub fn fold<A, I, F, C, Idemp, M, B2: SingletonBound>(
1442 self,
1443 init: impl IntoQuotedMut<'a, I, OperatorContext<L, B>>,
1444 comb: impl IntoQuotedMut<'a, F, OperatorContext<L, B>, AggFuncAlgebra<T, B, C, Idemp, M>>,
1445 ) -> Singleton<A, L, B2>
1446 where
1447 I: Fn() -> A + 'a,
1448 F: 'a + Fn(&mut A, T),
1449 C: ValidCommutativityFor<O>,
1450 Idemp: ValidIdempotenceFor<R>,
1451 B: ApplyMonotoneStream<M, B2>,
1452 {
1453 let init = init
1454 .splice_fn0_ctx(&OperatorContext::<L, B>::new(&self.location))
1455 .into();
1456 let (comb, proof) =
1457 comb.splice_fn2_borrow_mut_ctx_props(&OperatorContext::<L, B>::new(&self.location));
1458 let ordering_hook = proof.register_proof(&comb);
1459
1460 let nondet = nondet!();
1464 let retried: Stream<T, L::DropConsistency, B, O, ExactlyOnce> = self.assume_retries(nondet);
1465
1466 let mut metadata = retried
1467 .location
1468 .new_node_metadata(Singleton::<A, L::DropConsistency, B2>::collection_kind());
1469 metadata.op.sim_hook_id = ordering_hook.map(|hook| hook.id);
1470
1471 let core = HydroNode::Fold {
1472 init,
1473 acc: comb.into(),
1474 input: Box::new(retried.ir_node.replace(HydroNode::Placeholder)),
1475 metadata,
1476 };
1481
1482 Singleton::new(retried.location.clone(), core)
1483 .assert_has_consistency_of(manual_proof!())
1484 }
1485
1486 pub fn reduce<F, C, Idemp>(
1509 self,
1510 comb: impl IntoQuotedMut<'a, F, OperatorContext<L, B>, AggFuncAlgebra<T, B, C, Idemp>>,
1511 ) -> Optional<T, L, B::AggregatedOptional>
1512 where
1513 F: Fn(&mut T, T) + 'a,
1514 C: ValidCommutativityFor<O>,
1515 Idemp: ValidIdempotenceFor<R>,
1516 {
1517 let (f, proof) =
1518 comb.splice_fn2_borrow_mut_ctx_props(&OperatorContext::<L, B>::new(&self.location));
1519 let ordering_hook = proof.register_proof(&f);
1520
1521 let nondet_retries = nondet!();
1522 let ordered_etc: Stream<T, L::DropConsistency, B> =
1523 self.assume_retries(nondet_retries).assume_ordering(nondet!(
1524 hook = ordering_hook
1527 ));
1528
1529 let core = HydroNode::Reduce {
1530 f: f.into(),
1531 input: Box::new(ordered_etc.ir_node.replace(HydroNode::Placeholder)),
1532 metadata: ordered_etc.location.new_node_metadata(Optional::<
1533 T,
1534 L::DropConsistency,
1535 B::AggregatedOptional,
1536 >::collection_kind()),
1537 };
1538
1539 Optional::new(ordered_etc.location.clone(), core)
1540 .assert_has_consistency_of(manual_proof!())
1541 }
1542
1543 pub fn max(self) -> Optional<T, L, B::AggregatedOptional>
1563 where
1564 T: Ord,
1565 {
1566 self.assume_retries_trusted::<ExactlyOnce>(nondet!())
1567 .assume_ordering_trusted_bounded::<TotalOrder>(
1568 nondet!(),
1569 )
1570 .reduce(q!(|curr, new| {
1571 if new > *curr {
1572 *curr = new;
1573 }
1574 }))
1575 }
1576
1577 pub fn min(self) -> Optional<T, L, B::AggregatedOptional>
1597 where
1598 T: Ord,
1599 {
1600 self.assume_retries_trusted::<ExactlyOnce>(nondet!())
1601 .assume_ordering_trusted_bounded::<TotalOrder>(
1602 nondet!(),
1603 )
1604 .reduce(q!(|curr, new| {
1605 if new < *curr {
1606 *curr = new;
1607 }
1608 }))
1609 }
1610
1611 pub fn first(self) -> Optional<T, L, B::AggregatedOptional>
1634 where
1635 O: IsOrdered,
1636 {
1637 self.make_totally_ordered()
1638 .assume_retries_trusted::<ExactlyOnce>(nondet!())
1639 .generator(q!(|| ()), q!(|_, item| Generate::Return(item)))
1640 .reduce(q!(|_, _| {}))
1641 }
1642
1643 pub fn last(self) -> Optional<T, L, B::AggregatedOptional>
1666 where
1667 O: IsOrdered,
1668 {
1669 self.make_totally_ordered()
1670 .assume_retries_trusted::<ExactlyOnce>(nondet!())
1671 .reduce(q!(|curr, new| *curr = new))
1672 }
1673
1674 pub fn limit(
1697 self,
1698 n: impl QuotedWithContext<'a, usize, OperatorContext<L, B>> + Copy + 'a,
1699 ) -> Stream<T, L, B, TotalOrder, ExactlyOnce>
1700 where
1701 O: IsOrdered,
1702 R: IsExactlyOnce,
1703 {
1704 self.generator(
1705 q!(|| 0usize),
1706 q!(move |count, item| {
1707 if *count == n {
1708 Generate::Break
1709 } else {
1710 *count += 1;
1711 if *count == n {
1712 Generate::Return(item)
1713 } else {
1714 Generate::Yield(item)
1715 }
1716 }
1717 }),
1718 )
1719 }
1720
1721 pub fn collect_vec(self) -> Singleton<Vec<T>, L, B>
1747 where
1748 O: IsOrdered,
1749 R: IsExactlyOnce,
1750 {
1751 self.make_totally_ordered().make_exactly_once().fold(
1752 q!(|| vec![]),
1753 q!(|acc, v| {
1754 acc.push(v);
1755 }),
1756 )
1757 }
1758
1759 pub fn scan<A, U, I, F>(
1825 self,
1826 init: impl IntoQuotedMut<'a, I, OperatorContext<L, B>>,
1827 f: impl IntoQuotedMut<'a, F, OperatorContext<L, B>>,
1828 ) -> Stream<U, L, B, TotalOrder, ExactlyOnce>
1829 where
1830 O: IsOrdered,
1831 R: IsExactlyOnce,
1832 I: Fn() -> A + 'a,
1833 F: Fn(&mut A, T) -> Option<U> + 'a,
1834 {
1835 let init = crate::handoff_ref::with_ref_capture(|| {
1836 init.splice_fn0_ctx(&OperatorContext::<L, B>::new(&self.location))
1837 .into()
1838 });
1839 let f = crate::handoff_ref::with_ref_capture(|| {
1840 f.splice_fn2_borrow_mut_ctx(&OperatorContext::<L, B>::new(&self.location))
1841 .into()
1842 });
1843
1844 Stream::new(
1845 self.location.clone(),
1846 HydroNode::Scan {
1847 init,
1848 acc: f,
1849 input: Box::new(self.ir_node.replace(HydroNode::Placeholder)),
1850 metadata: self.location.new_node_metadata(
1851 Stream::<U, L, B, TotalOrder, ExactlyOnce>::collection_kind(),
1852 ),
1853 },
1854 )
1855 }
1856
1857 pub fn scan_async_blocking<A, U, I, F, Fut>(
1896 self,
1897 init: impl IntoQuotedMut<'a, I, OperatorContext<L, B>>,
1898 f: impl IntoQuotedMut<'a, F, OperatorContext<L, B>>,
1899 ) -> Stream<U, L, B, TotalOrder, ExactlyOnce>
1900 where
1901 O: IsOrdered,
1902 R: IsExactlyOnce,
1903 I: Fn() -> A + 'a,
1904 F: Fn(&mut A, T) -> Fut + 'a,
1905 Fut: Future<Output = Option<U>> + 'a,
1906 {
1907 let init = crate::handoff_ref::with_ref_capture(|| {
1908 init.splice_fn0_ctx(&OperatorContext::<L, B>::new(&self.location))
1909 .into()
1910 });
1911 let f = crate::handoff_ref::with_ref_capture(|| {
1912 f.splice_fn2_borrow_mut_ctx(&OperatorContext::<L, B>::new(&self.location))
1913 .into()
1914 });
1915
1916 Stream::new(
1917 self.location.clone(),
1918 HydroNode::ScanAsyncBlocking {
1919 init,
1920 acc: f,
1921 input: Box::new(self.ir_node.replace(HydroNode::Placeholder)),
1922 metadata: self.location.new_node_metadata(
1923 Stream::<U, L, B, TotalOrder, ExactlyOnce>::collection_kind(),
1924 ),
1925 },
1926 )
1927 }
1928
1929 pub fn generator<A, U, I, F>(
1974 self,
1975 init: impl IntoQuotedMut<'a, I, OperatorContext<L, B>> + Copy,
1976 f: impl IntoQuotedMut<'a, F, OperatorContext<L, B>> + Copy,
1977 ) -> Stream<U, L, B, TotalOrder, ExactlyOnce>
1978 where
1979 O: IsOrdered,
1980 R: IsExactlyOnce,
1981 I: Fn() -> A + 'a,
1982 F: Fn(&mut A, T) -> Generate<U> + 'a,
1983 {
1984 let init: ManualExpr<I, _> =
1985 ManualExpr::new(move |ctx: &OperatorContext<L, B>| init.splice_fn0_ctx(ctx));
1986 let f: ManualExpr<F, _> =
1987 ManualExpr::new(move |ctx: &OperatorContext<L, B>| f.splice_fn2_borrow_mut_ctx(ctx));
1988
1989 let this = self.make_totally_ordered().make_exactly_once();
1990
1991 let scan_init = crate::handoff_ref::with_ref_capture(|| {
1996 q!(|| None)
1997 .splice_fn0_ctx::<Option<Option<A>>>(&this.location)
1998 .into()
1999 });
2000 let scan_f = crate::handoff_ref::with_ref_capture(|| {
2001 q!(move |state: &mut Option<Option<_>>, v| {
2002 if state.is_none() {
2003 *state = Some(Some(init()));
2004 }
2005 match state {
2006 Some(Some(state_value)) => match f(state_value, v) {
2007 Generate::Yield(out) => Some(Some(out)),
2008 Generate::Return(out) => {
2009 *state = Some(None);
2010 Some(Some(out))
2011 }
2012 Generate::Break => None,
2016 Generate::Continue => Some(None),
2017 },
2018 _ => None,
2020 }
2021 })
2022 .splice_fn2_borrow_mut_ctx::<Option<Option<A>>, T, _>(&OperatorContext::<L, B>::new(
2023 &this.location,
2024 ))
2025 .into()
2026 });
2027
2028 let scan_node = HydroNode::Scan {
2029 init: scan_init,
2030 acc: scan_f,
2031 input: Box::new(this.ir_node.replace(HydroNode::Placeholder)),
2032 metadata: this.location.new_node_metadata(Stream::<
2033 Option<U>,
2034 L,
2035 B,
2036 TotalOrder,
2037 ExactlyOnce,
2038 >::collection_kind()),
2039 };
2040
2041 let flatten_f = q!(|d| d)
2042 .splice_fn1_ctx::<Option<U>, _>(&this.location)
2043 .into();
2044 let flatten_node = HydroNode::FlatMap {
2045 f: flatten_f,
2046 input: Box::new(scan_node),
2047 metadata: this
2048 .location
2049 .new_node_metadata(Stream::<U, L, B, TotalOrder, ExactlyOnce>::collection_kind()),
2050 };
2051
2052 Stream::new(this.location.clone(), flatten_node)
2053 }
2054
2055 #[cfg(feature = "tokio")]
2068 pub fn sample_every(
2069 self,
2070 interval: impl QuotedWithContext<'a, std::time::Duration, L> + Copy + 'a,
2071 mut nondet: NonDet<(
2072 Option<crate::sim_hooks::BatchHook<T, O, R>>,
2073 Option<crate::sim_hooks::BatchHook<()>>,
2074 )>,
2075 ) -> Stream<T, L::DropConsistency, Unbounded, O, AtLeastOnce>
2076 where
2077 L: TopLevel<'a>,
2078 {
2079 let samples = self.location.source_interval(interval);
2080 let (elements_hook, samples_hook) = nondet.take_hook();
2081
2082 let tick = self.location.tick();
2083 self.batch(
2084 &tick,
2085 nondet!(
2086 hook = elements_hook
2088 ),
2089 )
2090 .filter_if(
2091 samples
2092 .batch(
2093 &tick,
2094 nondet!(
2095 hook = samples_hook
2097 ),
2098 )
2099 .first()
2100 .is_some(),
2101 )
2102 .all_ticks()
2103 .weaken_retries()
2104 }
2105
2106 #[cfg(feature = "tokio")]
2116 pub fn timeout(
2117 self,
2118 duration: impl QuotedWithContext<
2119 'a,
2120 std::time::Duration,
2121 OperatorContext<Tick<L::DropConsistency>, Bounded>,
2122 > + Copy
2123 + 'a,
2124 nondet: NonDet,
2125 ) -> Optional<(), L::DropConsistency, Unbounded>
2126 where
2127 L: TopLevel<'a>,
2128 {
2129 let tick = self.location.tick();
2130
2131 let latest_received = self.assume_retries::<ExactlyOnce>(nondet).fold(
2132 q!(|| None),
2133 q!(
2134 |latest, _| {
2135 *latest = Some(Instant::now());
2136 },
2137 commutative = manual_proof!()
2138 ),
2139 );
2140
2141 latest_received
2142 .snapshot(
2143 &tick,
2144 nondet!(
2145 nondet
2147 ),
2148 )
2149 .filter_map(q!(move |latest_received| {
2150 if let Some(latest_received) = latest_received {
2151 if Instant::now().duration_since(latest_received) > duration {
2152 Some(())
2153 } else {
2154 None
2155 }
2156 } else {
2157 Some(())
2158 }
2159 }))
2160 .latest()
2161 }
2162
2163 pub fn atomic(self) -> Stream<T, Atomic<L>, B, O, R>
2169 where
2170 L: TopLevel<'a>,
2171 {
2172 let out_location = Atomic {
2173 tick: self.location.tick(),
2174 };
2175 Stream::new(
2176 out_location.clone(),
2177 HydroNode::BeginAtomic {
2178 inner: Box::new(self.ir_node.replace(HydroNode::Placeholder)),
2179 metadata: out_location
2180 .new_node_metadata(Stream::<T, Atomic<L>, B, O, R>::collection_kind()),
2181 },
2182 )
2183 }
2184
2185 pub fn batch<L2: Location<'a, DropConsistency = L::DropConsistency>>(
2197 self,
2198 tick: &Tick<L2>,
2199 mut nondet: NonDet<Option<crate::sim_hooks::BatchHook<T, O, R>>>,
2200 ) -> Stream<T, Tick<L::DropConsistency>, Bounded, O, R> {
2201 assert_eq!(
2202 Location::id(tick.parent_location()),
2203 Location::id(&self.location)
2204 );
2205
2206 let mut metadata =
2207 tick.new_node_metadata(Stream::<T, Tick<L>, Bounded, O, R>::collection_kind());
2208 metadata.op.sim_hook_id = nondet.take_hook().map(|h| h.id);
2209 Stream::new(
2210 tick.drop_consistency(),
2211 HydroNode::Batch {
2212 inner: Box::new(self.ir_node.replace(HydroNode::Placeholder)),
2213 metadata,
2214 },
2215 )
2216 }
2217
2218 pub fn ir_node_named(self, name: &str) -> Stream<T, L, B, O, R> {
2221 {
2222 let mut node = self.ir_node.borrow_mut();
2223 let metadata = node.metadata_mut();
2224 metadata.tag = Some(name.to_owned());
2225 }
2226 self
2227 }
2228
2229 pub(crate) fn cast_at_most_one_element(self) -> Optional<T, L, B>
2233 where
2234 B: IsBounded,
2235 {
2236 Optional::new(
2237 self.location.clone(),
2238 HydroNode::Cast {
2239 inner: Box::new(self.ir_node.replace(HydroNode::Placeholder)),
2240 metadata: self
2241 .location
2242 .new_node_metadata(Optional::<T, L, B>::collection_kind()),
2243 },
2244 )
2245 }
2246
2247 pub(crate) fn use_ordering_type<O2: Ordering>(self) -> Stream<T, L, B, O2, R> {
2248 if O::ORDERING_KIND == O2::ORDERING_KIND {
2249 Stream::new(
2250 self.location.clone(),
2251 self.ir_node.replace(HydroNode::Placeholder),
2252 )
2253 } else {
2254 panic!(
2255 "Runtime ordering {:?} did not match requested cast {:?}.",
2256 O::ORDERING_KIND,
2257 O2::ORDERING_KIND
2258 )
2259 }
2260 }
2261
2262 pub fn assume_ordering<O2: Ordering>(
2271 self,
2272 mut nondet: NonDet<Option<crate::sim_hooks::OrderingHook<T, B>>>,
2273 ) -> Stream<T, L::DropConsistency, B, O2, R> {
2274 if O::ORDERING_KIND == O2::ORDERING_KIND {
2275 self.use_ordering_type().weaken_consistency()
2276 } else if O2::ORDERING_KIND == StreamOrder::NoOrder {
2277 let target_location = self.location().drop_consistency();
2279 Stream::new(
2280 target_location.clone(),
2281 HydroNode::Cast {
2282 inner: Box::new(self.ir_node.replace(HydroNode::Placeholder)),
2283 metadata: target_location
2284 .new_node_metadata(Stream::<T, L, B, O2, R>::collection_kind()),
2285 },
2286 )
2287 } else {
2288 let target_location = self.location().drop_consistency();
2289 let mut metadata =
2290 target_location.new_node_metadata(Stream::<T, L, B, O2, R>::collection_kind());
2291 metadata.op.sim_hook_id = nondet.take_hook().map(|hook| hook.id);
2292 Stream::new(
2293 target_location,
2294 HydroNode::ObserveNonDet {
2295 inner: Box::new(self.ir_node.replace(HydroNode::Placeholder)),
2296 trusted: false,
2297 metadata,
2298 },
2299 )
2300 }
2301 }
2302
2303 fn assume_ordering_trusted_bounded<O2: Ordering>(
2306 self,
2307 nondet: NonDet,
2308 ) -> Stream<T, L, B, O2, R> {
2309 if B::BOUNDED {
2310 self.assume_ordering_trusted(nondet)
2311 } else {
2312 let self_location = self.location.clone();
2313 let inner: Stream<T, L::DropConsistency, B, O2, R> = self.assume_ordering(nondet!(
2314 nondet
2316 ));
2317 Stream::new(self_location, inner.ir_node.replace(HydroNode::Placeholder))
2318 }
2319 }
2320
2321 pub(crate) fn assume_ordering_trusted<O2: Ordering>(
2324 self,
2325 _nondet: NonDet,
2326 ) -> Stream<T, L, B, O2, R> {
2327 if O::ORDERING_KIND == O2::ORDERING_KIND {
2328 self.use_ordering_type()
2329 } else if O2::ORDERING_KIND == StreamOrder::NoOrder {
2330 Stream::new(
2332 self.location.clone(),
2333 HydroNode::Cast {
2334 inner: Box::new(self.ir_node.replace(HydroNode::Placeholder)),
2335 metadata: self
2336 .location
2337 .new_node_metadata(Stream::<T, L, B, O2, R>::collection_kind()),
2338 },
2339 )
2340 } else {
2341 Stream::new(
2342 self.location.clone(),
2343 HydroNode::ObserveNonDet {
2344 inner: Box::new(self.ir_node.replace(HydroNode::Placeholder)),
2345 trusted: true,
2346 metadata: self
2347 .location
2348 .new_node_metadata(Stream::<T, L, B, O2, R>::collection_kind()),
2349 },
2350 )
2351 }
2352 }
2353
2354 #[deprecated = "use `weaken_ordering::<NoOrder>()` instead"]
2355 pub fn weakest_ordering(self) -> Stream<T, L, B, NoOrder, R> {
2358 self.weaken_ordering::<NoOrder>()
2359 }
2360
2361 pub fn weaken_ordering<O2: WeakerOrderingThan<O>>(self) -> Stream<T, L, B, O2, R> {
2364 let nondet = nondet!();
2365 self.assume_ordering_trusted::<O2>(nondet)
2366 }
2367
2368 pub fn make_totally_ordered(self) -> Stream<T, L, B, TotalOrder, R>
2371 where
2372 O: IsOrdered,
2373 {
2374 self.assume_ordering_trusted(nondet!())
2375 }
2376
2377 pub fn assume_retries<R2: Retries>(
2386 self,
2387 _nondet: NonDet,
2388 ) -> Stream<T, L::DropConsistency, B, O, R2> {
2389 if R::RETRIES_KIND == R2::RETRIES_KIND {
2390 Stream::new(
2391 self.location.drop_consistency(),
2392 self.ir_node.replace(HydroNode::Placeholder),
2393 )
2394 } else if R2::RETRIES_KIND == StreamRetry::AtLeastOnce {
2395 let target_location = self.location.drop_consistency();
2397 Stream::new(
2398 target_location.clone(),
2399 HydroNode::Cast {
2400 inner: Box::new(self.ir_node.replace(HydroNode::Placeholder)),
2401 metadata: target_location
2402 .new_node_metadata(Stream::<T, L, B, O, R2>::collection_kind()),
2403 },
2404 )
2405 } else {
2406 let target_location = self.location.drop_consistency();
2407 Stream::new(
2408 target_location.clone(),
2409 HydroNode::ObserveNonDet {
2410 inner: Box::new(self.ir_node.replace(HydroNode::Placeholder)),
2411 trusted: false,
2412 metadata: target_location
2413 .new_node_metadata(Stream::<T, L, B, O, R2>::collection_kind()),
2414 },
2415 )
2416 }
2417 }
2418
2419 fn assume_retries_trusted<R2: Retries>(self, _nondet: NonDet) -> Stream<T, L, B, O, R2> {
2422 if R::RETRIES_KIND == R2::RETRIES_KIND {
2423 Stream::new(
2424 self.location.clone(),
2425 self.ir_node.replace(HydroNode::Placeholder),
2426 )
2427 } else if R2::RETRIES_KIND == StreamRetry::AtLeastOnce {
2428 Stream::new(
2430 self.location.clone(),
2431 HydroNode::Cast {
2432 inner: Box::new(self.ir_node.replace(HydroNode::Placeholder)),
2433 metadata: self
2434 .location
2435 .new_node_metadata(Stream::<T, L, B, O, R2>::collection_kind()),
2436 },
2437 )
2438 } else {
2439 Stream::new(
2440 self.location.clone(),
2441 HydroNode::ObserveNonDet {
2442 inner: Box::new(self.ir_node.replace(HydroNode::Placeholder)),
2443 trusted: true,
2444 metadata: self
2445 .location
2446 .new_node_metadata(Stream::<T, L, B, O, R2>::collection_kind()),
2447 },
2448 )
2449 }
2450 }
2451
2452 #[deprecated = "use `weaken_retries::<AtLeastOnce>()` instead"]
2453 pub fn weakest_retries(self) -> Stream<T, L, B, O, AtLeastOnce> {
2456 self.weaken_retries::<AtLeastOnce>()
2457 }
2458
2459 pub fn weaken_retries<R2: WeakerRetryThan<R>>(self) -> Stream<T, L, B, O, R2> {
2462 let nondet = nondet!();
2463 self.assume_retries_trusted::<R2>(nondet)
2464 }
2465
2466 pub fn make_exactly_once(self) -> Stream<T, L, B, O, ExactlyOnce>
2469 where
2470 R: IsExactlyOnce,
2471 {
2472 self.assume_retries_trusted(nondet!())
2473 }
2474
2475 pub fn make_bounded(self) -> Stream<T, L, Bounded, O, R>
2478 where
2479 B: IsBounded,
2480 {
2481 self.weaken_boundedness()
2482 }
2483
2484 pub fn weaken_boundedness<B2: Boundedness>(self) -> Stream<T, L, B2, O, R> {
2487 if B::BOUNDED == B2::BOUNDED {
2488 Stream::new(
2489 self.location.clone(),
2490 self.ir_node.replace(HydroNode::Placeholder),
2491 )
2492 } else {
2493 Stream::new(
2495 self.location.clone(),
2496 HydroNode::Cast {
2497 inner: Box::new(self.ir_node.replace(HydroNode::Placeholder)),
2498 metadata: self
2499 .location
2500 .new_node_metadata(Stream::<T, L, B2, O, R>::collection_kind()),
2501 },
2502 )
2503 }
2504 }
2505}
2506
2507impl<'a, T, L, B: Boundedness, O: Ordering, R: Retries> Stream<&T, L, B, O, R>
2508where
2509 L: Location<'a>,
2510{
2511 pub fn cloned(self) -> Stream<T, L, B, O, R>
2529 where
2530 T: Clone,
2531 {
2532 self.map(q!(|d| d.clone()))
2533 }
2534}
2535
2536impl<'a, T, L, B: Boundedness, O: Ordering> Stream<T, L, B, O, ExactlyOnce>
2537where
2538 L: Location<'a>,
2539{
2540 pub fn count(self) -> Singleton<usize, L, B::StreamToMonotone> {
2559 self.assume_ordering_trusted::<TotalOrder>(nondet!(
2560 ))
2562 .fold(
2563 q!(|| 0usize),
2564 q!(
2565 |count, _| *count += 1,
2566 monotone = manual_proof!()
2567 ),
2568 )
2569 }
2570}
2571
2572impl<'a, T, L: Location<'a>, O: Ordering, R: Retries> Stream<T, L, Unbounded, O, R> {
2573 pub fn merge_unordered<O2: Ordering, R2: Retries>(
2597 self,
2598 other: Stream<T, L, Unbounded, O2, R2>,
2599 ) -> Stream<T, L, Unbounded, NoOrder, <R as MinRetries<R2>>::Min>
2600 where
2601 R: MinRetries<R2>,
2602 {
2603 Stream::new(
2604 self.location.clone(),
2605 HydroNode::Chain {
2606 first: Box::new(self.ir_node.replace(HydroNode::Placeholder)),
2607 second: Box::new(other.ir_node.replace(HydroNode::Placeholder)),
2608 metadata: self.location.new_node_metadata(Stream::<
2609 T,
2610 L,
2611 Unbounded,
2612 NoOrder,
2613 <R as MinRetries<R2>>::Min,
2614 >::collection_kind()),
2615 },
2616 )
2617 }
2618
2619 #[deprecated(note = "use `merge_unordered` instead")]
2621 pub fn interleave<O2: Ordering, R2: Retries>(
2622 self,
2623 other: Stream<T, L, Unbounded, O2, R2>,
2624 ) -> Stream<T, L, Unbounded, NoOrder, <R as MinRetries<R2>>::Min>
2625 where
2626 R: MinRetries<R2>,
2627 {
2628 self.merge_unordered(other)
2629 }
2630}
2631
2632impl<'a, T, L: Location<'a>, B: Boundedness, R: Retries> Stream<T, L, B, TotalOrder, R> {
2633 pub fn merge_ordered<R2: Retries>(
2661 self,
2662 other: Stream<T, L, B, TotalOrder, R2>,
2663 _nondet: NonDet,
2664 ) -> Stream<T, L::DropConsistency, B, TotalOrder, <R as MinRetries<R2>>::Min>
2665 where
2666 R: MinRetries<R2>,
2667 {
2668 let target_location = self.location().drop_consistency();
2669 Stream::new(
2670 target_location.clone(),
2671 HydroNode::MergeOrdered {
2672 first: Box::new(self.ir_node.replace(HydroNode::Placeholder)),
2673 second: Box::new(other.ir_node.replace(HydroNode::Placeholder)),
2674 metadata: target_location.new_node_metadata(Stream::<
2675 T,
2676 L::DropConsistency,
2677 B,
2678 TotalOrder,
2679 <R as MinRetries<R2>>::Min,
2680 >::collection_kind()),
2681 },
2682 )
2683 }
2684}
2685
2686impl<'a, T, L, B: Boundedness, O: Ordering, R: Retries> Stream<T, L, B, O, R>
2687where
2688 L: Location<'a>,
2689{
2690 pub fn sort(self) -> Stream<T, L, Bounded, TotalOrder, R>
2716 where
2717 B: IsBounded,
2718 T: Ord,
2719 {
2720 let this = self.make_bounded();
2721 Stream::new(
2722 this.location.clone(),
2723 HydroNode::Sort {
2724 input: Box::new(this.ir_node.replace(HydroNode::Placeholder)),
2725 metadata: this
2726 .location
2727 .new_node_metadata(Stream::<T, L, Bounded, TotalOrder, R>::collection_kind()),
2728 },
2729 )
2730 }
2731
2732 pub fn chain<O2: Ordering, R2: Retries, B2: Boundedness>(
2760 self,
2761 other: Stream<T, L, B2, O2, R2>,
2762 ) -> Stream<T, L, B2, <O as MinOrder<O2>>::Min, <R as MinRetries<R2>>::Min>
2763 where
2764 B: IsBounded,
2765 O: MinOrder<O2>,
2766 R: MinRetries<R2>,
2767 {
2768 check_matching_location(&self.location, &other.location);
2769
2770 Stream::new(
2771 self.location.clone(),
2772 HydroNode::Chain {
2773 first: Box::new(self.ir_node.replace(HydroNode::Placeholder)),
2774 second: Box::new(other.ir_node.replace(HydroNode::Placeholder)),
2775 metadata: self.location.new_node_metadata(Stream::<
2776 T,
2777 L,
2778 B2,
2779 <O as MinOrder<O2>>::Min,
2780 <R as MinRetries<R2>>::Min,
2781 >::collection_kind()),
2782 },
2783 )
2784 }
2785
2786 pub fn cross_product_nested_loop<T2, O2: Ordering + MinOrder<O>, R2: Retries>(
2790 self,
2791 other: Stream<T2, L, Bounded, O2, R2>,
2792 ) -> Stream<(T, T2), L, Bounded, <O2 as MinOrder<O>>::Min, <R as MinRetries<R2>>::Min>
2793 where
2794 B: IsBounded,
2795 T: Clone,
2796 T2: Clone,
2797 R: MinRetries<R2>,
2798 {
2799 let this = self.make_bounded();
2800 check_matching_location(&this.location, &other.location);
2801
2802 Stream::new(
2803 this.location.clone(),
2804 HydroNode::CrossProduct {
2805 left: Box::new(this.ir_node.replace(HydroNode::Placeholder)),
2806 right: Box::new(other.ir_node.replace(HydroNode::Placeholder)),
2807 metadata: this.location.new_node_metadata(Stream::<
2808 (T, T2),
2809 L,
2810 Bounded,
2811 <O2 as MinOrder<O>>::Min,
2812 <R as MinRetries<R2>>::Min,
2813 >::collection_kind()),
2814 },
2815 )
2816 }
2817
2818 pub fn repeat_with_keys<K, V2>(
2856 self,
2857 keys: KeyedSingleton<K, V2, L, Bounded>,
2858 ) -> KeyedStream<K, T, L, Bounded, O, R>
2859 where
2860 B: IsBounded,
2861 K: Clone,
2862 T: Clone,
2863 {
2864 keys.keys()
2865 .assume_ordering_trusted::<TotalOrder>(
2866 nondet!(),
2867 )
2868 .cross_product_nested_loop(self.make_bounded())
2869 .into_keyed()
2870 }
2871
2872 pub fn resolve_futures_blocking(self) -> Stream<T::Output, L, B, NoOrder, R>
2909 where
2910 T: Future,
2911 {
2912 Stream::new(
2913 self.location.clone(),
2914 HydroNode::ResolveFuturesBlocking {
2915 input: Box::new(self.ir_node.replace(HydroNode::Placeholder)),
2916 metadata: self
2917 .location
2918 .new_node_metadata(Stream::<T::Output, L, B, NoOrder, R>::collection_kind()),
2919 },
2920 )
2921 }
2922
2923 #[expect(clippy::wrong_self_convention, reason = "stream function naming")]
2943 pub fn is_empty(self) -> Singleton<bool, L, Bounded>
2944 where
2945 B: IsBounded,
2946 {
2947 self.make_bounded()
2948 .assume_ordering_trusted::<TotalOrder>(
2949 nondet!(),
2950 )
2951 .first()
2952 .is_none()
2953 }
2954}
2955
2956impl<'a, K, V1, L, B: Boundedness, O: Ordering, R: Retries> Stream<(K, V1), L, B, O, R>
2957where
2958 L: Location<'a>,
2959{
2960 pub fn join<V2, B2: Boundedness, O2: Ordering, R2: Retries>(
2985 self,
2986 n: Stream<(K, V2), L, B2, O2, R2>,
2987 ) -> Stream<(K, (V1, V2)), L, B, B2::PreserveOrderIfBounded<O>, <R as MinRetries<R2>>::Min>
2988 where
2989 K: Eq + Hash + Clone,
2990 R: MinRetries<R2>,
2991 V1: Clone,
2992 V2: Clone,
2993 {
2994 check_matching_location(&self.location, &n.location);
2995
2996 let ir_node = if B2::BOUNDED {
2997 HydroNode::JoinHalf {
2998 left: Box::new(self.ir_node.replace(HydroNode::Placeholder)),
2999 right: Box::new(n.ir_node.replace(HydroNode::Placeholder)),
3000 metadata: self.location.new_node_metadata(Stream::<
3001 (K, (V1, V2)),
3002 L,
3003 B,
3004 B2::PreserveOrderIfBounded<O>,
3005 <R as MinRetries<R2>>::Min,
3006 >::collection_kind()),
3007 }
3008 } else {
3009 HydroNode::Join {
3010 left: Box::new(self.ir_node.replace(HydroNode::Placeholder)),
3011 right: Box::new(n.ir_node.replace(HydroNode::Placeholder)),
3012 metadata: self.location.new_node_metadata(Stream::<
3013 (K, (V1, V2)),
3014 L,
3015 B,
3016 B2::PreserveOrderIfBounded<O>,
3017 <R as MinRetries<R2>>::Min,
3018 >::collection_kind()),
3019 }
3020 };
3021
3022 Stream::new(self.location.clone(), ir_node)
3023 }
3024
3025 pub fn anti_join<O2: Ordering, R2: Retries>(
3051 self,
3052 n: Stream<K, L, Bounded, O2, R2>,
3053 ) -> Stream<(K, V1), L, B, O, R>
3054 where
3055 K: Eq + Hash,
3056 {
3057 check_matching_location(&self.location, &n.location);
3058
3059 Stream::new(
3060 self.location.clone(),
3061 HydroNode::AntiJoin {
3062 pos: Box::new(self.ir_node.replace(HydroNode::Placeholder)),
3063 neg: Box::new(n.ir_node.replace(HydroNode::Placeholder)),
3064 metadata: self
3065 .location
3066 .new_node_metadata(Stream::<(K, V1), L, B, O, R>::collection_kind()),
3067 },
3068 )
3069 }
3070}
3071
3072impl<'a, K, V, L: Location<'a>, B: Boundedness, O: Ordering, R: Retries>
3073 Stream<(K, V), L, B, O, R>
3074{
3075 pub fn into_keyed(self) -> KeyedStream<K, V, L, B, O, R> {
3102 KeyedStream::new(
3103 self.location.clone(),
3104 HydroNode::Cast {
3105 inner: Box::new(self.ir_node.replace(HydroNode::Placeholder)),
3106 metadata: self
3107 .location
3108 .new_node_metadata(KeyedStream::<K, V, L, B, O, R>::collection_kind()),
3109 },
3110 )
3111 }
3112}
3113
3114impl<'a, K, V, L, O: Ordering, R: Retries> Stream<(K, V), Tick<L>, Bounded, O, R>
3115where
3116 K: Eq + Hash,
3117 L: Location<'a>,
3118{
3119 pub fn keys(self) -> Stream<K, Tick<L>, Bounded, NoOrder, ExactlyOnce> {
3138 self.into_keyed()
3139 .fold(
3140 q!(|| ()),
3141 q!(
3142 |_, _| {},
3143 commutative = manual_proof!(),
3144 idempotent = manual_proof!()
3145 ),
3146 )
3147 .keys()
3148 }
3149}
3150
3151impl<'a, T, L, B: Boundedness, O: Ordering, R: Retries> Stream<T, Atomic<L>, B, O, R>
3152where
3153 L: Location<'a>,
3154{
3155 pub fn batch_atomic<L2: Location<'a, DropConsistency = L::DropConsistency>>(
3162 self,
3163 tick: &Tick<L2>,
3164 mut nondet: NonDet<Option<crate::sim_hooks::BatchHook<T, O, R>>>,
3165 ) -> Stream<T, Tick<L::DropConsistency>, Bounded, O, R> {
3166 assert_eq!(
3167 Location::id(tick.parent_location()),
3168 Location::id(self.location.tick.parent_location())
3169 );
3170
3171 let mut metadata =
3172 tick.new_node_metadata(Stream::<T, Tick<L>, Bounded, O, R>::collection_kind());
3173
3174 metadata.op.sim_hook_id = nondet.take_hook().map(|h| h.id);
3175 Stream::new(
3176 tick.drop_consistency(),
3177 HydroNode::Batch {
3178 inner: Box::new(self.ir_node.replace(HydroNode::Placeholder)),
3179 metadata,
3180 },
3181 )
3182 }
3183
3184 pub fn end_atomic(self) -> Stream<T, L, B, O, R> {
3187 Stream::new(
3188 self.location.tick.l.clone(),
3189 HydroNode::EndAtomic {
3190 inner: Box::new(self.ir_node.replace(HydroNode::Placeholder)),
3191 metadata: self
3192 .location
3193 .tick
3194 .l
3195 .new_node_metadata(Stream::<T, L, B, O, R>::collection_kind()),
3196 },
3197 )
3198 }
3199}
3200
3201impl<'a, F, T, L, B: Boundedness, O: Ordering, R: Retries> Stream<F, L, B, O, R>
3202where
3203 L: TopLevel<'a>,
3204 F: Future<Output = T>,
3205{
3206 pub fn resolve_futures(self) -> Stream<T, L, Unbounded, NoOrder, R> {
3237 Stream::new(
3238 self.location.clone(),
3239 HydroNode::ResolveFutures {
3240 input: Box::new(self.ir_node.replace(HydroNode::Placeholder)),
3241 metadata: self
3242 .location
3243 .new_node_metadata(Stream::<T, L, Unbounded, NoOrder, R>::collection_kind()),
3244 },
3245 )
3246 }
3247
3248 pub fn resolve_futures_ordered(self) -> Stream<T, L, Unbounded, O, R> {
3279 Stream::new(
3280 self.location.clone(),
3281 HydroNode::ResolveFuturesOrdered {
3282 input: Box::new(self.ir_node.replace(HydroNode::Placeholder)),
3283 metadata: self
3284 .location
3285 .new_node_metadata(Stream::<T, L, Unbounded, O, R>::collection_kind()),
3286 },
3287 )
3288 }
3289}
3290
3291impl<'a, T, L, O: Ordering, R: Retries> Stream<T, Tick<L>, Bounded, O, R>
3292where
3293 L: Location<'a>,
3294{
3295 pub fn all_ticks(self) -> Stream<T, L, Unbounded, O, R> {
3298 Stream::new(
3299 self.location.parent_location().clone(),
3300 HydroNode::YieldConcat {
3301 inner: Box::new(self.ir_node.replace(HydroNode::Placeholder)),
3302 metadata: self.location.parent_location().new_node_metadata(Stream::<
3303 T,
3304 L,
3305 Unbounded,
3306 O,
3307 R,
3308 >::collection_kind(
3309 )),
3310 },
3311 )
3312 }
3313
3314 pub fn all_ticks_atomic(self) -> Stream<T, Atomic<L>, Unbounded, O, R> {
3321 let out_location = Atomic {
3322 tick: self.location.clone(),
3323 };
3324
3325 Stream::new(
3326 out_location.clone(),
3327 HydroNode::YieldConcat {
3328 inner: Box::new(self.ir_node.replace(HydroNode::Placeholder)),
3329 metadata: out_location
3330 .new_node_metadata(Stream::<T, Atomic<L>, Unbounded, O, R>::collection_kind()),
3331 },
3332 )
3333 }
3334
3335 pub fn across_ticks<Out: BatchAtomic<'a>>(
3370 self,
3371 thunk: impl FnOnce(Stream<T, Atomic<L>, Unbounded, O, R>) -> Out,
3372 ) -> Out::Batched {
3373 thunk(self.all_ticks_atomic()).batched_atomic()
3374 }
3375
3376 pub fn defer_tick(self) -> Stream<T, Tick<L>, Bounded, O, R> {
3415 Stream::new(
3416 self.location.clone(),
3417 HydroNode::DeferTick {
3418 input: Box::new(self.ir_node.replace(HydroNode::Placeholder)),
3419 metadata: self
3420 .location
3421 .new_node_metadata(Stream::<T, Tick<L>, Bounded, O, R>::collection_kind()),
3422 },
3423 )
3424 }
3425}
3426
3427#[cfg(test)]
3428mod tests {
3429 #[cfg(feature = "deploy")]
3430 use futures::{SinkExt, StreamExt};
3431 #[cfg(feature = "deploy")]
3432 use hydro_deploy::Deployment;
3433 #[cfg(feature = "deploy")]
3434 use serde::{Deserialize, Serialize};
3435 #[cfg(any(feature = "deploy", feature = "sim"))]
3436 use stageleft::q;
3437
3438 #[cfg(any(feature = "deploy", feature = "sim"))]
3439 use crate::compile::builder::FlowBuilder;
3440 #[cfg(feature = "deploy")]
3441 use crate::live_collections::sliced::sliced;
3442 #[cfg(feature = "deploy")]
3443 use crate::live_collections::stream::ExactlyOnce;
3444 #[cfg(feature = "sim")]
3445 use crate::live_collections::stream::NoOrder;
3446 #[cfg(any(feature = "deploy", feature = "sim"))]
3447 use crate::live_collections::stream::TotalOrder;
3448 #[cfg(any(feature = "deploy", feature = "sim"))]
3449 use crate::location::Location;
3450 #[cfg(feature = "sim")]
3451 use crate::networking::TCP;
3452 #[cfg(any(feature = "deploy", feature = "sim"))]
3453 use crate::nondet::nondet;
3454
3455 mod backtrace_chained_ops;
3456
3457 #[cfg(feature = "deploy")]
3458 struct P1 {}
3459 #[cfg(feature = "deploy")]
3460 struct P2 {}
3461
3462 #[cfg(feature = "deploy")]
3463 #[derive(Serialize, Deserialize, Debug)]
3464 struct SendOverNetwork {
3465 n: u32,
3466 }
3467
3468 #[cfg(feature = "deploy")]
3469 #[tokio::test]
3470 async fn first_ten_distributed() {
3471 use crate::networking::TCP;
3472
3473 let mut deployment = Deployment::new();
3474
3475 let mut flow = FlowBuilder::new();
3476 let first_node = flow.process::<P1>();
3477 let second_node = flow.process::<P2>();
3478 let external = flow.external::<P2>();
3479
3480 let numbers = first_node.source_iter(q!(0..10));
3481 let out_port = numbers
3482 .map(q!(|n| SendOverNetwork { n }))
3483 .send(&second_node, TCP.fail_stop().bincode())
3484 .send_bincode_external(&external);
3485
3486 let nodes = flow
3487 .with_process(&first_node, deployment.Localhost())
3488 .with_process(&second_node, deployment.Localhost())
3489 .with_external(&external, deployment.Localhost())
3490 .deploy(&mut deployment);
3491
3492 deployment.deploy().await.unwrap();
3493
3494 let mut external_out = nodes.connect(out_port).await;
3495
3496 deployment.start().await.unwrap();
3497
3498 for i in 0..10 {
3499 assert_eq!(external_out.next().await.unwrap().n, i);
3500 }
3501 }
3502
3503 #[cfg(feature = "deploy")]
3504 #[tokio::test]
3505 async fn first_cardinality() {
3506 let mut deployment = Deployment::new();
3507
3508 let mut flow = FlowBuilder::new();
3509 let node = flow.process::<()>();
3510 let external = flow.external::<()>();
3511
3512 let node_tick = node.tick();
3513 let count = node_tick
3514 .singleton(q!([1, 2, 3]))
3515 .into_stream()
3516 .flatten_ordered()
3517 .first()
3518 .into_stream()
3519 .count()
3520 .all_ticks()
3521 .send_bincode_external(&external);
3522
3523 let nodes = flow
3524 .with_process(&node, deployment.Localhost())
3525 .with_external(&external, deployment.Localhost())
3526 .deploy(&mut deployment);
3527
3528 deployment.deploy().await.unwrap();
3529
3530 let mut external_out = nodes.connect(count).await;
3531
3532 deployment.start().await.unwrap();
3533
3534 assert_eq!(external_out.next().await.unwrap(), 1);
3535 }
3536
3537 #[cfg(feature = "deploy")]
3538 #[tokio::test]
3539 async fn unbounded_reduce_remembers_state() {
3540 let mut deployment = Deployment::new();
3541
3542 let mut flow = FlowBuilder::new();
3543 let node = flow.process::<()>();
3544 let external = flow.external::<()>();
3545
3546 let (input_port, input) = node.source_external_bincode(&external);
3547 let out = input
3548 .reduce(q!(|acc, v| *acc += v))
3549 .sample_eager(nondet!())
3550 .send_bincode_external(&external);
3551
3552 let nodes = flow
3553 .with_process(&node, deployment.Localhost())
3554 .with_external(&external, deployment.Localhost())
3555 .deploy(&mut deployment);
3556
3557 deployment.deploy().await.unwrap();
3558
3559 let mut external_in = nodes.connect(input_port).await;
3560 let mut external_out = nodes.connect(out).await;
3561
3562 deployment.start().await.unwrap();
3563
3564 external_in.send(1).await.unwrap();
3565 assert_eq!(external_out.next().await.unwrap(), 1);
3566
3567 external_in.send(2).await.unwrap();
3568 assert_eq!(external_out.next().await.unwrap(), 3);
3569 }
3570
3571 #[cfg(feature = "deploy")]
3572 #[tokio::test]
3573 async fn top_level_bounded_cross_singleton() {
3574 let mut deployment = Deployment::new();
3575
3576 let mut flow = FlowBuilder::new();
3577 let node = flow.process::<()>();
3578 let external = flow.external::<()>();
3579
3580 let (input_port, input) =
3581 node.source_external_bincode::<_, _, TotalOrder, ExactlyOnce>(&external);
3582
3583 let out = input
3584 .cross_singleton(
3585 node.source_iter(q!(vec![1, 2, 3]))
3586 .fold(q!(|| 0), q!(|acc, v| *acc += v)),
3587 )
3588 .send_bincode_external(&external);
3589
3590 let nodes = flow
3591 .with_process(&node, deployment.Localhost())
3592 .with_external(&external, deployment.Localhost())
3593 .deploy(&mut deployment);
3594
3595 deployment.deploy().await.unwrap();
3596
3597 let mut external_in = nodes.connect(input_port).await;
3598 let mut external_out = nodes.connect(out).await;
3599
3600 deployment.start().await.unwrap();
3601
3602 external_in.send(1).await.unwrap();
3603 assert_eq!(external_out.next().await.unwrap(), (1, 6));
3604
3605 external_in.send(2).await.unwrap();
3606 assert_eq!(external_out.next().await.unwrap(), (2, 6));
3607 }
3608
3609 #[cfg(feature = "deploy")]
3610 #[tokio::test]
3611 async fn top_level_bounded_reduce_cardinality() {
3612 let mut deployment = Deployment::new();
3613
3614 let mut flow = FlowBuilder::new();
3615 let node = flow.process::<()>();
3616 let external = flow.external::<()>();
3617
3618 let (input_port, input) =
3619 node.source_external_bincode::<_, _, TotalOrder, ExactlyOnce>(&external);
3620
3621 let out = sliced! {
3622 let input = use::batch(input, nondet!());
3623 let v = use::snapshot(node.source_iter(q!(vec![1, 2, 3])).reduce(q!(|acc, v| *acc += v)), nondet!());
3624 input.cross_singleton(v.into_stream().count())
3625 }
3626 .send_bincode_external(&external);
3627
3628 let nodes = flow
3629 .with_process(&node, deployment.Localhost())
3630 .with_external(&external, deployment.Localhost())
3631 .deploy(&mut deployment);
3632
3633 deployment.deploy().await.unwrap();
3634
3635 let mut external_in = nodes.connect(input_port).await;
3636 let mut external_out = nodes.connect(out).await;
3637
3638 deployment.start().await.unwrap();
3639
3640 external_in.send(1).await.unwrap();
3641 assert_eq!(external_out.next().await.unwrap(), (1, 1));
3642
3643 external_in.send(2).await.unwrap();
3644 assert_eq!(external_out.next().await.unwrap(), (2, 1));
3645 }
3646
3647 #[cfg(feature = "deploy")]
3648 #[tokio::test]
3649 async fn top_level_bounded_into_singleton_cardinality() {
3650 let mut deployment = Deployment::new();
3651
3652 let mut flow = FlowBuilder::new();
3653 let node = flow.process::<()>();
3654 let external = flow.external::<()>();
3655
3656 let (input_port, input) =
3657 node.source_external_bincode::<_, _, TotalOrder, ExactlyOnce>(&external);
3658
3659 let out = sliced! {
3660 let input = use::batch(input, nondet!());
3661 let v = use::snapshot(node.source_iter(q!(vec![1, 2, 3])).reduce(q!(|acc, v| *acc += v)).into_singleton(), nondet!());
3662 input.cross_singleton(v.into_stream().count())
3663 }
3664 .send_bincode_external(&external);
3665
3666 let nodes = flow
3667 .with_process(&node, deployment.Localhost())
3668 .with_external(&external, deployment.Localhost())
3669 .deploy(&mut deployment);
3670
3671 deployment.deploy().await.unwrap();
3672
3673 let mut external_in = nodes.connect(input_port).await;
3674 let mut external_out = nodes.connect(out).await;
3675
3676 deployment.start().await.unwrap();
3677
3678 external_in.send(1).await.unwrap();
3679 assert_eq!(external_out.next().await.unwrap(), (1, 1));
3680
3681 external_in.send(2).await.unwrap();
3682 assert_eq!(external_out.next().await.unwrap(), (2, 1));
3683 }
3684
3685 #[cfg(feature = "deploy")]
3686 #[tokio::test]
3687 async fn atomic_fold_replays_each_tick() {
3688 let mut deployment = Deployment::new();
3689
3690 let mut flow = FlowBuilder::new();
3691 let node = flow.process::<()>();
3692 let external = flow.external::<()>();
3693
3694 let (input_port, input) =
3695 node.source_external_bincode::<_, _, TotalOrder, ExactlyOnce>(&external);
3696 let tick = node.tick();
3697
3698 let out = input
3699 .batch(&tick, nondet!())
3700 .cross_singleton(
3701 node.source_iter(q!(vec![1, 2, 3]))
3702 .atomic()
3703 .fold(q!(|| 0), q!(|acc, v| *acc += v))
3704 .snapshot_atomic(&tick, nondet!()),
3705 )
3706 .all_ticks()
3707 .send_bincode_external(&external);
3708
3709 let nodes = flow
3710 .with_process(&node, deployment.Localhost())
3711 .with_external(&external, deployment.Localhost())
3712 .deploy(&mut deployment);
3713
3714 deployment.deploy().await.unwrap();
3715
3716 let mut external_in = nodes.connect(input_port).await;
3717 let mut external_out = nodes.connect(out).await;
3718
3719 deployment.start().await.unwrap();
3720
3721 external_in.send(1).await.unwrap();
3722 assert_eq!(external_out.next().await.unwrap(), (1, 6));
3723
3724 external_in.send(2).await.unwrap();
3725 assert_eq!(external_out.next().await.unwrap(), (2, 6));
3726 }
3727
3728 #[cfg(feature = "deploy")]
3729 #[tokio::test]
3730 async fn unbounded_scan_remembers_state() {
3731 let mut deployment = Deployment::new();
3732
3733 let mut flow = FlowBuilder::new();
3734 let node = flow.process::<()>();
3735 let external = flow.external::<()>();
3736
3737 let (input_port, input) = node.source_external_bincode(&external);
3738 let out = input
3739 .scan(
3740 q!(|| 0),
3741 q!(|acc, v| {
3742 *acc += v;
3743 Some(*acc)
3744 }),
3745 )
3746 .send_bincode_external(&external);
3747
3748 let nodes = flow
3749 .with_process(&node, deployment.Localhost())
3750 .with_external(&external, deployment.Localhost())
3751 .deploy(&mut deployment);
3752
3753 deployment.deploy().await.unwrap();
3754
3755 let mut external_in = nodes.connect(input_port).await;
3756 let mut external_out = nodes.connect(out).await;
3757
3758 deployment.start().await.unwrap();
3759
3760 external_in.send(1).await.unwrap();
3761 assert_eq!(external_out.next().await.unwrap(), 1);
3762
3763 external_in.send(2).await.unwrap();
3764 assert_eq!(external_out.next().await.unwrap(), 3);
3765 }
3766
3767 #[cfg(feature = "deploy")]
3768 #[tokio::test]
3769 async fn unbounded_enumerate_remembers_state() {
3770 let mut deployment = Deployment::new();
3771
3772 let mut flow = FlowBuilder::new();
3773 let node = flow.process::<()>();
3774 let external = flow.external::<()>();
3775
3776 let (input_port, input) = node.source_external_bincode(&external);
3777 let out = input.enumerate().send_bincode_external(&external);
3778
3779 let nodes = flow
3780 .with_process(&node, deployment.Localhost())
3781 .with_external(&external, deployment.Localhost())
3782 .deploy(&mut deployment);
3783
3784 deployment.deploy().await.unwrap();
3785
3786 let mut external_in = nodes.connect(input_port).await;
3787 let mut external_out = nodes.connect(out).await;
3788
3789 deployment.start().await.unwrap();
3790
3791 external_in.send(1).await.unwrap();
3792 assert_eq!(external_out.next().await.unwrap(), (0, 1));
3793
3794 external_in.send(2).await.unwrap();
3795 assert_eq!(external_out.next().await.unwrap(), (1, 2));
3796 }
3797
3798 #[cfg(feature = "deploy")]
3799 #[tokio::test]
3800 async fn unbounded_unique_remembers_state() {
3801 let mut deployment = Deployment::new();
3802
3803 let mut flow = FlowBuilder::new();
3804 let node = flow.process::<()>();
3805 let external = flow.external::<()>();
3806
3807 let (input_port, input) =
3808 node.source_external_bincode::<_, _, TotalOrder, ExactlyOnce>(&external);
3809 let out = input.unique().send_bincode_external(&external);
3810
3811 let nodes = flow
3812 .with_process(&node, deployment.Localhost())
3813 .with_external(&external, deployment.Localhost())
3814 .deploy(&mut deployment);
3815
3816 deployment.deploy().await.unwrap();
3817
3818 let mut external_in = nodes.connect(input_port).await;
3819 let mut external_out = nodes.connect(out).await;
3820
3821 deployment.start().await.unwrap();
3822
3823 external_in.send(1).await.unwrap();
3824 assert_eq!(external_out.next().await.unwrap(), 1);
3825
3826 external_in.send(2).await.unwrap();
3827 assert_eq!(external_out.next().await.unwrap(), 2);
3828
3829 external_in.send(1).await.unwrap();
3830 external_in.send(3).await.unwrap();
3831 assert_eq!(external_out.next().await.unwrap(), 3);
3832 }
3833
3834 #[cfg(feature = "sim")]
3835 #[test]
3836 #[should_panic]
3837 fn sim_batch_nondet_size() {
3838 let mut flow = FlowBuilder::new();
3839 let node = flow.process::<()>();
3840
3841 let (in_send, input) = node.sim_input::<_, TotalOrder, _>();
3842
3843 let tick = node.tick();
3844 let out_recv = input
3845 .batch(&tick, nondet!())
3846 .count()
3847 .all_ticks()
3848 .sim_output();
3849
3850 flow.sim().exhaustive(async || {
3851 in_send.send(());
3852 in_send.send(());
3853 in_send.send(());
3854
3855 assert_eq!(out_recv.next().await, 3); });
3857 }
3858
3859 #[cfg(feature = "sim")]
3860 #[test]
3861 fn sim_batch_preserves_order() {
3862 let mut flow = FlowBuilder::new();
3863 let node = flow.process::<()>();
3864
3865 let (in_send, input) = node.sim_input();
3866
3867 let tick = node.tick();
3868 let out_recv = input
3869 .batch(&tick, nondet!())
3870 .all_ticks()
3871 .sim_output();
3872
3873 flow.sim().exhaustive(async || {
3874 in_send.send(1);
3875 in_send.send(2);
3876 in_send.send(3);
3877
3878 out_recv.assert_yields_only([1, 2, 3]).await;
3879 });
3880 }
3881
3882 #[cfg(feature = "sim")]
3883 #[test]
3884 #[should_panic]
3885 fn sim_batch_unordered_shuffles() {
3886 let mut flow = FlowBuilder::new();
3887 let node = flow.process::<()>();
3888
3889 let (in_send, input) = node.sim_input::<_, NoOrder, _>();
3890
3891 let tick = node.tick();
3892 let batch = input.batch(&tick, nondet!());
3893 let out_recv = batch
3894 .clone()
3895 .min()
3896 .zip(batch.max())
3897 .all_ticks()
3898 .sim_output();
3899
3900 flow.sim().exhaustive(async || {
3901 in_send.send_many_unordered([1, 2, 3]);
3902
3903 if out_recv.collect::<Vec<_>>().await == vec![(1, 3), (2, 2)] {
3904 panic!("saw both (1, 3) and (2, 2), so batching must have shuffled the order");
3905 }
3906 });
3907 }
3908
3909 #[cfg(feature = "sim")]
3910 #[test]
3911 fn sim_batch_unordered_shuffles_count() {
3912 let mut flow = FlowBuilder::new();
3913 let node = flow.process::<()>();
3914
3915 let (in_send, input) = node.sim_input::<_, NoOrder, _>();
3916
3917 let tick = node.tick();
3918 let batch = input.batch(&tick, nondet!());
3919 let out_recv = batch.all_ticks().sim_output();
3920
3921 let instance_count = flow.sim().exhaustive(async || {
3922 in_send.send_many_unordered([1, 2, 3, 4]);
3923 out_recv.assert_yields_only_unordered([1, 2, 3, 4]).await;
3924 });
3925
3926 assert_eq!(
3927 instance_count,
3928 75 )
3930 }
3931
3932 #[cfg(feature = "sim")]
3933 #[test]
3934 #[should_panic]
3935 fn sim_observe_order_batched() {
3936 let mut flow = FlowBuilder::new();
3937 let node = flow.process::<()>();
3938
3939 let (in_send, input) = node.sim_input::<_, NoOrder, _>();
3940
3941 let tick = node.tick();
3942 let batch = input.batch(&tick, nondet!());
3943 let out_recv = batch
3944 .assume_ordering::<TotalOrder>(nondet!())
3945 .all_ticks()
3946 .sim_output();
3947
3948 flow.sim().exhaustive(async || {
3949 in_send.send_many_unordered([1, 2, 3, 4]);
3950 out_recv.assert_yields_only([1, 2, 3, 4]).await; });
3952 }
3953
3954 #[cfg(feature = "sim")]
3955 #[test]
3956 fn sim_observe_order_batched_count() {
3957 let mut flow = FlowBuilder::new();
3958 let node = flow.process::<()>();
3959
3960 let (in_send, input) = node.sim_input::<_, NoOrder, _>();
3961
3962 let tick = node.tick();
3963 let batch = input.batch(&tick, nondet!());
3964 let out_recv = batch
3965 .assume_ordering::<TotalOrder>(nondet!())
3966 .all_ticks()
3967 .sim_output();
3968
3969 let instance_count = flow.sim().exhaustive(async || {
3970 in_send.send_many_unordered([1, 2, 3, 4]);
3971 let _ = out_recv.collect::<Vec<_>>().await;
3972 });
3973
3974 assert_eq!(
3975 instance_count,
3976 192 )
3978 }
3979
3980 #[cfg(feature = "sim")]
3981 #[test]
3982 fn sim_unordered_count_instance_count() {
3983 let mut flow = FlowBuilder::new();
3984 let node = flow.process::<()>();
3985
3986 let (in_send, input) = node.sim_input::<_, NoOrder, _>();
3987
3988 let tick = node.tick();
3989 let out_recv = input
3990 .count()
3991 .snapshot(&tick, nondet!())
3992 .all_ticks()
3993 .sim_output();
3994
3995 let instance_count = flow.sim().exhaustive(async || {
3996 in_send.send_many_unordered([1, 2, 3, 4]);
3997 assert!(out_recv.collect::<Vec<_>>().await.last().unwrap() == &4);
3998 });
3999
4000 assert_eq!(
4001 instance_count,
4002 16 )
4004 }
4005
4006 #[cfg(feature = "sim")]
4007 #[test]
4008 fn sim_top_level_assume_ordering() {
4009 let mut flow = FlowBuilder::new();
4010 let node = flow.process::<()>();
4011
4012 let (in_send, input) = node.sim_input::<_, NoOrder, _>();
4013
4014 let out_recv = input
4015 .assume_ordering::<TotalOrder>(nondet!())
4016 .sim_output();
4017
4018 let instance_count = flow.sim().exhaustive(async || {
4019 in_send.send_many_unordered([1, 2, 3]);
4020 let mut out = out_recv.collect::<Vec<_>>().await;
4021 out.sort();
4022 assert_eq!(out, vec![1, 2, 3]);
4023 });
4024
4025 assert_eq!(instance_count, 6)
4026 }
4027
4028 #[cfg(feature = "sim")]
4029 #[test]
4030 fn sim_top_level_assume_ordering_cycle_back() {
4031 let mut flow = FlowBuilder::new();
4032 let node = flow.process::<()>();
4033 let node2 = flow.process::<()>();
4034
4035 let (in_send, input) = node.sim_input::<_, NoOrder, _>();
4036
4037 let (complete_cycle_back, cycle_back) =
4038 node.forward_ref::<super::Stream<_, _, _, NoOrder>>();
4039 let ordered = input
4040 .merge_unordered(cycle_back)
4041 .assume_ordering::<TotalOrder>(nondet!());
4042 complete_cycle_back.complete(
4043 ordered
4044 .clone()
4045 .map(q!(|v| v + 1))
4046 .filter(q!(|v| v % 2 == 1))
4047 .send(&node2, TCP.fail_stop().bincode())
4048 .send(&node, TCP.fail_stop().bincode()),
4049 );
4050
4051 let out_recv = ordered.sim_output();
4052
4053 let mut saw = false;
4054 let instance_count = flow.sim().exhaustive(async || {
4055 in_send.send_many_unordered([0, 2]);
4056 let out = out_recv.collect::<Vec<_>>().await;
4057
4058 if out.starts_with(&[0, 1, 2]) {
4059 saw = true;
4060 }
4061 });
4062
4063 assert!(saw, "did not see an instance with 0, 1, 2 in order");
4064 assert_eq!(instance_count, 6);
4065 }
4066
4067 #[cfg(feature = "sim")]
4068 #[test]
4069 fn sim_top_level_assume_ordering_cycle_back_tick() {
4070 let mut flow = FlowBuilder::new();
4071 let node = flow.process::<()>();
4072 let node2 = flow.process::<()>();
4073
4074 let (in_send, input) = node.sim_input::<_, NoOrder, _>();
4075
4076 let (complete_cycle_back, cycle_back) =
4077 node.forward_ref::<super::Stream<_, _, _, NoOrder>>();
4078 let ordered = input
4079 .merge_unordered(cycle_back)
4080 .assume_ordering::<TotalOrder>(nondet!());
4081 complete_cycle_back.complete(
4082 ordered
4083 .clone()
4084 .batch(&node.tick(), nondet!())
4085 .all_ticks()
4086 .map(q!(|v| v + 1))
4087 .filter(q!(|v| v % 2 == 1))
4088 .send(&node2, TCP.fail_stop().bincode())
4089 .send(&node, TCP.fail_stop().bincode()),
4090 );
4091
4092 let out_recv = ordered.sim_output();
4093
4094 let mut saw = false;
4095 let instance_count = flow.sim().exhaustive(async || {
4096 in_send.send_many_unordered([0, 2]);
4097 let out = out_recv.collect::<Vec<_>>().await;
4098
4099 if out.starts_with(&[0, 1, 2]) {
4100 saw = true;
4101 }
4102 });
4103
4104 assert!(saw, "did not see an instance with 0, 1, 2 in order");
4105 assert_eq!(instance_count, 58);
4106 }
4107
4108 #[cfg(feature = "sim")]
4109 #[test]
4110 fn sim_top_level_assume_ordering_multiple() {
4111 let mut flow = FlowBuilder::new();
4112 let node = flow.process::<()>();
4113 let node2 = flow.process::<()>();
4114
4115 let (in_send, input) = node.sim_input::<_, NoOrder, _>();
4116 let (_, input2) = node.sim_input::<_, NoOrder, _>();
4117
4118 let (complete_cycle_back, cycle_back) =
4119 node.forward_ref::<super::Stream<_, _, _, NoOrder>>();
4120 let input1_ordered = input
4121 .clone()
4122 .merge_unordered(cycle_back)
4123 .assume_ordering::<TotalOrder>(nondet!());
4124 let foo = input1_ordered
4125 .clone()
4126 .map(q!(|v| v + 3))
4127 .weaken_ordering::<NoOrder>()
4128 .merge_unordered(input2)
4129 .assume_ordering::<TotalOrder>(nondet!());
4130
4131 complete_cycle_back.complete(
4132 foo.filter(q!(|v| *v == 3))
4133 .send(&node2, TCP.fail_stop().bincode())
4134 .send(&node, TCP.fail_stop().bincode()),
4135 );
4136
4137 let out_recv = input1_ordered.sim_output();
4138
4139 let mut saw = false;
4140 let instance_count = flow.sim().exhaustive(async || {
4141 in_send.send_many_unordered([0, 1]);
4142 let out = out_recv.collect::<Vec<_>>().await;
4143
4144 if out.starts_with(&[0, 3, 1]) {
4145 saw = true;
4146 }
4147 });
4148
4149 assert!(saw, "did not see an instance with 0, 3, 1 in order");
4150 assert_eq!(instance_count, 15);
4151 }
4152
4153 #[cfg(feature = "sim")]
4154 #[test]
4155 fn sim_atomic_assume_ordering_cycle_back() {
4156 let mut flow = FlowBuilder::new();
4157 let node = flow.process::<()>();
4158 let node2 = flow.process::<()>();
4159
4160 let (in_send, input) = node.sim_input::<_, NoOrder, _>();
4161
4162 let (complete_cycle_back, cycle_back) =
4163 node.forward_ref::<super::Stream<_, _, _, NoOrder>>();
4164 let ordered = input
4165 .merge_unordered(cycle_back)
4166 .atomic()
4167 .assume_ordering::<TotalOrder>(nondet!())
4168 .end_atomic();
4169 complete_cycle_back.complete(
4170 ordered
4171 .clone()
4172 .map(q!(|v| v + 1))
4173 .filter(q!(|v| v % 2 == 1))
4174 .send(&node2, TCP.fail_stop().bincode())
4175 .send(&node, TCP.fail_stop().bincode()),
4176 );
4177
4178 let out_recv = ordered.sim_output();
4179
4180 let instance_count = flow.sim().exhaustive(async || {
4181 in_send.send_many_unordered([0, 2]);
4182 let out = out_recv.collect::<Vec<_>>().await;
4183 assert_eq!(out.len(), 4);
4184 });
4185 assert_eq!(instance_count, 22);
4186 }
4187
4188 #[cfg(feature = "deploy")]
4189 #[tokio::test]
4190 async fn partition_evens_odds() {
4191 let mut deployment = Deployment::new();
4192
4193 let mut flow = FlowBuilder::new();
4194 let node = flow.process::<()>();
4195 let external = flow.external::<()>();
4196
4197 let numbers = node.source_iter(q!(vec![1i32, 2, 3, 4, 5, 6]));
4198 let (evens, odds) = numbers.partition(q!(|x: &i32| x % 2 == 0));
4199 let evens_port = evens.send_bincode_external(&external);
4200 let odds_port = odds.send_bincode_external(&external);
4201
4202 let nodes = flow
4203 .with_process(&node, deployment.Localhost())
4204 .with_external(&external, deployment.Localhost())
4205 .deploy(&mut deployment);
4206
4207 deployment.deploy().await.unwrap();
4208
4209 let mut evens_out = nodes.connect(evens_port).await;
4210 let mut odds_out = nodes.connect(odds_port).await;
4211
4212 deployment.start().await.unwrap();
4213
4214 let mut even_results = Vec::new();
4215 for _ in 0..3 {
4216 even_results.push(evens_out.next().await.unwrap());
4217 }
4218 even_results.sort();
4219 assert_eq!(even_results, vec![2, 4, 6]);
4220
4221 let mut odd_results = Vec::new();
4222 for _ in 0..3 {
4223 odd_results.push(odds_out.next().await.unwrap());
4224 }
4225 odd_results.sort();
4226 assert_eq!(odd_results, vec![1, 3, 5]);
4227 }
4228
4229 #[cfg(feature = "deploy")]
4230 #[tokio::test]
4231 async fn unconsumed_inspect_still_runs() {
4232 use crate::deploy::DeployCrateWrapper;
4233
4234 let mut deployment = Deployment::new();
4235
4236 let mut flow = FlowBuilder::new();
4237 let node = flow.process::<()>();
4238
4239 node.source_iter(q!(0..5))
4242 .inspect(q!(|x| println!("inspect: {}", x)));
4243
4244 let nodes = flow
4245 .with_process(&node, deployment.Localhost())
4246 .deploy(&mut deployment);
4247
4248 deployment.deploy().await.unwrap();
4249
4250 let mut stdout = nodes.get_process(&node).stdout();
4251
4252 deployment.start().await.unwrap();
4253
4254 let mut lines = Vec::new();
4255 for _ in 0..5 {
4256 lines.push(stdout.recv().await.unwrap());
4257 }
4258 lines.sort();
4259 assert_eq!(
4260 lines,
4261 vec![
4262 "inspect: 0",
4263 "inspect: 1",
4264 "inspect: 2",
4265 "inspect: 3",
4266 "inspect: 4",
4267 ]
4268 );
4269 }
4270
4271 #[cfg(feature = "deploy")]
4272 #[tokio::test]
4273 async fn unconsumed_inspect_alive_at_deploy_still_runs() {
4274 use crate::deploy::DeployCrateWrapper;
4275
4276 let mut deployment = Deployment::new();
4277
4278 let mut flow = FlowBuilder::new();
4279 let node = flow.process::<()>();
4280
4281 let _inspected = node
4286 .source_iter(q!(0..5))
4287 .inspect(q!(|x| println!("inspect: {}", x)));
4288
4289 let nodes = flow
4290 .with_process(&node, deployment.Localhost())
4291 .deploy(&mut deployment);
4292
4293 deployment.deploy().await.unwrap();
4294
4295 let mut stdout = nodes.get_process(&node).stdout();
4296
4297 deployment.start().await.unwrap();
4298
4299 let mut lines = Vec::new();
4300 for _ in 0..5 {
4301 lines.push(stdout.recv().await.unwrap());
4302 }
4303 lines.sort();
4304 assert_eq!(
4305 lines,
4306 vec![
4307 "inspect: 0",
4308 "inspect: 1",
4309 "inspect: 2",
4310 "inspect: 3",
4311 "inspect: 4",
4312 ]
4313 );
4314 }
4315
4316 #[cfg(feature = "sim")]
4317 #[test]
4318 fn sim_limit() {
4319 let mut flow = FlowBuilder::new();
4320 let node = flow.process::<()>();
4321
4322 let (in_send, input) = node.sim_input();
4323
4324 let out_recv = input.limit(q!(3)).sim_output();
4325
4326 flow.sim().exhaustive(async || {
4327 in_send.send(1);
4328 in_send.send(2);
4329 in_send.send(3);
4330 in_send.send(4);
4331 in_send.send(5);
4332
4333 out_recv.assert_yields_only([1, 2, 3]).await;
4334 });
4335 }
4336
4337 #[cfg(feature = "sim")]
4338 #[test]
4339 fn sim_limit_zero() {
4340 let mut flow = FlowBuilder::new();
4341 let node = flow.process::<()>();
4342
4343 let (in_send, input) = node.sim_input();
4344
4345 let out_recv = input.limit(q!(0)).sim_output();
4346
4347 flow.sim().exhaustive(async || {
4348 in_send.send(1);
4349 in_send.send(2);
4350
4351 out_recv.assert_yields_only::<i32, _>([]).await;
4352 });
4353 }
4354
4355 #[cfg(feature = "sim")]
4356 #[test]
4357 fn sim_merge_ordered() {
4358 let mut flow = FlowBuilder::new();
4359 let node = flow.process::<()>();
4360
4361 let (in_send, input) = node.sim_input();
4362 let (in_send2, input2) = node.sim_input();
4363
4364 let out_recv = input
4365 .merge_ordered(input2, nondet!())
4366 .sim_output();
4367
4368 let mut saw_out_of_order = false;
4369 let instances = flow.sim().exhaustive(async || {
4370 in_send.send(1);
4371 in_send.send(2);
4372 in_send2.send(3);
4373 in_send2.send(4);
4374
4375 let out = out_recv.collect::<Vec<_>>().await;
4376
4377 if out == [1, 3, 2, 4] {
4378 saw_out_of_order = true;
4379 }
4380
4381 let mut first_elements = out.iter().filter(|v| **v <= 2).copied().collect::<Vec<_>>();
4384 let mut second_elements = out.iter().filter(|v| **v > 2).copied().collect::<Vec<_>>();
4385 assert_eq!(
4386 first_elements,
4387 vec![1, 2],
4388 "first input order violated: {:?}",
4389 out
4390 );
4391 assert_eq!(
4392 second_elements,
4393 vec![3, 4],
4394 "second input order violated: {:?}",
4395 out
4396 );
4397
4398 first_elements.append(&mut second_elements);
4399 first_elements.sort();
4400 assert_eq!(first_elements, vec![1, 2, 3, 4]);
4401 });
4402
4403 assert!(saw_out_of_order);
4404 assert_eq!(instances, 6);
4405 }
4406
4407 #[cfg(feature = "sim")]
4410 #[test]
4411 fn sim_merge_ordered_one_empty() {
4412 let mut flow = FlowBuilder::new();
4413 let node = flow.process::<()>();
4414
4415 let (in_send, input) = node.sim_input();
4416 let (_in_send2, input2) = node.sim_input();
4417
4418 let out_recv = input
4419 .merge_ordered(input2, nondet!())
4420 .sim_output();
4421
4422 let instances = flow.sim().exhaustive(async || {
4423 in_send.send(1);
4424 in_send.send(2);
4425
4426 let out = out_recv.collect::<Vec<_>>().await;
4427 assert_eq!(out, vec![1, 2]);
4428 });
4429
4430 assert_eq!(instances, 1);
4432 }
4433
4434 #[cfg(feature = "sim")]
4440 #[test]
4441 fn sim_merge_ordered_cycle_back() {
4442 let mut flow = FlowBuilder::new();
4443 let node = flow.process::<()>();
4444
4445 let (in_send, input) = node.sim_input();
4446
4447 let (complete_cycle_back, cycle_back) =
4449 node.forward_ref::<super::Stream<_, _, _, TotalOrder>>();
4450
4451 let merged = input.merge_ordered(cycle_back, nondet!());
4453
4454 complete_cycle_back.complete(merged.clone().filter(q!(|v| *v == 1)).map(q!(|v| v * 10)));
4456
4457 let out_recv = merged.sim_output();
4458
4459 let mut saw_cycle_before_second = false;
4462 flow.sim().exhaustive(async || {
4463 in_send.send(1);
4464 in_send.send(2);
4465
4466 let out = out_recv.collect::<Vec<_>>().await;
4467
4468 let pos_1 = out.iter().position(|v| *v == 1).unwrap();
4470 let pos_10 = out.iter().position(|v| *v == 10).unwrap();
4471 assert!(pos_1 < pos_10, "causal order violated: {:?}", out);
4472
4473 if out == [1, 10, 2] {
4475 saw_cycle_before_second = true;
4476 }
4477
4478 let mut sorted = out;
4479 sorted.sort();
4480 assert_eq!(sorted, vec![1, 2, 10]);
4481 });
4482
4483 assert!(
4484 saw_cycle_before_second,
4485 "never saw the cycled element arrive before the second input element"
4486 );
4487 }
4488
4489 #[cfg(feature = "sim")]
4493 #[test]
4494 fn sim_merge_ordered_delayed() {
4495 let mut flow = FlowBuilder::new();
4496 let node = flow.process::<()>();
4497
4498 let (in_send, input) = node.sim_input();
4499 let (in_send2, input2) = node.sim_input();
4500
4501 let out_recv = input
4502 .merge_ordered(input2, nondet!())
4503 .sim_output();
4504
4505 let mut saw_delayed_interleaving = false;
4506 flow.sim().exhaustive(async || {
4507 in_send.send(1);
4509 in_send2.send(3);
4510 in_send2.send(4);
4511
4512 let first_batch = out_recv.collect::<Vec<_>>().await;
4514
4515 in_send.send(2);
4517 let second_batch = out_recv.collect::<Vec<_>>().await;
4518
4519 let mut all: Vec<_> = first_batch
4520 .iter()
4521 .chain(second_batch.iter())
4522 .copied()
4523 .collect();
4524
4525 if all == [1, 3, 4, 2] {
4527 saw_delayed_interleaving = true;
4528 }
4529
4530 all.sort();
4531 assert_eq!(all, vec![1, 2, 3, 4]);
4532 });
4533
4534 assert!(saw_delayed_interleaving);
4535 }
4536
4537 #[cfg(feature = "deploy")]
4542 #[tokio::test]
4543 async fn deploy_merge_ordered_delayed() {
4544 let mut deployment = Deployment::new();
4545
4546 let mut flow = FlowBuilder::new();
4547 let node = flow.process::<()>();
4548 let external = flow.external::<()>();
4549
4550 let (input_a_port, input_a) = node.source_external_bincode(&external);
4551 let (input_b_port, input_b) = node.source_external_bincode(&external);
4552
4553 let out = input_a
4554 .assume_ordering(nondet!())
4555 .merge_ordered(
4556 input_b.assume_ordering(nondet!()),
4557 nondet!(),
4558 )
4559 .send_bincode_external(&external);
4560
4561 let nodes = flow
4562 .with_process(&node, deployment.Localhost())
4563 .with_external(&external, deployment.Localhost())
4564 .deploy(&mut deployment);
4565
4566 deployment.deploy().await.unwrap();
4567
4568 let mut ext_a = nodes.connect(input_a_port).await;
4569 let mut ext_b = nodes.connect(input_b_port).await;
4570 let mut ext_out = nodes.connect(out).await;
4571
4572 deployment.start().await.unwrap();
4573
4574 ext_a.send(1).await.unwrap();
4576 ext_b.send(3).await.unwrap();
4577 ext_b.send(4).await.unwrap();
4578
4579 let mut received = Vec::new();
4581 for _ in 0..3 {
4582 received.push(ext_out.next().await.unwrap());
4583 }
4584
4585 ext_a.send(2).await.unwrap();
4587 received.push(ext_out.next().await.unwrap());
4588
4589 received.sort();
4591 assert_eq!(received, vec![1, 2, 3, 4]);
4592 }
4593
4594 #[cfg(feature = "deploy")]
4595 #[tokio::test]
4596 async fn monotone_fold_threshold() {
4597 use crate::properties::manual_proof;
4598
4599 let mut deployment = Deployment::new();
4600
4601 let mut flow = FlowBuilder::new();
4602 let node = flow.process::<()>();
4603 let external = flow.external::<()>();
4604
4605 let in_unbounded: super::Stream<_, _> =
4606 node.source_iter(q!(vec![1i32, 2, 3, 4, 5, 6])).into();
4607 let sum = in_unbounded.fold(
4608 q!(|| 0),
4609 q!(
4610 |sum, v| {
4611 *sum += v;
4612 },
4613 monotone = manual_proof!()
4614 ),
4615 );
4616
4617 let threshold_out = sum
4618 .threshold_greater_or_equal(node.singleton(q!(7)))
4619 .send_bincode_external(&external);
4620
4621 let nodes = flow
4622 .with_process(&node, deployment.Localhost())
4623 .with_external(&external, deployment.Localhost())
4624 .deploy(&mut deployment);
4625
4626 deployment.deploy().await.unwrap();
4627
4628 let mut threshold_out = nodes.connect(threshold_out).await;
4629
4630 deployment.start().await.unwrap();
4631
4632 assert_eq!(threshold_out.next().await.unwrap(), 7);
4633 }
4634
4635 #[cfg(feature = "deploy")]
4636 #[tokio::test]
4637 async fn monotone_count_threshold() {
4638 let mut deployment = Deployment::new();
4639
4640 let mut flow = FlowBuilder::new();
4641 let node = flow.process::<()>();
4642 let external = flow.external::<()>();
4643
4644 let in_unbounded: super::Stream<_, _> =
4645 node.source_iter(q!(vec![1i32, 2, 3, 4, 5, 6])).into();
4646 let sum = in_unbounded.count();
4647
4648 let threshold_out = sum
4649 .threshold_greater_or_equal(node.singleton(q!(3)))
4650 .send_bincode_external(&external);
4651
4652 let nodes = flow
4653 .with_process(&node, deployment.Localhost())
4654 .with_external(&external, deployment.Localhost())
4655 .deploy(&mut deployment);
4656
4657 deployment.deploy().await.unwrap();
4658
4659 let mut threshold_out = nodes.connect(threshold_out).await;
4660
4661 deployment.start().await.unwrap();
4662
4663 assert_eq!(threshold_out.next().await.unwrap(), 3);
4664 }
4665
4666 #[cfg(feature = "deploy")]
4667 #[tokio::test]
4668 async fn monotone_map_order_preserving_threshold() {
4669 use crate::properties::manual_proof;
4670
4671 let mut deployment = Deployment::new();
4672
4673 let mut flow = FlowBuilder::new();
4674 let node = flow.process::<()>();
4675 let external = flow.external::<()>();
4676
4677 let in_unbounded: super::Stream<_, _> =
4678 node.source_iter(q!(vec![1i32, 2, 3, 4, 5, 6])).into();
4679 let sum = in_unbounded.fold(
4680 q!(|| 0),
4681 q!(
4682 |sum, v| {
4683 *sum += v;
4684 },
4685 monotone = manual_proof!()
4686 ),
4687 );
4688
4689 let doubled = sum.map(q!(
4691 |v| v * 2,
4692 order_preserving = manual_proof!()
4693 ));
4694
4695 let threshold_out = doubled
4696 .threshold_greater_or_equal(node.singleton(q!(14)))
4697 .send_bincode_external(&external);
4698
4699 let nodes = flow
4700 .with_process(&node, deployment.Localhost())
4701 .with_external(&external, deployment.Localhost())
4702 .deploy(&mut deployment);
4703
4704 deployment.deploy().await.unwrap();
4705
4706 let mut threshold_out = nodes.connect(threshold_out).await;
4707
4708 deployment.start().await.unwrap();
4709
4710 assert_eq!(threshold_out.next().await.unwrap(), 14);
4711 }
4712
4713 #[cfg(any(feature = "deploy", feature = "sim"))]
4716 mod join_ordering_type_tests {
4717 use crate::live_collections::boundedness::{Bounded, Unbounded};
4718 use crate::live_collections::stream::{ExactlyOnce, NoOrder, Stream, TotalOrder};
4719 use crate::location::{Location, Process};
4720
4721 #[expect(dead_code, reason = "compile-time type test")]
4722 fn join_unbounded_with_bounded_preserves_order<'a>(
4723 left: Stream<(i32, char), Process<'a>, Unbounded, TotalOrder, ExactlyOnce>,
4724 right: Stream<(i32, char), Process<'a>, Bounded, TotalOrder, ExactlyOnce>,
4725 ) -> Stream<(i32, (char, char)), Process<'a>, Unbounded, TotalOrder, ExactlyOnce> {
4726 left.join(right)
4727 }
4728
4729 #[expect(dead_code, reason = "compile-time type test")]
4730 fn join_unbounded_with_unbounded_is_no_order<'a>(
4731 left: Stream<(i32, char), Process<'a>, Unbounded, TotalOrder, ExactlyOnce>,
4732 right: Stream<(i32, char), Process<'a>, Unbounded, TotalOrder, ExactlyOnce>,
4733 ) -> Stream<(i32, (char, char)), Process<'a>, Unbounded, NoOrder, ExactlyOnce> {
4734 left.join(right)
4735 }
4736
4737 #[expect(dead_code, reason = "compile-time type test")]
4738 fn join_bounded_with_bounded_preserves_order<'a, L: Location<'a>>(
4739 left: Stream<(i32, char), L, Bounded, TotalOrder, ExactlyOnce>,
4740 right: Stream<(i32, char), L, Bounded, TotalOrder, ExactlyOnce>,
4741 ) -> Stream<(i32, (char, char)), L, Bounded, TotalOrder, ExactlyOnce> {
4742 left.join(right)
4743 }
4744
4745 #[expect(dead_code, reason = "compile-time type test")]
4746 fn join_unbounded_noorder_with_bounded<'a>(
4747 left: Stream<(i32, char), Process<'a>, Unbounded, NoOrder, ExactlyOnce>,
4748 right: Stream<(i32, char), Process<'a>, Bounded, NoOrder, ExactlyOnce>,
4749 ) -> Stream<(i32, (char, char)), Process<'a>, Unbounded, NoOrder, ExactlyOnce> {
4750 left.join(right)
4751 }
4752
4753 #[expect(dead_code, reason = "compile-time type test")]
4756 fn cross_product_unbounded_with_bounded_preserves_order<'a>(
4757 left: Stream<i32, Process<'a>, Unbounded, TotalOrder, ExactlyOnce>,
4758 right: Stream<char, Process<'a>, Bounded, TotalOrder, ExactlyOnce>,
4759 ) -> Stream<(i32, char), Process<'a>, Unbounded, TotalOrder, ExactlyOnce> {
4760 left.cross_product(right)
4761 }
4762
4763 #[expect(dead_code, reason = "compile-time type test")]
4764 fn cross_product_bounded_with_bounded_preserves_order<'a>(
4765 left: Stream<i32, Process<'a>, Bounded, TotalOrder, ExactlyOnce>,
4766 right: Stream<char, Process<'a>, Bounded, TotalOrder, ExactlyOnce>,
4767 ) -> Stream<(i32, char), Process<'a>, Bounded, TotalOrder, ExactlyOnce> {
4768 left.cross_product(right)
4769 }
4770
4771 #[expect(dead_code, reason = "compile-time type test")]
4772 fn cross_product_unbounded_with_unbounded_is_no_order<'a>(
4773 left: Stream<i32, Process<'a>, Unbounded, TotalOrder, ExactlyOnce>,
4774 right: Stream<char, Process<'a>, Unbounded, TotalOrder, ExactlyOnce>,
4775 ) -> Stream<(i32, char), Process<'a>, Unbounded, NoOrder, ExactlyOnce> {
4776 left.cross_product(right)
4777 }
4778 } #[cfg(feature = "sim")]
4783 #[test]
4784 fn cross_product_mixed_boundedness_correctness() {
4785 use stageleft::q;
4786
4787 use crate::compile::builder::FlowBuilder;
4788 use crate::nondet::nondet;
4789
4790 let mut flow = FlowBuilder::new();
4791 let process = flow.process::<()>();
4792 let tick = process.tick();
4793
4794 let left = process.source_iter(q!(vec![1, 2]));
4795 let right = process
4796 .source_iter(q!(vec!['a', 'b']))
4797 .batch(&tick, nondet!())
4798 .all_ticks();
4799
4800 let out = left.cross_product(right).sim_output();
4801
4802 flow.sim().exhaustive(async || {
4803 out.assert_yields_only_unordered(vec![(1, 'a'), (1, 'b'), (2, 'a'), (2, 'b')])
4804 .await;
4805 });
4806 }
4807
4808 #[cfg(feature = "sim")]
4809 #[test]
4810 fn join_mixed_boundedness_correctness() {
4811 use stageleft::q;
4812
4813 use crate::compile::builder::FlowBuilder;
4814 use crate::nondet::nondet;
4815
4816 let mut flow = FlowBuilder::new();
4817 let process = flow.process::<()>();
4818 let tick = process.tick();
4819
4820 let left = process.source_iter(q!(vec![(1, 'a'), (2, 'b')]));
4821 let right = process
4822 .source_iter(q!(vec![(1, 'x'), (2, 'y')]))
4823 .batch(&tick, nondet!())
4824 .all_ticks();
4825
4826 let out = left.join(right).sim_output();
4827
4828 flow.sim().exhaustive(async || {
4829 out.assert_yields_only_unordered(vec![(1, ('a', 'x')), (2, ('b', 'y'))])
4830 .await;
4831 });
4832 }
4833
4834 #[cfg(feature = "sim")]
4835 #[test]
4836 fn sim_merge_unordered_independent_atomics() {
4837 let mut flow = FlowBuilder::new();
4838 let node = flow.process::<()>();
4839
4840 let (in1_send, input1) = node.sim_input::<_, TotalOrder, _>();
4841 let (in2_send, input2) = node.sim_input::<_, TotalOrder, _>();
4842
4843 let out = input1
4844 .atomic()
4845 .merge_unordered(input2.atomic())
4846 .end_atomic()
4847 .sim_output();
4848
4849 flow.sim().exhaustive(async || {
4850 in1_send.send(1);
4851 in2_send.send(2);
4852
4853 out.assert_yields_only_unordered(vec![1, 2]).await;
4854 });
4855 }
4856
4857 #[cfg(feature = "deploy")]
4858 #[tokio::test]
4859 async fn test_stream_ref() {
4860 let mut deployment = Deployment::new();
4861
4862 let mut flow = FlowBuilder::new();
4863 let external = flow.external::<()>();
4864 let p1 = flow.process::<()>();
4865
4866 let my_stream = p1.source_iter(q!(1..=5i32));
4868
4869 let stream_ref = my_stream.by_ref();
4870
4871 let out_port = p1
4873 .source_iter(q!([()]))
4874 .map(q!(|_| stream_ref.len() as i32))
4875 .send_bincode_external(&external);
4876
4877 my_stream.for_each(q!(|_| {}));
4879
4880 let nodes = flow
4881 .with_default_optimize()
4882 .with_process(&p1, deployment.Localhost())
4883 .with_external(&external, deployment.Localhost())
4884 .deploy(&mut deployment);
4885
4886 deployment.deploy().await.unwrap();
4887
4888 let mut out_recv = nodes.connect(out_port).await;
4889
4890 deployment.start().await.unwrap();
4891
4892 let result = out_recv.next().await.unwrap();
4893 assert_eq!(result, 5);
4895 }
4896
4897 #[cfg(feature = "deploy")]
4898 #[tokio::test]
4899 async fn test_stream_ref_contents() {
4900 let mut deployment = Deployment::new();
4901
4902 let mut flow = FlowBuilder::new();
4903 let external = flow.external::<()>();
4904 let p1 = flow.process::<()>();
4905
4906 let my_stream = p1.source_iter(q!(1..=3i32));
4908
4909 let stream_ref = my_stream.by_ref();
4910
4911 let out_port = p1
4913 .source_iter(q!([()]))
4914 .map(q!(|_| stream_ref.iter().sum::<i32>()))
4915 .send_bincode_external(&external);
4916
4917 my_stream.for_each(q!(|_| {}));
4918
4919 let nodes = flow
4920 .with_default_optimize()
4921 .with_process(&p1, deployment.Localhost())
4922 .with_external(&external, deployment.Localhost())
4923 .deploy(&mut deployment);
4924
4925 deployment.deploy().await.unwrap();
4926
4927 let mut out_recv = nodes.connect(out_port).await;
4928
4929 deployment.start().await.unwrap();
4930
4931 let result = out_recv.next().await.unwrap();
4932 assert_eq!(result, 6);
4934 }
4935
4936 #[cfg(feature = "deploy")]
4937 #[tokio::test]
4938 async fn test_stream_ref_no_consumer() {
4939 let mut deployment = Deployment::new();
4940
4941 let mut flow = FlowBuilder::new();
4942 let external = flow.external::<()>();
4943 let p1 = flow.process::<()>();
4944
4945 let my_stream = p1.source_iter(q!(1..=4i32));
4947
4948 let stream_ref = my_stream.by_ref();
4949
4950 let out_port = p1
4951 .source_iter(q!([()]))
4952 .map(q!(|_| stream_ref.len() as i32))
4953 .send_bincode_external(&external);
4954
4955 let nodes = flow
4956 .with_default_optimize()
4957 .with_process(&p1, deployment.Localhost())
4958 .with_external(&external, deployment.Localhost())
4959 .deploy(&mut deployment);
4960
4961 deployment.deploy().await.unwrap();
4962
4963 let mut out_recv = nodes.connect(out_port).await;
4964
4965 deployment.start().await.unwrap();
4966
4967 let result = out_recv.next().await.unwrap();
4968 assert_eq!(result, 4);
4969 }
4970
4971 #[cfg(feature = "deploy")]
4972 #[tokio::test]
4973 async fn test_stream_mut() {
4974 let mut deployment = Deployment::new();
4975
4976 let mut flow = FlowBuilder::new();
4977 let external = flow.external::<()>();
4978 let p1 = flow.process::<()>();
4979
4980 let my_stream = p1.source_iter(q!(1..=5i32));
4982
4983 let stream_mut = my_stream.by_mut();
4984
4985 let out_port = p1
4987 .source_iter(q!([()]))
4988 .map(q!(|_| {
4989 stream_mut.retain(|x| *x > 3);
4990 stream_mut.len() as i32
4991 }))
4992 .send_bincode_external(&external);
4993
4994 my_stream.for_each(q!(|_| {}));
4995
4996 let nodes = flow
4997 .with_default_optimize()
4998 .with_process(&p1, deployment.Localhost())
4999 .with_external(&external, deployment.Localhost())
5000 .deploy(&mut deployment);
5001
5002 deployment.deploy().await.unwrap();
5003
5004 let mut out_recv = nodes.connect(out_port).await;
5005
5006 deployment.start().await.unwrap();
5007
5008 let result = out_recv.next().await.unwrap();
5009 assert_eq!(result, 2);
5011 }
5012
5013 #[cfg(feature = "sim")]
5017 #[test]
5018 fn sim_map_with_mut_on_unordered_explores_multiple_states() {
5019 use crate::live_collections::sliced::sliced;
5020 use crate::live_collections::stream::ExactlyOnce;
5021 use crate::properties::manual_proof;
5022
5023 let mut flow = FlowBuilder::new();
5024 let node = flow.process::<()>();
5025
5026 let (trigger_send, trigger) = node.sim_input::<i32, TotalOrder, ExactlyOnce>();
5027
5028 let out_recv = sliced! {
5029 let batch = use::batch(trigger, nondet!());
5030 let counter = batch.location().source_iter(q!(vec![0i32]))
5031 .fold(q!(|| 0i32), q!(|acc, v| *acc += v));
5032 let counter_mut = counter.by_mut();
5033 let items = batch.location().source_iter(q!(vec![1i32, 2])).weaken_ordering::<NoOrder>();
5034 items.map(q!(
5035 |x| {
5036 *counter_mut += x;
5037 *counter_mut
5038 },
5039 commutative = manual_proof!()
5040 ))
5041 }
5042 .sim_output();
5043
5044 let count = flow.sim().exhaustive(async || {
5045 trigger_send.send(1);
5046 let _all: Vec<i32> = out_recv.collect_sorted().await;
5047 });
5048
5049 assert_eq!(
5050 count, 2,
5051 "Expected 2 simulation instances due to mut on unordered input, got {}",
5052 count
5053 );
5054 }
5055
5056 #[cfg(feature = "sim")]
5060 #[test]
5061 fn sim_scan_with_ref_capture() {
5062 use crate::live_collections::sliced::sliced;
5063 use crate::live_collections::stream::ExactlyOnce;
5064
5065 let mut flow = FlowBuilder::new();
5066 let node = flow.process::<()>();
5067
5068 let (trigger_send, trigger) = node.sim_input::<i32, TotalOrder, ExactlyOnce>();
5069
5070 let out_recv = sliced! {
5071 let batch = use::batch(trigger, nondet!());
5072 let offset = batch
5073 .location()
5074 .source_iter(q!(vec![10i32]))
5075 .fold(q!(|| 0i32), q!(|acc, v| *acc += v));
5076 let offset_ref = offset.by_ref();
5077 batch
5078 .location()
5079 .source_iter(q!(vec![1i32, 2, 3]))
5080 .scan(
5081 q!(|| 0i32),
5082 q!(move |acc: &mut i32, x| {
5083 *acc += x + *offset_ref;
5084 Some(*acc)
5085 }),
5086 )
5087 }
5088 .sim_output();
5089
5090 let count = flow.sim().exhaustive(async || {
5091 trigger_send.send(1);
5092 let all: Vec<i32> = out_recv.collect().await;
5093 assert_eq!(all, vec![11, 23, 36]);
5098 });
5099
5100 assert_eq!(
5101 count, 1,
5102 "Expected a single simulation instance for a totally-ordered scan, got {}",
5103 count
5104 );
5105 }
5106
5107 #[cfg(feature = "sim")]
5111 #[test]
5112 #[ignore = "observe_nondet not yet supported for top-level bounded inputs (https://github.com/hydro-project/hydro/issues/2950)"]
5113 fn sim_map_with_mut_on_unordered_top_level() {
5114 use crate::properties::manual_proof;
5115
5116 let mut flow = FlowBuilder::new();
5117 let node = flow.process::<()>();
5118
5119 let counter = node
5120 .source_iter(q!(vec![0i32]))
5121 .fold(q!(|| 0i32), q!(|acc, v| *acc += v));
5122 let counter_mut = counter.by_mut();
5123
5124 let out_recv = node
5125 .source_iter(q!(vec![1i32, 2]))
5126 .weaken_ordering::<NoOrder>()
5127 .map(q!(
5128 |x| {
5129 *counter_mut += x;
5130 *counter_mut
5131 },
5132 commutative = manual_proof!()
5133 ))
5134 .assume_ordering::<TotalOrder>(nondet!())
5135 .sim_output();
5136
5137 counter.into_stream().for_each(q!(|_| {}));
5138
5139 let count = flow.sim().exhaustive(async || {
5140 let _all: Vec<i32> = out_recv.collect().await;
5141 });
5142
5143 assert_eq!(
5144 count, 2,
5145 "Expected 2 simulation instances due to mut on unordered input, got {}",
5146 count
5147 );
5148 }
5149}