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<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<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<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<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<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<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<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<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<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<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 proof.register_proof(&comb);
1459
1460 let nondet = nondet!();
1463 let retried: Stream<T, L::DropConsistency, B, O, ExactlyOnce> = self.assume_retries(nondet);
1464
1465 let core = HydroNode::Fold {
1466 init,
1467 acc: comb.into(),
1468 input: Box::new(retried.ir_node.replace(HydroNode::Placeholder)),
1469 metadata: retried
1470 .location
1471 .new_node_metadata(Singleton::<A, L::DropConsistency, B2>::collection_kind()),
1472 };
1477
1478 Singleton::new(retried.location.clone(), core)
1479 .assert_has_consistency_of(manual_proof!())
1480 }
1481
1482 pub fn reduce<F, C, Idemp>(
1505 self,
1506 comb: impl IntoQuotedMut<'a, F, OperatorContext<L, B>, AggFuncAlgebra<C, Idemp>>,
1507 ) -> Optional<T, L, B>
1508 where
1509 F: Fn(&mut T, T) + 'a,
1510 C: ValidCommutativityFor<O>,
1511 Idemp: ValidIdempotenceFor<R>,
1512 {
1513 let (f, proof) =
1514 comb.splice_fn2_borrow_mut_ctx_props(&OperatorContext::<L, B>::new(&self.location));
1515 proof.register_proof(&f);
1516
1517 let nondet = nondet!();
1518 let ordered_etc: Stream<T, L::DropConsistency, B> =
1519 self.assume_retries(nondet).assume_ordering(nondet);
1520
1521 let core = HydroNode::Reduce {
1522 f: f.into(),
1523 input: Box::new(ordered_etc.ir_node.replace(HydroNode::Placeholder)),
1524 metadata: ordered_etc
1525 .location
1526 .new_node_metadata(Optional::<T, L::DropConsistency, B>::collection_kind()),
1527 };
1528
1529 Optional::new(ordered_etc.location.clone(), core)
1530 .assert_has_consistency_of(manual_proof!())
1531 }
1532
1533 pub fn max(self) -> Optional<T, L, B>
1553 where
1554 T: Ord,
1555 {
1556 self.assume_retries_trusted::<ExactlyOnce>(nondet!())
1557 .assume_ordering_trusted_bounded::<TotalOrder>(
1558 nondet!(),
1559 )
1560 .reduce(q!(|curr, new| {
1561 if new > *curr {
1562 *curr = new;
1563 }
1564 }))
1565 }
1566
1567 pub fn min(self) -> Optional<T, L, B>
1587 where
1588 T: Ord,
1589 {
1590 self.assume_retries_trusted::<ExactlyOnce>(nondet!())
1591 .assume_ordering_trusted_bounded::<TotalOrder>(
1592 nondet!(),
1593 )
1594 .reduce(q!(|curr, new| {
1595 if new < *curr {
1596 *curr = new;
1597 }
1598 }))
1599 }
1600
1601 pub fn first(self) -> Optional<T, L, B>
1624 where
1625 O: IsOrdered,
1626 {
1627 self.make_totally_ordered()
1628 .assume_retries_trusted::<ExactlyOnce>(nondet!())
1629 .generator(q!(|| ()), q!(|_, item| Generate::Return(item)))
1630 .reduce(q!(|_, _| {}))
1631 }
1632
1633 pub fn last(self) -> Optional<T, L, B>
1656 where
1657 O: IsOrdered,
1658 {
1659 self.make_totally_ordered()
1660 .assume_retries_trusted::<ExactlyOnce>(nondet!())
1661 .reduce(q!(|curr, new| *curr = new))
1662 }
1663
1664 pub fn limit(
1687 self,
1688 n: impl QuotedWithContext<'a, usize, OperatorContext<L, B>> + Copy + 'a,
1689 ) -> Stream<T, L, B, TotalOrder, ExactlyOnce>
1690 where
1691 O: IsOrdered,
1692 R: IsExactlyOnce,
1693 {
1694 self.generator(
1695 q!(|| 0usize),
1696 q!(move |count, item| {
1697 if *count == n {
1698 Generate::Break
1699 } else {
1700 *count += 1;
1701 if *count == n {
1702 Generate::Return(item)
1703 } else {
1704 Generate::Yield(item)
1705 }
1706 }
1707 }),
1708 )
1709 }
1710
1711 pub fn collect_vec(self) -> Singleton<Vec<T>, L, B>
1737 where
1738 O: IsOrdered,
1739 R: IsExactlyOnce,
1740 {
1741 self.make_totally_ordered().make_exactly_once().fold(
1742 q!(|| vec![]),
1743 q!(|acc, v| {
1744 acc.push(v);
1745 }),
1746 )
1747 }
1748
1749 pub fn scan<A, U, I, F>(
1815 self,
1816 init: impl IntoQuotedMut<'a, I, OperatorContext<L, B>>,
1817 f: impl IntoQuotedMut<'a, F, OperatorContext<L, B>>,
1818 ) -> Stream<U, L, B, TotalOrder, ExactlyOnce>
1819 where
1820 O: IsOrdered,
1821 R: IsExactlyOnce,
1822 I: Fn() -> A + 'a,
1823 F: Fn(&mut A, T) -> Option<U> + 'a,
1824 {
1825 let init = crate::handoff_ref::with_ref_capture(|| {
1826 init.splice_fn0_ctx(&OperatorContext::<L, B>::new(&self.location))
1827 .into()
1828 });
1829 let f = crate::handoff_ref::with_ref_capture(|| {
1830 f.splice_fn2_borrow_mut_ctx(&OperatorContext::<L, B>::new(&self.location))
1831 .into()
1832 });
1833
1834 Stream::new(
1835 self.location.clone(),
1836 HydroNode::Scan {
1837 init,
1838 acc: f,
1839 input: Box::new(self.ir_node.replace(HydroNode::Placeholder)),
1840 metadata: self.location.new_node_metadata(
1841 Stream::<U, L, B, TotalOrder, ExactlyOnce>::collection_kind(),
1842 ),
1843 },
1844 )
1845 }
1846
1847 pub fn scan_async_blocking<A, U, I, F, Fut>(
1886 self,
1887 init: impl IntoQuotedMut<'a, I, OperatorContext<L, B>>,
1888 f: impl IntoQuotedMut<'a, F, OperatorContext<L, B>>,
1889 ) -> Stream<U, L, B, TotalOrder, ExactlyOnce>
1890 where
1891 O: IsOrdered,
1892 R: IsExactlyOnce,
1893 I: Fn() -> A + 'a,
1894 F: Fn(&mut A, T) -> Fut + 'a,
1895 Fut: Future<Output = Option<U>> + 'a,
1896 {
1897 let init = crate::handoff_ref::with_ref_capture(|| {
1898 init.splice_fn0_ctx(&OperatorContext::<L, B>::new(&self.location))
1899 .into()
1900 });
1901 let f = crate::handoff_ref::with_ref_capture(|| {
1902 f.splice_fn2_borrow_mut_ctx(&OperatorContext::<L, B>::new(&self.location))
1903 .into()
1904 });
1905
1906 Stream::new(
1907 self.location.clone(),
1908 HydroNode::ScanAsyncBlocking {
1909 init,
1910 acc: f,
1911 input: Box::new(self.ir_node.replace(HydroNode::Placeholder)),
1912 metadata: self.location.new_node_metadata(
1913 Stream::<U, L, B, TotalOrder, ExactlyOnce>::collection_kind(),
1914 ),
1915 },
1916 )
1917 }
1918
1919 pub fn generator<A, U, I, F>(
1964 self,
1965 init: impl IntoQuotedMut<'a, I, OperatorContext<L, B>> + Copy,
1966 f: impl IntoQuotedMut<'a, F, OperatorContext<L, B>> + Copy,
1967 ) -> Stream<U, L, B, TotalOrder, ExactlyOnce>
1968 where
1969 O: IsOrdered,
1970 R: IsExactlyOnce,
1971 I: Fn() -> A + 'a,
1972 F: Fn(&mut A, T) -> Generate<U> + 'a,
1973 {
1974 let init: ManualExpr<I, _> =
1975 ManualExpr::new(move |ctx: &OperatorContext<L, B>| init.splice_fn0_ctx(ctx));
1976 let f: ManualExpr<F, _> =
1977 ManualExpr::new(move |ctx: &OperatorContext<L, B>| f.splice_fn2_borrow_mut_ctx(ctx));
1978
1979 let this = self.make_totally_ordered().make_exactly_once();
1980
1981 let scan_init = crate::handoff_ref::with_ref_capture(|| {
1986 q!(|| None)
1987 .splice_fn0_ctx::<Option<Option<A>>>(&this.location)
1988 .into()
1989 });
1990 let scan_f = crate::handoff_ref::with_ref_capture(|| {
1991 q!(move |state: &mut Option<Option<_>>, v| {
1992 if state.is_none() {
1993 *state = Some(Some(init()));
1994 }
1995 match state {
1996 Some(Some(state_value)) => match f(state_value, v) {
1997 Generate::Yield(out) => Some(Some(out)),
1998 Generate::Return(out) => {
1999 *state = Some(None);
2000 Some(Some(out))
2001 }
2002 Generate::Break => None,
2006 Generate::Continue => Some(None),
2007 },
2008 _ => None,
2010 }
2011 })
2012 .splice_fn2_borrow_mut_ctx::<Option<Option<A>>, T, _>(&OperatorContext::<L, B>::new(
2013 &this.location,
2014 ))
2015 .into()
2016 });
2017
2018 let scan_node = HydroNode::Scan {
2019 init: scan_init,
2020 acc: scan_f,
2021 input: Box::new(this.ir_node.replace(HydroNode::Placeholder)),
2022 metadata: this.location.new_node_metadata(Stream::<
2023 Option<U>,
2024 L,
2025 B,
2026 TotalOrder,
2027 ExactlyOnce,
2028 >::collection_kind()),
2029 };
2030
2031 let flatten_f = q!(|d| d)
2032 .splice_fn1_ctx::<Option<U>, _>(&this.location)
2033 .into();
2034 let flatten_node = HydroNode::FlatMap {
2035 f: flatten_f,
2036 input: Box::new(scan_node),
2037 metadata: this
2038 .location
2039 .new_node_metadata(Stream::<U, L, B, TotalOrder, ExactlyOnce>::collection_kind()),
2040 };
2041
2042 Stream::new(this.location.clone(), flatten_node)
2043 }
2044
2045 #[cfg(feature = "tokio")]
2054 pub fn sample_every(
2055 self,
2056 interval: impl QuotedWithContext<'a, std::time::Duration, L> + Copy + 'a,
2057 nondet: NonDet,
2058 ) -> Stream<T, L::DropConsistency, Unbounded, O, AtLeastOnce>
2059 where
2060 L: TopLevel<'a>,
2061 {
2062 let samples = self.location.source_interval(interval);
2063
2064 let tick = self.location.tick();
2065 self.batch(&tick, nondet)
2066 .filter_if(samples.batch(&tick, nondet).first().is_some())
2067 .all_ticks()
2068 .weaken_retries()
2069 }
2070
2071 #[cfg(feature = "tokio")]
2081 pub fn timeout(
2082 self,
2083 duration: impl QuotedWithContext<
2084 'a,
2085 std::time::Duration,
2086 OperatorContext<Tick<L::DropConsistency>, Bounded>,
2087 > + Copy
2088 + 'a,
2089 nondet: NonDet,
2090 ) -> Optional<(), L::DropConsistency, Unbounded>
2091 where
2092 L: TopLevel<'a>,
2093 {
2094 let tick = self.location.tick();
2095
2096 let latest_received = self.assume_retries::<ExactlyOnce>(nondet).fold(
2097 q!(|| None),
2098 q!(
2099 |latest, _| {
2100 *latest = Some(Instant::now());
2101 },
2102 commutative = manual_proof!()
2103 ),
2104 );
2105
2106 latest_received
2107 .snapshot(&tick, nondet)
2108 .filter_map(q!(move |latest_received| {
2109 if let Some(latest_received) = latest_received {
2110 if Instant::now().duration_since(latest_received) > duration {
2111 Some(())
2112 } else {
2113 None
2114 }
2115 } else {
2116 Some(())
2117 }
2118 }))
2119 .latest()
2120 }
2121
2122 pub fn atomic(self) -> Stream<T, Atomic<L>, B, O, R> {
2128 let id = self.location.flow_state().borrow_mut().next_clock_id();
2129 let out_location = Atomic {
2130 tick: Tick {
2131 id,
2132 l: self.location.clone(),
2133 },
2134 };
2135 Stream::new(
2136 out_location.clone(),
2137 HydroNode::BeginAtomic {
2138 inner: Box::new(self.ir_node.replace(HydroNode::Placeholder)),
2139 metadata: out_location
2140 .new_node_metadata(Stream::<T, Atomic<L>, B, O, R>::collection_kind()),
2141 },
2142 )
2143 }
2144
2145 pub fn batch<L2: Location<'a, DropConsistency = L::DropConsistency>>(
2153 self,
2154 tick: &Tick<L2>,
2155 _nondet: NonDet,
2156 ) -> Stream<T, Tick<L::DropConsistency>, Bounded, O, R> {
2157 assert_eq!(Location::id(tick.outer()), Location::id(&self.location));
2158 Stream::new(
2159 tick.drop_consistency(),
2160 HydroNode::Batch {
2161 inner: Box::new(self.ir_node.replace(HydroNode::Placeholder)),
2162 metadata: tick
2163 .new_node_metadata(Stream::<T, Tick<L>, Bounded, O, R>::collection_kind()),
2164 },
2165 )
2166 }
2167
2168 pub fn ir_node_named(self, name: &str) -> Stream<T, L, B, O, R> {
2171 {
2172 let mut node = self.ir_node.borrow_mut();
2173 let metadata = node.metadata_mut();
2174 metadata.tag = Some(name.to_owned());
2175 }
2176 self
2177 }
2178
2179 pub(crate) fn cast_at_most_one_element(self) -> Optional<T, L, B>
2183 where
2184 B: IsBounded,
2185 {
2186 Optional::new(
2187 self.location.clone(),
2188 HydroNode::Cast {
2189 inner: Box::new(self.ir_node.replace(HydroNode::Placeholder)),
2190 metadata: self
2191 .location
2192 .new_node_metadata(Optional::<T, L, B>::collection_kind()),
2193 },
2194 )
2195 }
2196
2197 pub(crate) fn use_ordering_type<O2: Ordering>(self) -> Stream<T, L, B, O2, R> {
2198 if O::ORDERING_KIND == O2::ORDERING_KIND {
2199 Stream::new(
2200 self.location.clone(),
2201 self.ir_node.replace(HydroNode::Placeholder),
2202 )
2203 } else {
2204 panic!(
2205 "Runtime ordering {:?} did not match requested cast {:?}.",
2206 O::ORDERING_KIND,
2207 O2::ORDERING_KIND
2208 )
2209 }
2210 }
2211
2212 pub fn assume_ordering<O2: Ordering>(
2221 self,
2222 _nondet: NonDet,
2223 ) -> Stream<T, L::DropConsistency, B, O2, R> {
2224 if O::ORDERING_KIND == O2::ORDERING_KIND {
2225 self.use_ordering_type().weaken_consistency()
2226 } else if O2::ORDERING_KIND == StreamOrder::NoOrder {
2227 let target_location = self.location().drop_consistency();
2229 Stream::new(
2230 target_location.clone(),
2231 HydroNode::Cast {
2232 inner: Box::new(self.ir_node.replace(HydroNode::Placeholder)),
2233 metadata: target_location
2234 .new_node_metadata(Stream::<T, L, B, O2, R>::collection_kind()),
2235 },
2236 )
2237 } else {
2238 let target_location = self.location().drop_consistency();
2239 Stream::new(
2240 target_location.clone(),
2241 HydroNode::ObserveNonDet {
2242 inner: Box::new(self.ir_node.replace(HydroNode::Placeholder)),
2243 trusted: false,
2244 metadata: target_location
2245 .new_node_metadata(Stream::<T, L, B, O2, R>::collection_kind()),
2246 },
2247 )
2248 }
2249 }
2250
2251 fn assume_ordering_trusted_bounded<O2: Ordering>(
2254 self,
2255 nondet: NonDet,
2256 ) -> Stream<T, L, B, O2, R> {
2257 if B::BOUNDED {
2258 self.assume_ordering_trusted(nondet)
2259 } else {
2260 let self_location = self.location.clone();
2261 let inner: Stream<T, L::DropConsistency, B, O2, R> = self.assume_ordering(nondet);
2262 Stream::new(self_location, inner.ir_node.replace(HydroNode::Placeholder))
2263 }
2264 }
2265
2266 pub(crate) fn assume_ordering_trusted<O2: Ordering>(
2269 self,
2270 _nondet: NonDet,
2271 ) -> Stream<T, L, B, O2, R> {
2272 if O::ORDERING_KIND == O2::ORDERING_KIND {
2273 self.use_ordering_type()
2274 } else if O2::ORDERING_KIND == StreamOrder::NoOrder {
2275 Stream::new(
2277 self.location.clone(),
2278 HydroNode::Cast {
2279 inner: Box::new(self.ir_node.replace(HydroNode::Placeholder)),
2280 metadata: self
2281 .location
2282 .new_node_metadata(Stream::<T, L, B, O2, R>::collection_kind()),
2283 },
2284 )
2285 } else {
2286 Stream::new(
2287 self.location.clone(),
2288 HydroNode::ObserveNonDet {
2289 inner: Box::new(self.ir_node.replace(HydroNode::Placeholder)),
2290 trusted: true,
2291 metadata: self
2292 .location
2293 .new_node_metadata(Stream::<T, L, B, O2, R>::collection_kind()),
2294 },
2295 )
2296 }
2297 }
2298
2299 #[deprecated = "use `weaken_ordering::<NoOrder>()` instead"]
2300 pub fn weakest_ordering(self) -> Stream<T, L, B, NoOrder, R> {
2303 self.weaken_ordering::<NoOrder>()
2304 }
2305
2306 pub fn weaken_ordering<O2: WeakerOrderingThan<O>>(self) -> Stream<T, L, B, O2, R> {
2309 let nondet = nondet!();
2310 self.assume_ordering_trusted::<O2>(nondet)
2311 }
2312
2313 pub fn make_totally_ordered(self) -> Stream<T, L, B, TotalOrder, R>
2316 where
2317 O: IsOrdered,
2318 {
2319 self.assume_ordering_trusted(nondet!())
2320 }
2321
2322 pub fn assume_retries<R2: Retries>(
2331 self,
2332 _nondet: NonDet,
2333 ) -> Stream<T, L::DropConsistency, B, O, R2> {
2334 if R::RETRIES_KIND == R2::RETRIES_KIND {
2335 Stream::new(
2336 self.location.drop_consistency(),
2337 self.ir_node.replace(HydroNode::Placeholder),
2338 )
2339 } else if R2::RETRIES_KIND == StreamRetry::AtLeastOnce {
2340 let target_location = self.location.drop_consistency();
2342 Stream::new(
2343 target_location.clone(),
2344 HydroNode::Cast {
2345 inner: Box::new(self.ir_node.replace(HydroNode::Placeholder)),
2346 metadata: target_location
2347 .new_node_metadata(Stream::<T, L, B, O, R2>::collection_kind()),
2348 },
2349 )
2350 } else {
2351 let target_location = self.location.drop_consistency();
2352 Stream::new(
2353 target_location.clone(),
2354 HydroNode::ObserveNonDet {
2355 inner: Box::new(self.ir_node.replace(HydroNode::Placeholder)),
2356 trusted: false,
2357 metadata: target_location
2358 .new_node_metadata(Stream::<T, L, B, O, R2>::collection_kind()),
2359 },
2360 )
2361 }
2362 }
2363
2364 fn assume_retries_trusted<R2: Retries>(self, _nondet: NonDet) -> Stream<T, L, B, O, R2> {
2367 if R::RETRIES_KIND == R2::RETRIES_KIND {
2368 Stream::new(
2369 self.location.clone(),
2370 self.ir_node.replace(HydroNode::Placeholder),
2371 )
2372 } else if R2::RETRIES_KIND == StreamRetry::AtLeastOnce {
2373 Stream::new(
2375 self.location.clone(),
2376 HydroNode::Cast {
2377 inner: Box::new(self.ir_node.replace(HydroNode::Placeholder)),
2378 metadata: self
2379 .location
2380 .new_node_metadata(Stream::<T, L, B, O, R2>::collection_kind()),
2381 },
2382 )
2383 } else {
2384 Stream::new(
2385 self.location.clone(),
2386 HydroNode::ObserveNonDet {
2387 inner: Box::new(self.ir_node.replace(HydroNode::Placeholder)),
2388 trusted: true,
2389 metadata: self
2390 .location
2391 .new_node_metadata(Stream::<T, L, B, O, R2>::collection_kind()),
2392 },
2393 )
2394 }
2395 }
2396
2397 #[deprecated = "use `weaken_retries::<AtLeastOnce>()` instead"]
2398 pub fn weakest_retries(self) -> Stream<T, L, B, O, AtLeastOnce> {
2401 self.weaken_retries::<AtLeastOnce>()
2402 }
2403
2404 pub fn weaken_retries<R2: WeakerRetryThan<R>>(self) -> Stream<T, L, B, O, R2> {
2407 let nondet = nondet!();
2408 self.assume_retries_trusted::<R2>(nondet)
2409 }
2410
2411 pub fn make_exactly_once(self) -> Stream<T, L, B, O, ExactlyOnce>
2414 where
2415 R: IsExactlyOnce,
2416 {
2417 self.assume_retries_trusted(nondet!())
2418 }
2419
2420 pub fn make_bounded(self) -> Stream<T, L, Bounded, O, R>
2423 where
2424 B: IsBounded,
2425 {
2426 self.weaken_boundedness()
2427 }
2428
2429 pub fn weaken_boundedness<B2: Boundedness>(self) -> Stream<T, L, B2, O, R> {
2432 if B::BOUNDED == B2::BOUNDED {
2433 Stream::new(
2434 self.location.clone(),
2435 self.ir_node.replace(HydroNode::Placeholder),
2436 )
2437 } else {
2438 Stream::new(
2440 self.location.clone(),
2441 HydroNode::Cast {
2442 inner: Box::new(self.ir_node.replace(HydroNode::Placeholder)),
2443 metadata: self
2444 .location
2445 .new_node_metadata(Stream::<T, L, B2, O, R>::collection_kind()),
2446 },
2447 )
2448 }
2449 }
2450}
2451
2452impl<'a, T, L, B: Boundedness, O: Ordering, R: Retries> Stream<&T, L, B, O, R>
2453where
2454 L: Location<'a>,
2455{
2456 pub fn cloned(self) -> Stream<T, L, B, O, R>
2474 where
2475 T: Clone,
2476 {
2477 self.map(q!(|d| d.clone()))
2478 }
2479}
2480
2481impl<'a, T, L, B: Boundedness, O: Ordering> Stream<T, L, B, O, ExactlyOnce>
2482where
2483 L: Location<'a>,
2484{
2485 pub fn count(self) -> Singleton<usize, L, B::StreamToMonotone> {
2504 self.assume_ordering_trusted::<TotalOrder>(nondet!(
2505 ))
2507 .fold(
2508 q!(|| 0usize),
2509 q!(
2510 |count, _| *count += 1,
2511 monotone = manual_proof!()
2512 ),
2513 )
2514 }
2515}
2516
2517impl<'a, T, L: Location<'a>, O: Ordering, R: Retries> Stream<T, L, Unbounded, O, R> {
2518 pub fn merge_unordered<O2: Ordering, R2: Retries>(
2542 self,
2543 other: Stream<T, L, Unbounded, O2, R2>,
2544 ) -> Stream<T, L, Unbounded, NoOrder, <R as MinRetries<R2>>::Min>
2545 where
2546 R: MinRetries<R2>,
2547 {
2548 Stream::new(
2549 self.location.clone(),
2550 HydroNode::Chain {
2551 first: Box::new(self.ir_node.replace(HydroNode::Placeholder)),
2552 second: Box::new(other.ir_node.replace(HydroNode::Placeholder)),
2553 metadata: self.location.new_node_metadata(Stream::<
2554 T,
2555 L,
2556 Unbounded,
2557 NoOrder,
2558 <R as MinRetries<R2>>::Min,
2559 >::collection_kind()),
2560 },
2561 )
2562 }
2563
2564 #[deprecated(note = "use `merge_unordered` instead")]
2566 pub fn interleave<O2: Ordering, R2: Retries>(
2567 self,
2568 other: Stream<T, L, Unbounded, O2, R2>,
2569 ) -> Stream<T, L, Unbounded, NoOrder, <R as MinRetries<R2>>::Min>
2570 where
2571 R: MinRetries<R2>,
2572 {
2573 self.merge_unordered(other)
2574 }
2575}
2576
2577impl<'a, T, L: Location<'a>, B: Boundedness, R: Retries> Stream<T, L, B, TotalOrder, R> {
2578 pub fn merge_ordered<R2: Retries>(
2606 self,
2607 other: Stream<T, L, B, TotalOrder, R2>,
2608 _nondet: NonDet,
2609 ) -> Stream<T, L::DropConsistency, B, TotalOrder, <R as MinRetries<R2>>::Min>
2610 where
2611 R: MinRetries<R2>,
2612 {
2613 let target_location = self.location().drop_consistency();
2614 Stream::new(
2615 target_location.clone(),
2616 HydroNode::MergeOrdered {
2617 first: Box::new(self.ir_node.replace(HydroNode::Placeholder)),
2618 second: Box::new(other.ir_node.replace(HydroNode::Placeholder)),
2619 metadata: target_location.new_node_metadata(Stream::<
2620 T,
2621 L::DropConsistency,
2622 B,
2623 TotalOrder,
2624 <R as MinRetries<R2>>::Min,
2625 >::collection_kind()),
2626 },
2627 )
2628 }
2629}
2630
2631impl<'a, T, L, B: Boundedness, O: Ordering, R: Retries> Stream<T, L, B, O, R>
2632where
2633 L: Location<'a>,
2634{
2635 pub fn sort(self) -> Stream<T, L, Bounded, TotalOrder, R>
2661 where
2662 B: IsBounded,
2663 T: Ord,
2664 {
2665 let this = self.make_bounded();
2666 Stream::new(
2667 this.location.clone(),
2668 HydroNode::Sort {
2669 input: Box::new(this.ir_node.replace(HydroNode::Placeholder)),
2670 metadata: this
2671 .location
2672 .new_node_metadata(Stream::<T, L, Bounded, TotalOrder, R>::collection_kind()),
2673 },
2674 )
2675 }
2676
2677 pub fn chain<O2: Ordering, R2: Retries, B2: Boundedness>(
2705 self,
2706 other: Stream<T, L, B2, O2, R2>,
2707 ) -> Stream<T, L, B2, <O as MinOrder<O2>>::Min, <R as MinRetries<R2>>::Min>
2708 where
2709 B: IsBounded,
2710 O: MinOrder<O2>,
2711 R: MinRetries<R2>,
2712 {
2713 check_matching_location(&self.location, &other.location);
2714
2715 Stream::new(
2716 self.location.clone(),
2717 HydroNode::Chain {
2718 first: Box::new(self.ir_node.replace(HydroNode::Placeholder)),
2719 second: Box::new(other.ir_node.replace(HydroNode::Placeholder)),
2720 metadata: self.location.new_node_metadata(Stream::<
2721 T,
2722 L,
2723 B2,
2724 <O as MinOrder<O2>>::Min,
2725 <R as MinRetries<R2>>::Min,
2726 >::collection_kind()),
2727 },
2728 )
2729 }
2730
2731 pub fn cross_product_nested_loop<T2, O2: Ordering + MinOrder<O>, R2: Retries>(
2735 self,
2736 other: Stream<T2, L, Bounded, O2, R2>,
2737 ) -> Stream<(T, T2), L, Bounded, <O2 as MinOrder<O>>::Min, <R as MinRetries<R2>>::Min>
2738 where
2739 B: IsBounded,
2740 T: Clone,
2741 T2: Clone,
2742 R: MinRetries<R2>,
2743 {
2744 let this = self.make_bounded();
2745 check_matching_location(&this.location, &other.location);
2746
2747 Stream::new(
2748 this.location.clone(),
2749 HydroNode::CrossProduct {
2750 left: Box::new(this.ir_node.replace(HydroNode::Placeholder)),
2751 right: Box::new(other.ir_node.replace(HydroNode::Placeholder)),
2752 metadata: this.location.new_node_metadata(Stream::<
2753 (T, T2),
2754 L,
2755 Bounded,
2756 <O2 as MinOrder<O>>::Min,
2757 <R as MinRetries<R2>>::Min,
2758 >::collection_kind()),
2759 },
2760 )
2761 }
2762
2763 pub fn repeat_with_keys<K, V2>(
2801 self,
2802 keys: KeyedSingleton<K, V2, L, Bounded>,
2803 ) -> KeyedStream<K, T, L, Bounded, O, R>
2804 where
2805 B: IsBounded,
2806 K: Clone,
2807 T: Clone,
2808 {
2809 keys.keys()
2810 .assume_ordering_trusted::<TotalOrder>(
2811 nondet!(),
2812 )
2813 .cross_product_nested_loop(self.make_bounded())
2814 .into_keyed()
2815 }
2816
2817 pub fn resolve_futures_blocking(self) -> Stream<T::Output, L, B, NoOrder, R>
2854 where
2855 T: Future,
2856 {
2857 Stream::new(
2858 self.location.clone(),
2859 HydroNode::ResolveFuturesBlocking {
2860 input: Box::new(self.ir_node.replace(HydroNode::Placeholder)),
2861 metadata: self
2862 .location
2863 .new_node_metadata(Stream::<T::Output, L, B, NoOrder, R>::collection_kind()),
2864 },
2865 )
2866 }
2867
2868 #[expect(clippy::wrong_self_convention, reason = "stream function naming")]
2888 pub fn is_empty(self) -> Singleton<bool, L, Bounded>
2889 where
2890 B: IsBounded,
2891 {
2892 self.make_bounded()
2893 .assume_ordering_trusted::<TotalOrder>(
2894 nondet!(),
2895 )
2896 .first()
2897 .is_none()
2898 }
2899}
2900
2901impl<'a, K, V1, L, B: Boundedness, O: Ordering, R: Retries> Stream<(K, V1), L, B, O, R>
2902where
2903 L: Location<'a>,
2904{
2905 pub fn join<V2, B2: Boundedness, O2: Ordering, R2: Retries>(
2930 self,
2931 n: Stream<(K, V2), L, B2, O2, R2>,
2932 ) -> Stream<(K, (V1, V2)), L, B, B2::PreserveOrderIfBounded<O>, <R as MinRetries<R2>>::Min>
2933 where
2934 K: Eq + Hash + Clone,
2935 R: MinRetries<R2>,
2936 V1: Clone,
2937 V2: Clone,
2938 {
2939 check_matching_location(&self.location, &n.location);
2940
2941 let ir_node = if B2::BOUNDED {
2942 HydroNode::JoinHalf {
2943 left: Box::new(self.ir_node.replace(HydroNode::Placeholder)),
2944 right: Box::new(n.ir_node.replace(HydroNode::Placeholder)),
2945 metadata: self.location.new_node_metadata(Stream::<
2946 (K, (V1, V2)),
2947 L,
2948 B,
2949 B2::PreserveOrderIfBounded<O>,
2950 <R as MinRetries<R2>>::Min,
2951 >::collection_kind()),
2952 }
2953 } else {
2954 HydroNode::Join {
2955 left: Box::new(self.ir_node.replace(HydroNode::Placeholder)),
2956 right: Box::new(n.ir_node.replace(HydroNode::Placeholder)),
2957 metadata: self.location.new_node_metadata(Stream::<
2958 (K, (V1, V2)),
2959 L,
2960 B,
2961 B2::PreserveOrderIfBounded<O>,
2962 <R as MinRetries<R2>>::Min,
2963 >::collection_kind()),
2964 }
2965 };
2966
2967 Stream::new(self.location.clone(), ir_node)
2968 }
2969
2970 pub fn anti_join<O2: Ordering, R2: Retries>(
2996 self,
2997 n: Stream<K, L, Bounded, O2, R2>,
2998 ) -> Stream<(K, V1), L, B, O, R>
2999 where
3000 K: Eq + Hash,
3001 {
3002 check_matching_location(&self.location, &n.location);
3003
3004 Stream::new(
3005 self.location.clone(),
3006 HydroNode::AntiJoin {
3007 pos: Box::new(self.ir_node.replace(HydroNode::Placeholder)),
3008 neg: Box::new(n.ir_node.replace(HydroNode::Placeholder)),
3009 metadata: self
3010 .location
3011 .new_node_metadata(Stream::<(K, V1), L, B, O, R>::collection_kind()),
3012 },
3013 )
3014 }
3015}
3016
3017impl<'a, K, V, L: Location<'a>, B: Boundedness, O: Ordering, R: Retries>
3018 Stream<(K, V), L, B, O, R>
3019{
3020 pub fn into_keyed(self) -> KeyedStream<K, V, L, B, O, R> {
3047 KeyedStream::new(
3048 self.location.clone(),
3049 HydroNode::Cast {
3050 inner: Box::new(self.ir_node.replace(HydroNode::Placeholder)),
3051 metadata: self
3052 .location
3053 .new_node_metadata(KeyedStream::<K, V, L, B, O, R>::collection_kind()),
3054 },
3055 )
3056 }
3057}
3058
3059impl<'a, K, V, L, O: Ordering, R: Retries> Stream<(K, V), Tick<L>, Bounded, O, R>
3060where
3061 K: Eq + Hash,
3062 L: Location<'a>,
3063{
3064 pub fn keys(self) -> Stream<K, Tick<L>, Bounded, NoOrder, ExactlyOnce> {
3083 self.into_keyed()
3084 .fold(
3085 q!(|| ()),
3086 q!(
3087 |_, _| {},
3088 commutative = manual_proof!(),
3089 idempotent = manual_proof!()
3090 ),
3091 )
3092 .keys()
3093 }
3094}
3095
3096impl<'a, T, L, B: Boundedness, O: Ordering, R: Retries> Stream<T, Atomic<L>, B, O, R>
3097where
3098 L: Location<'a>,
3099{
3100 pub fn batch_atomic<L2: Location<'a, DropConsistency = L::DropConsistency>>(
3107 self,
3108 tick: &Tick<L2>,
3109 _nondet: NonDet,
3110 ) -> Stream<T, Tick<L::DropConsistency>, Bounded, O, R> {
3111 Stream::new(
3112 tick.drop_consistency(),
3113 HydroNode::Batch {
3114 inner: Box::new(self.ir_node.replace(HydroNode::Placeholder)),
3115 metadata: tick
3116 .new_node_metadata(Stream::<T, Tick<L>, Bounded, O, R>::collection_kind()),
3117 },
3118 )
3119 }
3120
3121 pub fn end_atomic(self) -> Stream<T, L, B, O, R> {
3124 Stream::new(
3125 self.location.tick.l.clone(),
3126 HydroNode::EndAtomic {
3127 inner: Box::new(self.ir_node.replace(HydroNode::Placeholder)),
3128 metadata: self
3129 .location
3130 .tick
3131 .l
3132 .new_node_metadata(Stream::<T, L, B, O, R>::collection_kind()),
3133 },
3134 )
3135 }
3136}
3137
3138impl<'a, F, T, L, B: Boundedness, O: Ordering, R: Retries> Stream<F, L, B, O, R>
3139where
3140 L: TopLevel<'a>,
3141 F: Future<Output = T>,
3142{
3143 pub fn resolve_futures(self) -> Stream<T, L, Unbounded, NoOrder, R> {
3174 Stream::new(
3175 self.location.clone(),
3176 HydroNode::ResolveFutures {
3177 input: Box::new(self.ir_node.replace(HydroNode::Placeholder)),
3178 metadata: self
3179 .location
3180 .new_node_metadata(Stream::<T, L, Unbounded, NoOrder, R>::collection_kind()),
3181 },
3182 )
3183 }
3184
3185 pub fn resolve_futures_ordered(self) -> Stream<T, L, Unbounded, O, R> {
3216 Stream::new(
3217 self.location.clone(),
3218 HydroNode::ResolveFuturesOrdered {
3219 input: Box::new(self.ir_node.replace(HydroNode::Placeholder)),
3220 metadata: self
3221 .location
3222 .new_node_metadata(Stream::<T, L, Unbounded, O, R>::collection_kind()),
3223 },
3224 )
3225 }
3226}
3227
3228impl<'a, T, L, O: Ordering, R: Retries> Stream<T, Tick<L>, Bounded, O, R>
3229where
3230 L: Location<'a>,
3231{
3232 pub fn all_ticks(self) -> Stream<T, L, Unbounded, O, R> {
3235 Stream::new(
3236 self.location.outer().clone(),
3237 HydroNode::YieldConcat {
3238 inner: Box::new(self.ir_node.replace(HydroNode::Placeholder)),
3239 metadata: self
3240 .location
3241 .outer()
3242 .new_node_metadata(Stream::<T, L, Unbounded, O, R>::collection_kind()),
3243 },
3244 )
3245 }
3246
3247 pub fn all_ticks_atomic(self) -> Stream<T, Atomic<L>, Unbounded, O, R> {
3254 let out_location = Atomic {
3255 tick: self.location.clone(),
3256 };
3257
3258 Stream::new(
3259 out_location.clone(),
3260 HydroNode::YieldConcat {
3261 inner: Box::new(self.ir_node.replace(HydroNode::Placeholder)),
3262 metadata: out_location
3263 .new_node_metadata(Stream::<T, Atomic<L>, Unbounded, O, R>::collection_kind()),
3264 },
3265 )
3266 }
3267
3268 pub fn across_ticks<Out: BatchAtomic<'a>>(
3303 self,
3304 thunk: impl FnOnce(Stream<T, Atomic<L>, Unbounded, O, R>) -> Out,
3305 ) -> Out::Batched {
3306 thunk(self.all_ticks_atomic()).batched_atomic()
3307 }
3308
3309 pub fn defer_tick(self) -> Stream<T, Tick<L>, Bounded, O, R> {
3348 Stream::new(
3349 self.location.clone(),
3350 HydroNode::DeferTick {
3351 input: Box::new(self.ir_node.replace(HydroNode::Placeholder)),
3352 metadata: self
3353 .location
3354 .new_node_metadata(Stream::<T, Tick<L>, Bounded, O, R>::collection_kind()),
3355 },
3356 )
3357 }
3358}
3359
3360#[cfg(test)]
3361mod tests {
3362 #[cfg(feature = "deploy")]
3363 use futures::{SinkExt, StreamExt};
3364 #[cfg(feature = "deploy")]
3365 use hydro_deploy::Deployment;
3366 #[cfg(feature = "deploy")]
3367 use serde::{Deserialize, Serialize};
3368 #[cfg(any(feature = "deploy", feature = "sim"))]
3369 use stageleft::q;
3370
3371 #[cfg(any(feature = "deploy", feature = "sim"))]
3372 use crate::compile::builder::FlowBuilder;
3373 #[cfg(feature = "deploy")]
3374 use crate::live_collections::sliced::sliced;
3375 #[cfg(feature = "deploy")]
3376 use crate::live_collections::stream::ExactlyOnce;
3377 #[cfg(feature = "sim")]
3378 use crate::live_collections::stream::NoOrder;
3379 #[cfg(any(feature = "deploy", feature = "sim"))]
3380 use crate::live_collections::stream::TotalOrder;
3381 #[cfg(any(feature = "deploy", feature = "sim"))]
3382 use crate::location::Location;
3383 #[cfg(feature = "sim")]
3384 use crate::networking::TCP;
3385 #[cfg(any(feature = "deploy", feature = "sim"))]
3386 use crate::nondet::nondet;
3387
3388 mod backtrace_chained_ops;
3389
3390 #[cfg(feature = "deploy")]
3391 struct P1 {}
3392 #[cfg(feature = "deploy")]
3393 struct P2 {}
3394
3395 #[cfg(feature = "deploy")]
3396 #[derive(Serialize, Deserialize, Debug)]
3397 struct SendOverNetwork {
3398 n: u32,
3399 }
3400
3401 #[cfg(feature = "deploy")]
3402 #[tokio::test]
3403 async fn first_ten_distributed() {
3404 use crate::networking::TCP;
3405
3406 let mut deployment = Deployment::new();
3407
3408 let mut flow = FlowBuilder::new();
3409 let first_node = flow.process::<P1>();
3410 let second_node = flow.process::<P2>();
3411 let external = flow.external::<P2>();
3412
3413 let numbers = first_node.source_iter(q!(0..10));
3414 let out_port = numbers
3415 .map(q!(|n| SendOverNetwork { n }))
3416 .send(&second_node, TCP.fail_stop().bincode())
3417 .send_bincode_external(&external);
3418
3419 let nodes = flow
3420 .with_process(&first_node, deployment.Localhost())
3421 .with_process(&second_node, deployment.Localhost())
3422 .with_external(&external, deployment.Localhost())
3423 .deploy(&mut deployment);
3424
3425 deployment.deploy().await.unwrap();
3426
3427 let mut external_out = nodes.connect(out_port).await;
3428
3429 deployment.start().await.unwrap();
3430
3431 for i in 0..10 {
3432 assert_eq!(external_out.next().await.unwrap().n, i);
3433 }
3434 }
3435
3436 #[cfg(feature = "deploy")]
3437 #[tokio::test]
3438 async fn first_cardinality() {
3439 let mut deployment = Deployment::new();
3440
3441 let mut flow = FlowBuilder::new();
3442 let node = flow.process::<()>();
3443 let external = flow.external::<()>();
3444
3445 let node_tick = node.tick();
3446 let count = node_tick
3447 .singleton(q!([1, 2, 3]))
3448 .into_stream()
3449 .flatten_ordered()
3450 .first()
3451 .into_stream()
3452 .count()
3453 .all_ticks()
3454 .send_bincode_external(&external);
3455
3456 let nodes = flow
3457 .with_process(&node, deployment.Localhost())
3458 .with_external(&external, deployment.Localhost())
3459 .deploy(&mut deployment);
3460
3461 deployment.deploy().await.unwrap();
3462
3463 let mut external_out = nodes.connect(count).await;
3464
3465 deployment.start().await.unwrap();
3466
3467 assert_eq!(external_out.next().await.unwrap(), 1);
3468 }
3469
3470 #[cfg(feature = "deploy")]
3471 #[tokio::test]
3472 async fn unbounded_reduce_remembers_state() {
3473 let mut deployment = Deployment::new();
3474
3475 let mut flow = FlowBuilder::new();
3476 let node = flow.process::<()>();
3477 let external = flow.external::<()>();
3478
3479 let (input_port, input) = node.source_external_bincode(&external);
3480 let out = input
3481 .reduce(q!(|acc, v| *acc += v))
3482 .sample_eager(nondet!())
3483 .send_bincode_external(&external);
3484
3485 let nodes = flow
3486 .with_process(&node, deployment.Localhost())
3487 .with_external(&external, deployment.Localhost())
3488 .deploy(&mut deployment);
3489
3490 deployment.deploy().await.unwrap();
3491
3492 let mut external_in = nodes.connect(input_port).await;
3493 let mut external_out = nodes.connect(out).await;
3494
3495 deployment.start().await.unwrap();
3496
3497 external_in.send(1).await.unwrap();
3498 assert_eq!(external_out.next().await.unwrap(), 1);
3499
3500 external_in.send(2).await.unwrap();
3501 assert_eq!(external_out.next().await.unwrap(), 3);
3502 }
3503
3504 #[cfg(feature = "deploy")]
3505 #[tokio::test]
3506 async fn top_level_bounded_cross_singleton() {
3507 let mut deployment = Deployment::new();
3508
3509 let mut flow = FlowBuilder::new();
3510 let node = flow.process::<()>();
3511 let external = flow.external::<()>();
3512
3513 let (input_port, input) =
3514 node.source_external_bincode::<_, _, TotalOrder, ExactlyOnce>(&external);
3515
3516 let out = input
3517 .cross_singleton(
3518 node.source_iter(q!(vec![1, 2, 3]))
3519 .fold(q!(|| 0), q!(|acc, v| *acc += v)),
3520 )
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_in = nodes.connect(input_port).await;
3531 let mut external_out = nodes.connect(out).await;
3532
3533 deployment.start().await.unwrap();
3534
3535 external_in.send(1).await.unwrap();
3536 assert_eq!(external_out.next().await.unwrap(), (1, 6));
3537
3538 external_in.send(2).await.unwrap();
3539 assert_eq!(external_out.next().await.unwrap(), (2, 6));
3540 }
3541
3542 #[cfg(feature = "deploy")]
3543 #[tokio::test]
3544 async fn top_level_bounded_reduce_cardinality() {
3545 let mut deployment = Deployment::new();
3546
3547 let mut flow = FlowBuilder::new();
3548 let node = flow.process::<()>();
3549 let external = flow.external::<()>();
3550
3551 let (input_port, input) =
3552 node.source_external_bincode::<_, _, TotalOrder, ExactlyOnce>(&external);
3553
3554 let out = sliced! {
3555 let input = use::batch(input, nondet!());
3556 let v = use::snapshot(node.source_iter(q!(vec![1, 2, 3])).reduce(q!(|acc, v| *acc += v)), nondet!());
3557 input.cross_singleton(v.into_stream().count())
3558 }
3559 .send_bincode_external(&external);
3560
3561 let nodes = flow
3562 .with_process(&node, deployment.Localhost())
3563 .with_external(&external, deployment.Localhost())
3564 .deploy(&mut deployment);
3565
3566 deployment.deploy().await.unwrap();
3567
3568 let mut external_in = nodes.connect(input_port).await;
3569 let mut external_out = nodes.connect(out).await;
3570
3571 deployment.start().await.unwrap();
3572
3573 external_in.send(1).await.unwrap();
3574 assert_eq!(external_out.next().await.unwrap(), (1, 1));
3575
3576 external_in.send(2).await.unwrap();
3577 assert_eq!(external_out.next().await.unwrap(), (2, 1));
3578 }
3579
3580 #[cfg(feature = "deploy")]
3581 #[tokio::test]
3582 async fn top_level_bounded_into_singleton_cardinality() {
3583 let mut deployment = Deployment::new();
3584
3585 let mut flow = FlowBuilder::new();
3586 let node = flow.process::<()>();
3587 let external = flow.external::<()>();
3588
3589 let (input_port, input) =
3590 node.source_external_bincode::<_, _, TotalOrder, ExactlyOnce>(&external);
3591
3592 let out = sliced! {
3593 let input = use::batch(input, nondet!());
3594 let v = use::snapshot(node.source_iter(q!(vec![1, 2, 3])).reduce(q!(|acc, v| *acc += v)).into_singleton(), nondet!());
3595 input.cross_singleton(v.into_stream().count())
3596 }
3597 .send_bincode_external(&external);
3598
3599 let nodes = flow
3600 .with_process(&node, deployment.Localhost())
3601 .with_external(&external, deployment.Localhost())
3602 .deploy(&mut deployment);
3603
3604 deployment.deploy().await.unwrap();
3605
3606 let mut external_in = nodes.connect(input_port).await;
3607 let mut external_out = nodes.connect(out).await;
3608
3609 deployment.start().await.unwrap();
3610
3611 external_in.send(1).await.unwrap();
3612 assert_eq!(external_out.next().await.unwrap(), (1, 1));
3613
3614 external_in.send(2).await.unwrap();
3615 assert_eq!(external_out.next().await.unwrap(), (2, 1));
3616 }
3617
3618 #[cfg(feature = "deploy")]
3619 #[tokio::test]
3620 async fn atomic_fold_replays_each_tick() {
3621 let mut deployment = Deployment::new();
3622
3623 let mut flow = FlowBuilder::new();
3624 let node = flow.process::<()>();
3625 let external = flow.external::<()>();
3626
3627 let (input_port, input) =
3628 node.source_external_bincode::<_, _, TotalOrder, ExactlyOnce>(&external);
3629 let tick = node.tick();
3630
3631 let out = input
3632 .batch(&tick, nondet!())
3633 .cross_singleton(
3634 node.source_iter(q!(vec![1, 2, 3]))
3635 .atomic()
3636 .fold(q!(|| 0), q!(|acc, v| *acc += v))
3637 .snapshot_atomic(&tick, nondet!()),
3638 )
3639 .all_ticks()
3640 .send_bincode_external(&external);
3641
3642 let nodes = flow
3643 .with_process(&node, deployment.Localhost())
3644 .with_external(&external, deployment.Localhost())
3645 .deploy(&mut deployment);
3646
3647 deployment.deploy().await.unwrap();
3648
3649 let mut external_in = nodes.connect(input_port).await;
3650 let mut external_out = nodes.connect(out).await;
3651
3652 deployment.start().await.unwrap();
3653
3654 external_in.send(1).await.unwrap();
3655 assert_eq!(external_out.next().await.unwrap(), (1, 6));
3656
3657 external_in.send(2).await.unwrap();
3658 assert_eq!(external_out.next().await.unwrap(), (2, 6));
3659 }
3660
3661 #[cfg(feature = "deploy")]
3662 #[tokio::test]
3663 async fn unbounded_scan_remembers_state() {
3664 let mut deployment = Deployment::new();
3665
3666 let mut flow = FlowBuilder::new();
3667 let node = flow.process::<()>();
3668 let external = flow.external::<()>();
3669
3670 let (input_port, input) = node.source_external_bincode(&external);
3671 let out = input
3672 .scan(
3673 q!(|| 0),
3674 q!(|acc, v| {
3675 *acc += v;
3676 Some(*acc)
3677 }),
3678 )
3679 .send_bincode_external(&external);
3680
3681 let nodes = flow
3682 .with_process(&node, deployment.Localhost())
3683 .with_external(&external, deployment.Localhost())
3684 .deploy(&mut deployment);
3685
3686 deployment.deploy().await.unwrap();
3687
3688 let mut external_in = nodes.connect(input_port).await;
3689 let mut external_out = nodes.connect(out).await;
3690
3691 deployment.start().await.unwrap();
3692
3693 external_in.send(1).await.unwrap();
3694 assert_eq!(external_out.next().await.unwrap(), 1);
3695
3696 external_in.send(2).await.unwrap();
3697 assert_eq!(external_out.next().await.unwrap(), 3);
3698 }
3699
3700 #[cfg(feature = "deploy")]
3701 #[tokio::test]
3702 async fn unbounded_enumerate_remembers_state() {
3703 let mut deployment = Deployment::new();
3704
3705 let mut flow = FlowBuilder::new();
3706 let node = flow.process::<()>();
3707 let external = flow.external::<()>();
3708
3709 let (input_port, input) = node.source_external_bincode(&external);
3710 let out = input.enumerate().send_bincode_external(&external);
3711
3712 let nodes = flow
3713 .with_process(&node, deployment.Localhost())
3714 .with_external(&external, deployment.Localhost())
3715 .deploy(&mut deployment);
3716
3717 deployment.deploy().await.unwrap();
3718
3719 let mut external_in = nodes.connect(input_port).await;
3720 let mut external_out = nodes.connect(out).await;
3721
3722 deployment.start().await.unwrap();
3723
3724 external_in.send(1).await.unwrap();
3725 assert_eq!(external_out.next().await.unwrap(), (0, 1));
3726
3727 external_in.send(2).await.unwrap();
3728 assert_eq!(external_out.next().await.unwrap(), (1, 2));
3729 }
3730
3731 #[cfg(feature = "deploy")]
3732 #[tokio::test]
3733 async fn unbounded_unique_remembers_state() {
3734 let mut deployment = Deployment::new();
3735
3736 let mut flow = FlowBuilder::new();
3737 let node = flow.process::<()>();
3738 let external = flow.external::<()>();
3739
3740 let (input_port, input) =
3741 node.source_external_bincode::<_, _, TotalOrder, ExactlyOnce>(&external);
3742 let out = input.unique().send_bincode_external(&external);
3743
3744 let nodes = flow
3745 .with_process(&node, deployment.Localhost())
3746 .with_external(&external, deployment.Localhost())
3747 .deploy(&mut deployment);
3748
3749 deployment.deploy().await.unwrap();
3750
3751 let mut external_in = nodes.connect(input_port).await;
3752 let mut external_out = nodes.connect(out).await;
3753
3754 deployment.start().await.unwrap();
3755
3756 external_in.send(1).await.unwrap();
3757 assert_eq!(external_out.next().await.unwrap(), 1);
3758
3759 external_in.send(2).await.unwrap();
3760 assert_eq!(external_out.next().await.unwrap(), 2);
3761
3762 external_in.send(1).await.unwrap();
3763 external_in.send(3).await.unwrap();
3764 assert_eq!(external_out.next().await.unwrap(), 3);
3765 }
3766
3767 #[cfg(feature = "sim")]
3768 #[test]
3769 #[should_panic]
3770 fn sim_batch_nondet_size() {
3771 let mut flow = FlowBuilder::new();
3772 let node = flow.process::<()>();
3773
3774 let (in_send, input) = node.sim_input::<_, TotalOrder, _>();
3775
3776 let tick = node.tick();
3777 let out_recv = input
3778 .batch(&tick, nondet!())
3779 .count()
3780 .all_ticks()
3781 .sim_output();
3782
3783 flow.sim().exhaustive(async || {
3784 in_send.send(());
3785 in_send.send(());
3786 in_send.send(());
3787
3788 assert_eq!(out_recv.next().await, 3); });
3790 }
3791
3792 #[cfg(feature = "sim")]
3793 #[test]
3794 fn sim_batch_preserves_order() {
3795 let mut flow = FlowBuilder::new();
3796 let node = flow.process::<()>();
3797
3798 let (in_send, input) = node.sim_input();
3799
3800 let tick = node.tick();
3801 let out_recv = input
3802 .batch(&tick, nondet!())
3803 .all_ticks()
3804 .sim_output();
3805
3806 flow.sim().exhaustive(async || {
3807 in_send.send(1);
3808 in_send.send(2);
3809 in_send.send(3);
3810
3811 out_recv.assert_yields_only([1, 2, 3]).await;
3812 });
3813 }
3814
3815 #[cfg(feature = "sim")]
3816 #[test]
3817 #[should_panic]
3818 fn sim_batch_unordered_shuffles() {
3819 let mut flow = FlowBuilder::new();
3820 let node = flow.process::<()>();
3821
3822 let (in_send, input) = node.sim_input::<_, NoOrder, _>();
3823
3824 let tick = node.tick();
3825 let batch = input.batch(&tick, nondet!());
3826 let out_recv = batch
3827 .clone()
3828 .min()
3829 .zip(batch.max())
3830 .all_ticks()
3831 .sim_output();
3832
3833 flow.sim().exhaustive(async || {
3834 in_send.send_many_unordered([1, 2, 3]);
3835
3836 if out_recv.collect::<Vec<_>>().await == vec![(1, 3), (2, 2)] {
3837 panic!("saw both (1, 3) and (2, 2), so batching must have shuffled the order");
3838 }
3839 });
3840 }
3841
3842 #[cfg(feature = "sim")]
3843 #[test]
3844 fn sim_batch_unordered_shuffles_count() {
3845 let mut flow = FlowBuilder::new();
3846 let node = flow.process::<()>();
3847
3848 let (in_send, input) = node.sim_input::<_, NoOrder, _>();
3849
3850 let tick = node.tick();
3851 let batch = input.batch(&tick, nondet!());
3852 let out_recv = batch.all_ticks().sim_output();
3853
3854 let instance_count = flow.sim().exhaustive(async || {
3855 in_send.send_many_unordered([1, 2, 3, 4]);
3856 out_recv.assert_yields_only_unordered([1, 2, 3, 4]).await;
3857 });
3858
3859 assert_eq!(
3860 instance_count,
3861 75 )
3863 }
3864
3865 #[cfg(feature = "sim")]
3866 #[test]
3867 #[should_panic]
3868 fn sim_observe_order_batched() {
3869 let mut flow = FlowBuilder::new();
3870 let node = flow.process::<()>();
3871
3872 let (in_send, input) = node.sim_input::<_, NoOrder, _>();
3873
3874 let tick = node.tick();
3875 let batch = input.batch(&tick, nondet!());
3876 let out_recv = batch
3877 .assume_ordering::<TotalOrder>(nondet!())
3878 .all_ticks()
3879 .sim_output();
3880
3881 flow.sim().exhaustive(async || {
3882 in_send.send_many_unordered([1, 2, 3, 4]);
3883 out_recv.assert_yields_only([1, 2, 3, 4]).await; });
3885 }
3886
3887 #[cfg(feature = "sim")]
3888 #[test]
3889 fn sim_observe_order_batched_count() {
3890 let mut flow = FlowBuilder::new();
3891 let node = flow.process::<()>();
3892
3893 let (in_send, input) = node.sim_input::<_, NoOrder, _>();
3894
3895 let tick = node.tick();
3896 let batch = input.batch(&tick, nondet!());
3897 let out_recv = batch
3898 .assume_ordering::<TotalOrder>(nondet!())
3899 .all_ticks()
3900 .sim_output();
3901
3902 let instance_count = flow.sim().exhaustive(async || {
3903 in_send.send_many_unordered([1, 2, 3, 4]);
3904 let _ = out_recv.collect::<Vec<_>>().await;
3905 });
3906
3907 assert_eq!(
3908 instance_count,
3909 192 )
3911 }
3912
3913 #[cfg(feature = "sim")]
3914 #[test]
3915 fn sim_unordered_count_instance_count() {
3916 let mut flow = FlowBuilder::new();
3917 let node = flow.process::<()>();
3918
3919 let (in_send, input) = node.sim_input::<_, NoOrder, _>();
3920
3921 let tick = node.tick();
3922 let out_recv = input
3923 .count()
3924 .snapshot(&tick, nondet!())
3925 .all_ticks()
3926 .sim_output();
3927
3928 let instance_count = flow.sim().exhaustive(async || {
3929 in_send.send_many_unordered([1, 2, 3, 4]);
3930 assert!(out_recv.collect::<Vec<_>>().await.last().unwrap() == &4);
3931 });
3932
3933 assert_eq!(
3934 instance_count,
3935 16 )
3937 }
3938
3939 #[cfg(feature = "sim")]
3940 #[test]
3941 fn sim_top_level_assume_ordering() {
3942 let mut flow = FlowBuilder::new();
3943 let node = flow.process::<()>();
3944
3945 let (in_send, input) = node.sim_input::<_, NoOrder, _>();
3946
3947 let out_recv = input
3948 .assume_ordering::<TotalOrder>(nondet!())
3949 .sim_output();
3950
3951 let instance_count = flow.sim().exhaustive(async || {
3952 in_send.send_many_unordered([1, 2, 3]);
3953 let mut out = out_recv.collect::<Vec<_>>().await;
3954 out.sort();
3955 assert_eq!(out, vec![1, 2, 3]);
3956 });
3957
3958 assert_eq!(instance_count, 6)
3959 }
3960
3961 #[cfg(feature = "sim")]
3962 #[test]
3963 fn sim_top_level_assume_ordering_cycle_back() {
3964 let mut flow = FlowBuilder::new();
3965 let node = flow.process::<()>();
3966 let node2 = flow.process::<()>();
3967
3968 let (in_send, input) = node.sim_input::<_, NoOrder, _>();
3969
3970 let (complete_cycle_back, cycle_back) =
3971 node.forward_ref::<super::Stream<_, _, _, NoOrder>>();
3972 let ordered = input
3973 .merge_unordered(cycle_back)
3974 .assume_ordering::<TotalOrder>(nondet!());
3975 complete_cycle_back.complete(
3976 ordered
3977 .clone()
3978 .map(q!(|v| v + 1))
3979 .filter(q!(|v| v % 2 == 1))
3980 .send(&node2, TCP.fail_stop().bincode())
3981 .send(&node, TCP.fail_stop().bincode()),
3982 );
3983
3984 let out_recv = ordered.sim_output();
3985
3986 let mut saw = false;
3987 let instance_count = flow.sim().exhaustive(async || {
3988 in_send.send_many_unordered([0, 2]);
3989 let out = out_recv.collect::<Vec<_>>().await;
3990
3991 if out.starts_with(&[0, 1, 2]) {
3992 saw = true;
3993 }
3994 });
3995
3996 assert!(saw, "did not see an instance with 0, 1, 2 in order");
3997 assert_eq!(instance_count, 6);
3998 }
3999
4000 #[cfg(feature = "sim")]
4001 #[test]
4002 fn sim_top_level_assume_ordering_cycle_back_tick() {
4003 let mut flow = FlowBuilder::new();
4004 let node = flow.process::<()>();
4005 let node2 = flow.process::<()>();
4006
4007 let (in_send, input) = node.sim_input::<_, NoOrder, _>();
4008
4009 let (complete_cycle_back, cycle_back) =
4010 node.forward_ref::<super::Stream<_, _, _, NoOrder>>();
4011 let ordered = input
4012 .merge_unordered(cycle_back)
4013 .assume_ordering::<TotalOrder>(nondet!());
4014 complete_cycle_back.complete(
4015 ordered
4016 .clone()
4017 .batch(&node.tick(), nondet!())
4018 .all_ticks()
4019 .map(q!(|v| v + 1))
4020 .filter(q!(|v| v % 2 == 1))
4021 .send(&node2, TCP.fail_stop().bincode())
4022 .send(&node, TCP.fail_stop().bincode()),
4023 );
4024
4025 let out_recv = ordered.sim_output();
4026
4027 let mut saw = false;
4028 let instance_count = flow.sim().exhaustive(async || {
4029 in_send.send_many_unordered([0, 2]);
4030 let out = out_recv.collect::<Vec<_>>().await;
4031
4032 if out.starts_with(&[0, 1, 2]) {
4033 saw = true;
4034 }
4035 });
4036
4037 assert!(saw, "did not see an instance with 0, 1, 2 in order");
4038 assert_eq!(instance_count, 58);
4039 }
4040
4041 #[cfg(feature = "sim")]
4042 #[test]
4043 fn sim_top_level_assume_ordering_multiple() {
4044 let mut flow = FlowBuilder::new();
4045 let node = flow.process::<()>();
4046 let node2 = flow.process::<()>();
4047
4048 let (in_send, input) = node.sim_input::<_, NoOrder, _>();
4049 let (_, input2) = node.sim_input::<_, NoOrder, _>();
4050
4051 let (complete_cycle_back, cycle_back) =
4052 node.forward_ref::<super::Stream<_, _, _, NoOrder>>();
4053 let input1_ordered = input
4054 .clone()
4055 .merge_unordered(cycle_back)
4056 .assume_ordering::<TotalOrder>(nondet!());
4057 let foo = input1_ordered
4058 .clone()
4059 .map(q!(|v| v + 3))
4060 .weaken_ordering::<NoOrder>()
4061 .merge_unordered(input2)
4062 .assume_ordering::<TotalOrder>(nondet!());
4063
4064 complete_cycle_back.complete(
4065 foo.filter(q!(|v| *v == 3))
4066 .send(&node2, TCP.fail_stop().bincode())
4067 .send(&node, TCP.fail_stop().bincode()),
4068 );
4069
4070 let out_recv = input1_ordered.sim_output();
4071
4072 let mut saw = false;
4073 let instance_count = flow.sim().exhaustive(async || {
4074 in_send.send_many_unordered([0, 1]);
4075 let out = out_recv.collect::<Vec<_>>().await;
4076
4077 if out.starts_with(&[0, 3, 1]) {
4078 saw = true;
4079 }
4080 });
4081
4082 assert!(saw, "did not see an instance with 0, 3, 1 in order");
4083 assert_eq!(instance_count, 24);
4084 }
4085
4086 #[cfg(feature = "sim")]
4087 #[test]
4088 fn sim_atomic_assume_ordering_cycle_back() {
4089 let mut flow = FlowBuilder::new();
4090 let node = flow.process::<()>();
4091 let node2 = flow.process::<()>();
4092
4093 let (in_send, input) = node.sim_input::<_, NoOrder, _>();
4094
4095 let (complete_cycle_back, cycle_back) =
4096 node.forward_ref::<super::Stream<_, _, _, NoOrder>>();
4097 let ordered = input
4098 .merge_unordered(cycle_back)
4099 .atomic()
4100 .assume_ordering::<TotalOrder>(nondet!())
4101 .end_atomic();
4102 complete_cycle_back.complete(
4103 ordered
4104 .clone()
4105 .map(q!(|v| v + 1))
4106 .filter(q!(|v| v % 2 == 1))
4107 .send(&node2, TCP.fail_stop().bincode())
4108 .send(&node, TCP.fail_stop().bincode()),
4109 );
4110
4111 let out_recv = ordered.sim_output();
4112
4113 let instance_count = flow.sim().exhaustive(async || {
4114 in_send.send_many_unordered([0, 2]);
4115 let out = out_recv.collect::<Vec<_>>().await;
4116 assert_eq!(out.len(), 4);
4117 });
4118 assert_eq!(instance_count, 22);
4119 }
4120
4121 #[cfg(feature = "deploy")]
4122 #[tokio::test]
4123 async fn partition_evens_odds() {
4124 let mut deployment = Deployment::new();
4125
4126 let mut flow = FlowBuilder::new();
4127 let node = flow.process::<()>();
4128 let external = flow.external::<()>();
4129
4130 let numbers = node.source_iter(q!(vec![1i32, 2, 3, 4, 5, 6]));
4131 let (evens, odds) = numbers.partition(q!(|x: &i32| x % 2 == 0));
4132 let evens_port = evens.send_bincode_external(&external);
4133 let odds_port = odds.send_bincode_external(&external);
4134
4135 let nodes = flow
4136 .with_process(&node, deployment.Localhost())
4137 .with_external(&external, deployment.Localhost())
4138 .deploy(&mut deployment);
4139
4140 deployment.deploy().await.unwrap();
4141
4142 let mut evens_out = nodes.connect(evens_port).await;
4143 let mut odds_out = nodes.connect(odds_port).await;
4144
4145 deployment.start().await.unwrap();
4146
4147 let mut even_results = Vec::new();
4148 for _ in 0..3 {
4149 even_results.push(evens_out.next().await.unwrap());
4150 }
4151 even_results.sort();
4152 assert_eq!(even_results, vec![2, 4, 6]);
4153
4154 let mut odd_results = Vec::new();
4155 for _ in 0..3 {
4156 odd_results.push(odds_out.next().await.unwrap());
4157 }
4158 odd_results.sort();
4159 assert_eq!(odd_results, vec![1, 3, 5]);
4160 }
4161
4162 #[cfg(feature = "deploy")]
4163 #[tokio::test]
4164 async fn unconsumed_inspect_still_runs() {
4165 use crate::deploy::DeployCrateWrapper;
4166
4167 let mut deployment = Deployment::new();
4168
4169 let mut flow = FlowBuilder::new();
4170 let node = flow.process::<()>();
4171
4172 node.source_iter(q!(0..5))
4175 .inspect(q!(|x| println!("inspect: {}", x)));
4176
4177 let nodes = flow
4178 .with_process(&node, deployment.Localhost())
4179 .deploy(&mut deployment);
4180
4181 deployment.deploy().await.unwrap();
4182
4183 let mut stdout = nodes.get_process(&node).stdout();
4184
4185 deployment.start().await.unwrap();
4186
4187 let mut lines = Vec::new();
4188 for _ in 0..5 {
4189 lines.push(stdout.recv().await.unwrap());
4190 }
4191 lines.sort();
4192 assert_eq!(
4193 lines,
4194 vec![
4195 "inspect: 0",
4196 "inspect: 1",
4197 "inspect: 2",
4198 "inspect: 3",
4199 "inspect: 4",
4200 ]
4201 );
4202 }
4203
4204 #[cfg(feature = "deploy")]
4205 #[tokio::test]
4206 async fn unconsumed_inspect_alive_at_deploy_still_runs() {
4207 use crate::deploy::DeployCrateWrapper;
4208
4209 let mut deployment = Deployment::new();
4210
4211 let mut flow = FlowBuilder::new();
4212 let node = flow.process::<()>();
4213
4214 let _inspected = node
4219 .source_iter(q!(0..5))
4220 .inspect(q!(|x| println!("inspect: {}", x)));
4221
4222 let nodes = flow
4223 .with_process(&node, deployment.Localhost())
4224 .deploy(&mut deployment);
4225
4226 deployment.deploy().await.unwrap();
4227
4228 let mut stdout = nodes.get_process(&node).stdout();
4229
4230 deployment.start().await.unwrap();
4231
4232 let mut lines = Vec::new();
4233 for _ in 0..5 {
4234 lines.push(stdout.recv().await.unwrap());
4235 }
4236 lines.sort();
4237 assert_eq!(
4238 lines,
4239 vec![
4240 "inspect: 0",
4241 "inspect: 1",
4242 "inspect: 2",
4243 "inspect: 3",
4244 "inspect: 4",
4245 ]
4246 );
4247 }
4248
4249 #[cfg(feature = "sim")]
4250 #[test]
4251 fn sim_limit() {
4252 let mut flow = FlowBuilder::new();
4253 let node = flow.process::<()>();
4254
4255 let (in_send, input) = node.sim_input();
4256
4257 let out_recv = input.limit(q!(3)).sim_output();
4258
4259 flow.sim().exhaustive(async || {
4260 in_send.send(1);
4261 in_send.send(2);
4262 in_send.send(3);
4263 in_send.send(4);
4264 in_send.send(5);
4265
4266 out_recv.assert_yields_only([1, 2, 3]).await;
4267 });
4268 }
4269
4270 #[cfg(feature = "sim")]
4271 #[test]
4272 fn sim_limit_zero() {
4273 let mut flow = FlowBuilder::new();
4274 let node = flow.process::<()>();
4275
4276 let (in_send, input) = node.sim_input();
4277
4278 let out_recv = input.limit(q!(0)).sim_output();
4279
4280 flow.sim().exhaustive(async || {
4281 in_send.send(1);
4282 in_send.send(2);
4283
4284 out_recv.assert_yields_only::<i32, _>([]).await;
4285 });
4286 }
4287
4288 #[cfg(feature = "sim")]
4289 #[test]
4290 fn sim_merge_ordered() {
4291 let mut flow = FlowBuilder::new();
4292 let node = flow.process::<()>();
4293
4294 let (in_send, input) = node.sim_input();
4295 let (in_send2, input2) = node.sim_input();
4296
4297 let out_recv = input
4298 .merge_ordered(input2, nondet!())
4299 .sim_output();
4300
4301 let mut saw_out_of_order = false;
4302 let instances = flow.sim().exhaustive(async || {
4303 in_send.send(1);
4304 in_send.send(2);
4305 in_send2.send(3);
4306 in_send2.send(4);
4307
4308 let out = out_recv.collect::<Vec<_>>().await;
4309
4310 if out == [1, 3, 2, 4] {
4311 saw_out_of_order = true;
4312 }
4313
4314 let mut first_elements = out.iter().filter(|v| **v <= 2).copied().collect::<Vec<_>>();
4317 let mut second_elements = out.iter().filter(|v| **v > 2).copied().collect::<Vec<_>>();
4318 assert_eq!(
4319 first_elements,
4320 vec![1, 2],
4321 "first input order violated: {:?}",
4322 out
4323 );
4324 assert_eq!(
4325 second_elements,
4326 vec![3, 4],
4327 "second input order violated: {:?}",
4328 out
4329 );
4330
4331 first_elements.append(&mut second_elements);
4332 first_elements.sort();
4333 assert_eq!(first_elements, vec![1, 2, 3, 4]);
4334 });
4335
4336 assert!(saw_out_of_order);
4337 assert_eq!(instances, 6);
4338 }
4339
4340 #[cfg(feature = "sim")]
4343 #[test]
4344 fn sim_merge_ordered_one_empty() {
4345 let mut flow = FlowBuilder::new();
4346 let node = flow.process::<()>();
4347
4348 let (in_send, input) = node.sim_input();
4349 let (_in_send2, input2) = node.sim_input();
4350
4351 let out_recv = input
4352 .merge_ordered(input2, nondet!())
4353 .sim_output();
4354
4355 let instances = flow.sim().exhaustive(async || {
4356 in_send.send(1);
4357 in_send.send(2);
4358
4359 let out = out_recv.collect::<Vec<_>>().await;
4360 assert_eq!(out, vec![1, 2]);
4361 });
4362
4363 assert_eq!(instances, 1);
4365 }
4366
4367 #[cfg(feature = "sim")]
4373 #[test]
4374 fn sim_merge_ordered_cycle_back() {
4375 let mut flow = FlowBuilder::new();
4376 let node = flow.process::<()>();
4377
4378 let (in_send, input) = node.sim_input();
4379
4380 let (complete_cycle_back, cycle_back) =
4382 node.forward_ref::<super::Stream<_, _, _, TotalOrder>>();
4383
4384 let merged = input.merge_ordered(cycle_back, nondet!());
4386
4387 complete_cycle_back.complete(merged.clone().filter(q!(|v| *v == 1)).map(q!(|v| v * 10)));
4389
4390 let out_recv = merged.sim_output();
4391
4392 let mut saw_cycle_before_second = false;
4395 flow.sim().exhaustive(async || {
4396 in_send.send(1);
4397 in_send.send(2);
4398
4399 let out = out_recv.collect::<Vec<_>>().await;
4400
4401 let pos_1 = out.iter().position(|v| *v == 1).unwrap();
4403 let pos_10 = out.iter().position(|v| *v == 10).unwrap();
4404 assert!(pos_1 < pos_10, "causal order violated: {:?}", out);
4405
4406 if out == [1, 10, 2] {
4408 saw_cycle_before_second = true;
4409 }
4410
4411 let mut sorted = out;
4412 sorted.sort();
4413 assert_eq!(sorted, vec![1, 2, 10]);
4414 });
4415
4416 assert!(
4417 saw_cycle_before_second,
4418 "never saw the cycled element arrive before the second input element"
4419 );
4420 }
4421
4422 #[cfg(feature = "sim")]
4426 #[test]
4427 fn sim_merge_ordered_delayed() {
4428 let mut flow = FlowBuilder::new();
4429 let node = flow.process::<()>();
4430
4431 let (in_send, input) = node.sim_input();
4432 let (in_send2, input2) = node.sim_input();
4433
4434 let out_recv = input
4435 .merge_ordered(input2, nondet!())
4436 .sim_output();
4437
4438 let mut saw_delayed_interleaving = false;
4439 flow.sim().exhaustive(async || {
4440 in_send.send(1);
4442 in_send2.send(3);
4443 in_send2.send(4);
4444
4445 let first_batch = out_recv.collect::<Vec<_>>().await;
4447
4448 in_send.send(2);
4450 let second_batch = out_recv.collect::<Vec<_>>().await;
4451
4452 let mut all: Vec<_> = first_batch
4453 .iter()
4454 .chain(second_batch.iter())
4455 .copied()
4456 .collect();
4457
4458 if all == [1, 3, 4, 2] {
4460 saw_delayed_interleaving = true;
4461 }
4462
4463 all.sort();
4464 assert_eq!(all, vec![1, 2, 3, 4]);
4465 });
4466
4467 assert!(saw_delayed_interleaving);
4468 }
4469
4470 #[cfg(feature = "deploy")]
4475 #[tokio::test]
4476 async fn deploy_merge_ordered_delayed() {
4477 let mut deployment = Deployment::new();
4478
4479 let mut flow = FlowBuilder::new();
4480 let node = flow.process::<()>();
4481 let external = flow.external::<()>();
4482
4483 let (input_a_port, input_a) = node.source_external_bincode(&external);
4484 let (input_b_port, input_b) = node.source_external_bincode(&external);
4485
4486 let out = input_a
4487 .assume_ordering(nondet!())
4488 .merge_ordered(
4489 input_b.assume_ordering(nondet!()),
4490 nondet!(),
4491 )
4492 .send_bincode_external(&external);
4493
4494 let nodes = flow
4495 .with_process(&node, deployment.Localhost())
4496 .with_external(&external, deployment.Localhost())
4497 .deploy(&mut deployment);
4498
4499 deployment.deploy().await.unwrap();
4500
4501 let mut ext_a = nodes.connect(input_a_port).await;
4502 let mut ext_b = nodes.connect(input_b_port).await;
4503 let mut ext_out = nodes.connect(out).await;
4504
4505 deployment.start().await.unwrap();
4506
4507 ext_a.send(1).await.unwrap();
4509 ext_b.send(3).await.unwrap();
4510 ext_b.send(4).await.unwrap();
4511
4512 let mut received = Vec::new();
4514 for _ in 0..3 {
4515 received.push(ext_out.next().await.unwrap());
4516 }
4517
4518 ext_a.send(2).await.unwrap();
4520 received.push(ext_out.next().await.unwrap());
4521
4522 received.sort();
4524 assert_eq!(received, vec![1, 2, 3, 4]);
4525 }
4526
4527 #[cfg(feature = "deploy")]
4528 #[tokio::test]
4529 async fn monotone_fold_threshold() {
4530 use crate::properties::manual_proof;
4531
4532 let mut deployment = Deployment::new();
4533
4534 let mut flow = FlowBuilder::new();
4535 let node = flow.process::<()>();
4536 let external = flow.external::<()>();
4537
4538 let in_unbounded: super::Stream<_, _> =
4539 node.source_iter(q!(vec![1i32, 2, 3, 4, 5, 6])).into();
4540 let sum = in_unbounded.fold(
4541 q!(|| 0),
4542 q!(
4543 |sum, v| {
4544 *sum += v;
4545 },
4546 monotone = manual_proof!()
4547 ),
4548 );
4549
4550 let threshold_out = sum
4551 .threshold_greater_or_equal(node.singleton(q!(7)))
4552 .send_bincode_external(&external);
4553
4554 let nodes = flow
4555 .with_process(&node, deployment.Localhost())
4556 .with_external(&external, deployment.Localhost())
4557 .deploy(&mut deployment);
4558
4559 deployment.deploy().await.unwrap();
4560
4561 let mut threshold_out = nodes.connect(threshold_out).await;
4562
4563 deployment.start().await.unwrap();
4564
4565 assert_eq!(threshold_out.next().await.unwrap(), 7);
4566 }
4567
4568 #[cfg(feature = "deploy")]
4569 #[tokio::test]
4570 async fn monotone_count_threshold() {
4571 let mut deployment = Deployment::new();
4572
4573 let mut flow = FlowBuilder::new();
4574 let node = flow.process::<()>();
4575 let external = flow.external::<()>();
4576
4577 let in_unbounded: super::Stream<_, _> =
4578 node.source_iter(q!(vec![1i32, 2, 3, 4, 5, 6])).into();
4579 let sum = in_unbounded.count();
4580
4581 let threshold_out = sum
4582 .threshold_greater_or_equal(node.singleton(q!(3)))
4583 .send_bincode_external(&external);
4584
4585 let nodes = flow
4586 .with_process(&node, deployment.Localhost())
4587 .with_external(&external, deployment.Localhost())
4588 .deploy(&mut deployment);
4589
4590 deployment.deploy().await.unwrap();
4591
4592 let mut threshold_out = nodes.connect(threshold_out).await;
4593
4594 deployment.start().await.unwrap();
4595
4596 assert_eq!(threshold_out.next().await.unwrap(), 3);
4597 }
4598
4599 #[cfg(feature = "deploy")]
4600 #[tokio::test]
4601 async fn monotone_map_order_preserving_threshold() {
4602 use crate::properties::manual_proof;
4603
4604 let mut deployment = Deployment::new();
4605
4606 let mut flow = FlowBuilder::new();
4607 let node = flow.process::<()>();
4608 let external = flow.external::<()>();
4609
4610 let in_unbounded: super::Stream<_, _> =
4611 node.source_iter(q!(vec![1i32, 2, 3, 4, 5, 6])).into();
4612 let sum = in_unbounded.fold(
4613 q!(|| 0),
4614 q!(
4615 |sum, v| {
4616 *sum += v;
4617 },
4618 monotone = manual_proof!()
4619 ),
4620 );
4621
4622 let doubled = sum.map(q!(
4624 |v| v * 2,
4625 order_preserving = manual_proof!()
4626 ));
4627
4628 let threshold_out = doubled
4629 .threshold_greater_or_equal(node.singleton(q!(14)))
4630 .send_bincode_external(&external);
4631
4632 let nodes = flow
4633 .with_process(&node, deployment.Localhost())
4634 .with_external(&external, deployment.Localhost())
4635 .deploy(&mut deployment);
4636
4637 deployment.deploy().await.unwrap();
4638
4639 let mut threshold_out = nodes.connect(threshold_out).await;
4640
4641 deployment.start().await.unwrap();
4642
4643 assert_eq!(threshold_out.next().await.unwrap(), 14);
4644 }
4645
4646 #[cfg(any(feature = "deploy", feature = "sim"))]
4649 mod join_ordering_type_tests {
4650 use crate::live_collections::boundedness::{Bounded, Unbounded};
4651 use crate::live_collections::stream::{ExactlyOnce, NoOrder, Stream, TotalOrder};
4652 use crate::location::{Location, Process};
4653
4654 #[expect(dead_code, reason = "compile-time type test")]
4655 fn join_unbounded_with_bounded_preserves_order<'a>(
4656 left: Stream<(i32, char), Process<'a>, Unbounded, TotalOrder, ExactlyOnce>,
4657 right: Stream<(i32, char), Process<'a>, Bounded, TotalOrder, ExactlyOnce>,
4658 ) -> Stream<(i32, (char, char)), Process<'a>, Unbounded, TotalOrder, ExactlyOnce> {
4659 left.join(right)
4660 }
4661
4662 #[expect(dead_code, reason = "compile-time type test")]
4663 fn join_unbounded_with_unbounded_is_no_order<'a>(
4664 left: Stream<(i32, char), Process<'a>, Unbounded, TotalOrder, ExactlyOnce>,
4665 right: Stream<(i32, char), Process<'a>, Unbounded, TotalOrder, ExactlyOnce>,
4666 ) -> Stream<(i32, (char, char)), Process<'a>, Unbounded, NoOrder, ExactlyOnce> {
4667 left.join(right)
4668 }
4669
4670 #[expect(dead_code, reason = "compile-time type test")]
4671 fn join_bounded_with_bounded_preserves_order<'a, L: Location<'a>>(
4672 left: Stream<(i32, char), L, Bounded, TotalOrder, ExactlyOnce>,
4673 right: Stream<(i32, char), L, Bounded, TotalOrder, ExactlyOnce>,
4674 ) -> Stream<(i32, (char, char)), L, Bounded, TotalOrder, ExactlyOnce> {
4675 left.join(right)
4676 }
4677
4678 #[expect(dead_code, reason = "compile-time type test")]
4679 fn join_unbounded_noorder_with_bounded<'a>(
4680 left: Stream<(i32, char), Process<'a>, Unbounded, NoOrder, ExactlyOnce>,
4681 right: Stream<(i32, char), Process<'a>, Bounded, NoOrder, ExactlyOnce>,
4682 ) -> Stream<(i32, (char, char)), Process<'a>, Unbounded, NoOrder, ExactlyOnce> {
4683 left.join(right)
4684 }
4685
4686 #[expect(dead_code, reason = "compile-time type test")]
4689 fn cross_product_unbounded_with_bounded_preserves_order<'a>(
4690 left: Stream<i32, Process<'a>, Unbounded, TotalOrder, ExactlyOnce>,
4691 right: Stream<char, Process<'a>, Bounded, TotalOrder, ExactlyOnce>,
4692 ) -> Stream<(i32, char), Process<'a>, Unbounded, TotalOrder, ExactlyOnce> {
4693 left.cross_product(right)
4694 }
4695
4696 #[expect(dead_code, reason = "compile-time type test")]
4697 fn cross_product_bounded_with_bounded_preserves_order<'a>(
4698 left: Stream<i32, Process<'a>, Bounded, TotalOrder, ExactlyOnce>,
4699 right: Stream<char, Process<'a>, Bounded, TotalOrder, ExactlyOnce>,
4700 ) -> Stream<(i32, char), Process<'a>, Bounded, TotalOrder, ExactlyOnce> {
4701 left.cross_product(right)
4702 }
4703
4704 #[expect(dead_code, reason = "compile-time type test")]
4705 fn cross_product_unbounded_with_unbounded_is_no_order<'a>(
4706 left: Stream<i32, Process<'a>, Unbounded, TotalOrder, ExactlyOnce>,
4707 right: Stream<char, Process<'a>, Unbounded, TotalOrder, ExactlyOnce>,
4708 ) -> Stream<(i32, char), Process<'a>, Unbounded, NoOrder, ExactlyOnce> {
4709 left.cross_product(right)
4710 }
4711 } #[cfg(feature = "sim")]
4716 #[test]
4717 fn cross_product_mixed_boundedness_correctness() {
4718 use stageleft::q;
4719
4720 use crate::compile::builder::FlowBuilder;
4721 use crate::nondet::nondet;
4722
4723 let mut flow = FlowBuilder::new();
4724 let process = flow.process::<()>();
4725 let tick = process.tick();
4726
4727 let left = process.source_iter(q!(vec![1, 2]));
4728 let right = process
4729 .source_iter(q!(vec!['a', 'b']))
4730 .batch(&tick, nondet!())
4731 .all_ticks();
4732
4733 let out = left.cross_product(right).sim_output();
4734
4735 flow.sim().exhaustive(async || {
4736 out.assert_yields_only_unordered(vec![(1, 'a'), (1, 'b'), (2, 'a'), (2, 'b')])
4737 .await;
4738 });
4739 }
4740
4741 #[cfg(feature = "sim")]
4742 #[test]
4743 fn join_mixed_boundedness_correctness() {
4744 use stageleft::q;
4745
4746 use crate::compile::builder::FlowBuilder;
4747 use crate::nondet::nondet;
4748
4749 let mut flow = FlowBuilder::new();
4750 let process = flow.process::<()>();
4751 let tick = process.tick();
4752
4753 let left = process.source_iter(q!(vec![(1, 'a'), (2, 'b')]));
4754 let right = process
4755 .source_iter(q!(vec![(1, 'x'), (2, 'y')]))
4756 .batch(&tick, nondet!())
4757 .all_ticks();
4758
4759 let out = left.join(right).sim_output();
4760
4761 flow.sim().exhaustive(async || {
4762 out.assert_yields_only_unordered(vec![(1, ('a', 'x')), (2, ('b', 'y'))])
4763 .await;
4764 });
4765 }
4766
4767 #[cfg(feature = "sim")]
4768 #[test]
4769 fn sim_merge_unordered_independent_atomics() {
4770 let mut flow = FlowBuilder::new();
4771 let node = flow.process::<()>();
4772
4773 let (in1_send, input1) = node.sim_input::<_, TotalOrder, _>();
4774 let (in2_send, input2) = node.sim_input::<_, TotalOrder, _>();
4775
4776 let out = input1
4777 .atomic()
4778 .merge_unordered(input2.atomic())
4779 .end_atomic()
4780 .sim_output();
4781
4782 flow.sim().exhaustive(async || {
4783 in1_send.send(1);
4784 in2_send.send(2);
4785
4786 out.assert_yields_only_unordered(vec![1, 2]).await;
4787 });
4788 }
4789
4790 #[cfg(feature = "deploy")]
4791 #[tokio::test]
4792 async fn test_stream_ref() {
4793 let mut deployment = Deployment::new();
4794
4795 let mut flow = FlowBuilder::new();
4796 let external = flow.external::<()>();
4797 let p1 = flow.process::<()>();
4798
4799 let my_stream = p1.source_iter(q!(1..=5i32));
4801
4802 let stream_ref = my_stream.by_ref();
4803
4804 let out_port = p1
4806 .source_iter(q!([()]))
4807 .map(q!(|_| stream_ref.len() as i32))
4808 .send_bincode_external(&external);
4809
4810 my_stream.for_each(q!(|_| {}));
4812
4813 let nodes = flow
4814 .with_default_optimize()
4815 .with_process(&p1, deployment.Localhost())
4816 .with_external(&external, deployment.Localhost())
4817 .deploy(&mut deployment);
4818
4819 deployment.deploy().await.unwrap();
4820
4821 let mut out_recv = nodes.connect(out_port).await;
4822
4823 deployment.start().await.unwrap();
4824
4825 let result = out_recv.next().await.unwrap();
4826 assert_eq!(result, 5);
4828 }
4829
4830 #[cfg(feature = "deploy")]
4831 #[tokio::test]
4832 async fn test_stream_ref_contents() {
4833 let mut deployment = Deployment::new();
4834
4835 let mut flow = FlowBuilder::new();
4836 let external = flow.external::<()>();
4837 let p1 = flow.process::<()>();
4838
4839 let my_stream = p1.source_iter(q!(1..=3i32));
4841
4842 let stream_ref = my_stream.by_ref();
4843
4844 let out_port = p1
4846 .source_iter(q!([()]))
4847 .map(q!(|_| stream_ref.iter().sum::<i32>()))
4848 .send_bincode_external(&external);
4849
4850 my_stream.for_each(q!(|_| {}));
4851
4852 let nodes = flow
4853 .with_default_optimize()
4854 .with_process(&p1, deployment.Localhost())
4855 .with_external(&external, deployment.Localhost())
4856 .deploy(&mut deployment);
4857
4858 deployment.deploy().await.unwrap();
4859
4860 let mut out_recv = nodes.connect(out_port).await;
4861
4862 deployment.start().await.unwrap();
4863
4864 let result = out_recv.next().await.unwrap();
4865 assert_eq!(result, 6);
4867 }
4868
4869 #[cfg(feature = "deploy")]
4870 #[tokio::test]
4871 async fn test_stream_ref_no_consumer() {
4872 let mut deployment = Deployment::new();
4873
4874 let mut flow = FlowBuilder::new();
4875 let external = flow.external::<()>();
4876 let p1 = flow.process::<()>();
4877
4878 let my_stream = p1.source_iter(q!(1..=4i32));
4880
4881 let stream_ref = my_stream.by_ref();
4882
4883 let out_port = p1
4884 .source_iter(q!([()]))
4885 .map(q!(|_| stream_ref.len() as i32))
4886 .send_bincode_external(&external);
4887
4888 let nodes = flow
4889 .with_default_optimize()
4890 .with_process(&p1, deployment.Localhost())
4891 .with_external(&external, deployment.Localhost())
4892 .deploy(&mut deployment);
4893
4894 deployment.deploy().await.unwrap();
4895
4896 let mut out_recv = nodes.connect(out_port).await;
4897
4898 deployment.start().await.unwrap();
4899
4900 let result = out_recv.next().await.unwrap();
4901 assert_eq!(result, 4);
4902 }
4903
4904 #[cfg(feature = "deploy")]
4905 #[tokio::test]
4906 async fn test_stream_mut() {
4907 let mut deployment = Deployment::new();
4908
4909 let mut flow = FlowBuilder::new();
4910 let external = flow.external::<()>();
4911 let p1 = flow.process::<()>();
4912
4913 let my_stream = p1.source_iter(q!(1..=5i32));
4915
4916 let stream_mut = my_stream.by_mut();
4917
4918 let out_port = p1
4920 .source_iter(q!([()]))
4921 .map(q!(|_| {
4922 stream_mut.retain(|x| *x > 3);
4923 stream_mut.len() as i32
4924 }))
4925 .send_bincode_external(&external);
4926
4927 my_stream.for_each(q!(|_| {}));
4928
4929 let nodes = flow
4930 .with_default_optimize()
4931 .with_process(&p1, deployment.Localhost())
4932 .with_external(&external, deployment.Localhost())
4933 .deploy(&mut deployment);
4934
4935 deployment.deploy().await.unwrap();
4936
4937 let mut out_recv = nodes.connect(out_port).await;
4938
4939 deployment.start().await.unwrap();
4940
4941 let result = out_recv.next().await.unwrap();
4942 assert_eq!(result, 2);
4944 }
4945
4946 #[cfg(feature = "sim")]
4950 #[test]
4951 fn sim_map_with_mut_on_unordered_explores_multiple_states() {
4952 use crate::live_collections::sliced::sliced;
4953 use crate::live_collections::stream::ExactlyOnce;
4954 use crate::properties::manual_proof;
4955
4956 let mut flow = FlowBuilder::new();
4957 let node = flow.process::<()>();
4958
4959 let (trigger_send, trigger) = node.sim_input::<i32, TotalOrder, ExactlyOnce>();
4960
4961 let out_recv = sliced! {
4962 let batch = use::batch(trigger, nondet!());
4963 let counter = batch.location().source_iter(q!(vec![0i32]))
4964 .fold(q!(|| 0i32), q!(|acc, v| *acc += v));
4965 let counter_mut = counter.by_mut();
4966 let items = batch.location().source_iter(q!(vec![1i32, 2])).weaken_ordering::<NoOrder>();
4967 items.map(q!(
4968 |x| {
4969 *counter_mut += x;
4970 *counter_mut
4971 },
4972 commutative = manual_proof!()
4973 ))
4974 }
4975 .sim_output();
4976
4977 let count = flow.sim().exhaustive(async || {
4978 trigger_send.send(1);
4979 let _all: Vec<i32> = out_recv.collect_sorted().await;
4980 });
4981
4982 assert_eq!(
4983 count, 2,
4984 "Expected 2 simulation instances due to mut on unordered input, got {}",
4985 count
4986 );
4987 }
4988
4989 #[cfg(feature = "sim")]
4993 #[test]
4994 fn sim_scan_with_ref_capture() {
4995 use crate::live_collections::sliced::sliced;
4996 use crate::live_collections::stream::ExactlyOnce;
4997
4998 let mut flow = FlowBuilder::new();
4999 let node = flow.process::<()>();
5000
5001 let (trigger_send, trigger) = node.sim_input::<i32, TotalOrder, ExactlyOnce>();
5002
5003 let out_recv = sliced! {
5004 let batch = use::batch(trigger, nondet!());
5005 let offset = batch
5006 .location()
5007 .source_iter(q!(vec![10i32]))
5008 .fold(q!(|| 0i32), q!(|acc, v| *acc += v));
5009 let offset_ref = offset.by_ref();
5010 batch
5011 .location()
5012 .source_iter(q!(vec![1i32, 2, 3]))
5013 .scan(
5014 q!(|| 0i32),
5015 q!(move |acc: &mut i32, x| {
5016 *acc += x + *offset_ref;
5017 Some(*acc)
5018 }),
5019 )
5020 }
5021 .sim_output();
5022
5023 let count = flow.sim().exhaustive(async || {
5024 trigger_send.send(1);
5025 let all: Vec<i32> = out_recv.collect().await;
5026 assert_eq!(all, vec![11, 23, 36]);
5031 });
5032
5033 assert_eq!(
5034 count, 1,
5035 "Expected a single simulation instance for a totally-ordered scan, got {}",
5036 count
5037 );
5038 }
5039
5040 #[cfg(feature = "sim")]
5044 #[test]
5045 #[ignore = "observe_nondet not yet supported for top-level bounded inputs (https://github.com/hydro-project/hydro/issues/2950)"]
5046 fn sim_map_with_mut_on_unordered_top_level() {
5047 use crate::properties::manual_proof;
5048
5049 let mut flow = FlowBuilder::new();
5050 let node = flow.process::<()>();
5051
5052 let counter = node
5053 .source_iter(q!(vec![0i32]))
5054 .fold(q!(|| 0i32), q!(|acc, v| *acc += v));
5055 let counter_mut = counter.by_mut();
5056
5057 let out_recv = node
5058 .source_iter(q!(vec![1i32, 2]))
5059 .weaken_ordering::<NoOrder>()
5060 .map(q!(
5061 |x| {
5062 *counter_mut += x;
5063 *counter_mut
5064 },
5065 commutative = manual_proof!()
5066 ))
5067 .assume_ordering::<TotalOrder>(nondet!())
5068 .sim_output();
5069
5070 counter.into_stream().for_each(q!(|_| {}));
5071
5072 let count = flow.sim().exhaustive(async || {
5073 let _all: Vec<i32> = out_recv.collect().await;
5074 });
5075
5076 assert_eq!(
5077 count, 2,
5078 "Expected 2 simulation instances due to mut on unordered input, got {}",
5079 count
5080 );
5081 }
5082}