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 = SharedNode(Rc::new(RefCell::new(
918 self.ir_node.replace(HydroNode::Placeholder),
919 )));
920
921 let true_stream = Stream::new(
922 self.location.clone(),
923 HydroNode::Partition {
924 inner: SharedNode(shared.0.clone()),
925 f: f.clone(),
926 is_true: true,
927 metadata: self.location.new_node_metadata(Self::collection_kind()),
928 },
929 );
930
931 let false_stream = Stream::new(
932 self.location.clone(),
933 HydroNode::Partition {
934 inner: SharedNode(shared.0),
935 f,
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>>(
3304 self,
3305 thunk: impl FnOnce(Stream<T, Atomic<L>, Unbounded, O, R>) -> Out,
3306 ) -> Out::Batched {
3307 thunk(self.all_ticks_atomic()).batched_atomic()
3308 }
3309
3310 pub fn defer_tick(self) -> Stream<T, Tick<L>, Bounded, O, R> {
3349 Stream::new(
3350 self.location.clone(),
3351 HydroNode::DeferTick {
3352 input: Box::new(self.ir_node.replace(HydroNode::Placeholder)),
3353 metadata: self
3354 .location
3355 .new_node_metadata(Stream::<T, Tick<L>, Bounded, O, R>::collection_kind()),
3356 },
3357 )
3358 }
3359}
3360
3361#[cfg(test)]
3362mod tests {
3363 #[cfg(feature = "deploy")]
3364 use futures::{SinkExt, StreamExt};
3365 #[cfg(feature = "deploy")]
3366 use hydro_deploy::Deployment;
3367 #[cfg(feature = "deploy")]
3368 use serde::{Deserialize, Serialize};
3369 #[cfg(any(feature = "deploy", feature = "sim"))]
3370 use stageleft::q;
3371
3372 #[cfg(any(feature = "deploy", feature = "sim"))]
3373 use crate::compile::builder::FlowBuilder;
3374 #[cfg(feature = "deploy")]
3375 use crate::live_collections::sliced::sliced;
3376 #[cfg(feature = "deploy")]
3377 use crate::live_collections::stream::ExactlyOnce;
3378 #[cfg(feature = "sim")]
3379 use crate::live_collections::stream::NoOrder;
3380 #[cfg(any(feature = "deploy", feature = "sim"))]
3381 use crate::live_collections::stream::TotalOrder;
3382 #[cfg(any(feature = "deploy", feature = "sim"))]
3383 use crate::location::Location;
3384 #[cfg(feature = "sim")]
3385 use crate::networking::TCP;
3386 #[cfg(any(feature = "deploy", feature = "sim"))]
3387 use crate::nondet::nondet;
3388
3389 mod backtrace_chained_ops;
3390
3391 #[cfg(feature = "deploy")]
3392 struct P1 {}
3393 #[cfg(feature = "deploy")]
3394 struct P2 {}
3395
3396 #[cfg(feature = "deploy")]
3397 #[derive(Serialize, Deserialize, Debug)]
3398 struct SendOverNetwork {
3399 n: u32,
3400 }
3401
3402 #[cfg(feature = "deploy")]
3403 #[tokio::test]
3404 async fn first_ten_distributed() {
3405 use crate::networking::TCP;
3406
3407 let mut deployment = Deployment::new();
3408
3409 let mut flow = FlowBuilder::new();
3410 let first_node = flow.process::<P1>();
3411 let second_node = flow.process::<P2>();
3412 let external = flow.external::<P2>();
3413
3414 let numbers = first_node.source_iter(q!(0..10));
3415 let out_port = numbers
3416 .map(q!(|n| SendOverNetwork { n }))
3417 .send(&second_node, TCP.fail_stop().bincode())
3418 .send_bincode_external(&external);
3419
3420 let nodes = flow
3421 .with_process(&first_node, deployment.Localhost())
3422 .with_process(&second_node, deployment.Localhost())
3423 .with_external(&external, deployment.Localhost())
3424 .deploy(&mut deployment);
3425
3426 deployment.deploy().await.unwrap();
3427
3428 let mut external_out = nodes.connect(out_port).await;
3429
3430 deployment.start().await.unwrap();
3431
3432 for i in 0..10 {
3433 assert_eq!(external_out.next().await.unwrap().n, i);
3434 }
3435 }
3436
3437 #[cfg(feature = "deploy")]
3438 #[tokio::test]
3439 async fn first_cardinality() {
3440 let mut deployment = Deployment::new();
3441
3442 let mut flow = FlowBuilder::new();
3443 let node = flow.process::<()>();
3444 let external = flow.external::<()>();
3445
3446 let node_tick = node.tick();
3447 let count = node_tick
3448 .singleton(q!([1, 2, 3]))
3449 .into_stream()
3450 .flatten_ordered()
3451 .first()
3452 .into_stream()
3453 .count()
3454 .all_ticks()
3455 .send_bincode_external(&external);
3456
3457 let nodes = flow
3458 .with_process(&node, deployment.Localhost())
3459 .with_external(&external, deployment.Localhost())
3460 .deploy(&mut deployment);
3461
3462 deployment.deploy().await.unwrap();
3463
3464 let mut external_out = nodes.connect(count).await;
3465
3466 deployment.start().await.unwrap();
3467
3468 assert_eq!(external_out.next().await.unwrap(), 1);
3469 }
3470
3471 #[cfg(feature = "deploy")]
3472 #[tokio::test]
3473 async fn unbounded_reduce_remembers_state() {
3474 let mut deployment = Deployment::new();
3475
3476 let mut flow = FlowBuilder::new();
3477 let node = flow.process::<()>();
3478 let external = flow.external::<()>();
3479
3480 let (input_port, input) = node.source_external_bincode(&external);
3481 let out = input
3482 .reduce(q!(|acc, v| *acc += v))
3483 .sample_eager(nondet!())
3484 .send_bincode_external(&external);
3485
3486 let nodes = flow
3487 .with_process(&node, deployment.Localhost())
3488 .with_external(&external, deployment.Localhost())
3489 .deploy(&mut deployment);
3490
3491 deployment.deploy().await.unwrap();
3492
3493 let mut external_in = nodes.connect(input_port).await;
3494 let mut external_out = nodes.connect(out).await;
3495
3496 deployment.start().await.unwrap();
3497
3498 external_in.send(1).await.unwrap();
3499 assert_eq!(external_out.next().await.unwrap(), 1);
3500
3501 external_in.send(2).await.unwrap();
3502 assert_eq!(external_out.next().await.unwrap(), 3);
3503 }
3504
3505 #[cfg(feature = "deploy")]
3506 #[tokio::test]
3507 async fn top_level_bounded_cross_singleton() {
3508 let mut deployment = Deployment::new();
3509
3510 let mut flow = FlowBuilder::new();
3511 let node = flow.process::<()>();
3512 let external = flow.external::<()>();
3513
3514 let (input_port, input) =
3515 node.source_external_bincode::<_, _, TotalOrder, ExactlyOnce>(&external);
3516
3517 let out = input
3518 .cross_singleton(
3519 node.source_iter(q!(vec![1, 2, 3]))
3520 .fold(q!(|| 0), q!(|acc, v| *acc += v)),
3521 )
3522 .send_bincode_external(&external);
3523
3524 let nodes = flow
3525 .with_process(&node, deployment.Localhost())
3526 .with_external(&external, deployment.Localhost())
3527 .deploy(&mut deployment);
3528
3529 deployment.deploy().await.unwrap();
3530
3531 let mut external_in = nodes.connect(input_port).await;
3532 let mut external_out = nodes.connect(out).await;
3533
3534 deployment.start().await.unwrap();
3535
3536 external_in.send(1).await.unwrap();
3537 assert_eq!(external_out.next().await.unwrap(), (1, 6));
3538
3539 external_in.send(2).await.unwrap();
3540 assert_eq!(external_out.next().await.unwrap(), (2, 6));
3541 }
3542
3543 #[cfg(feature = "deploy")]
3544 #[tokio::test]
3545 async fn top_level_bounded_reduce_cardinality() {
3546 let mut deployment = Deployment::new();
3547
3548 let mut flow = FlowBuilder::new();
3549 let node = flow.process::<()>();
3550 let external = flow.external::<()>();
3551
3552 let (input_port, input) =
3553 node.source_external_bincode::<_, _, TotalOrder, ExactlyOnce>(&external);
3554
3555 let out = sliced! {
3556 let input = use::batch(input, nondet!());
3557 let v = use::snapshot(node.source_iter(q!(vec![1, 2, 3])).reduce(q!(|acc, v| *acc += v)), nondet!());
3558 input.cross_singleton(v.into_stream().count())
3559 }
3560 .send_bincode_external(&external);
3561
3562 let nodes = flow
3563 .with_process(&node, deployment.Localhost())
3564 .with_external(&external, deployment.Localhost())
3565 .deploy(&mut deployment);
3566
3567 deployment.deploy().await.unwrap();
3568
3569 let mut external_in = nodes.connect(input_port).await;
3570 let mut external_out = nodes.connect(out).await;
3571
3572 deployment.start().await.unwrap();
3573
3574 external_in.send(1).await.unwrap();
3575 assert_eq!(external_out.next().await.unwrap(), (1, 1));
3576
3577 external_in.send(2).await.unwrap();
3578 assert_eq!(external_out.next().await.unwrap(), (2, 1));
3579 }
3580
3581 #[cfg(feature = "deploy")]
3582 #[tokio::test]
3583 async fn top_level_bounded_into_singleton_cardinality() {
3584 let mut deployment = Deployment::new();
3585
3586 let mut flow = FlowBuilder::new();
3587 let node = flow.process::<()>();
3588 let external = flow.external::<()>();
3589
3590 let (input_port, input) =
3591 node.source_external_bincode::<_, _, TotalOrder, ExactlyOnce>(&external);
3592
3593 let out = sliced! {
3594 let input = use::batch(input, nondet!());
3595 let v = use::snapshot(node.source_iter(q!(vec![1, 2, 3])).reduce(q!(|acc, v| *acc += v)).into_singleton(), nondet!());
3596 input.cross_singleton(v.into_stream().count())
3597 }
3598 .send_bincode_external(&external);
3599
3600 let nodes = flow
3601 .with_process(&node, deployment.Localhost())
3602 .with_external(&external, deployment.Localhost())
3603 .deploy(&mut deployment);
3604
3605 deployment.deploy().await.unwrap();
3606
3607 let mut external_in = nodes.connect(input_port).await;
3608 let mut external_out = nodes.connect(out).await;
3609
3610 deployment.start().await.unwrap();
3611
3612 external_in.send(1).await.unwrap();
3613 assert_eq!(external_out.next().await.unwrap(), (1, 1));
3614
3615 external_in.send(2).await.unwrap();
3616 assert_eq!(external_out.next().await.unwrap(), (2, 1));
3617 }
3618
3619 #[cfg(feature = "deploy")]
3620 #[tokio::test]
3621 async fn atomic_fold_replays_each_tick() {
3622 let mut deployment = Deployment::new();
3623
3624 let mut flow = FlowBuilder::new();
3625 let node = flow.process::<()>();
3626 let external = flow.external::<()>();
3627
3628 let (input_port, input) =
3629 node.source_external_bincode::<_, _, TotalOrder, ExactlyOnce>(&external);
3630 let tick = node.tick();
3631
3632 let out = input
3633 .batch(&tick, nondet!())
3634 .cross_singleton(
3635 node.source_iter(q!(vec![1, 2, 3]))
3636 .atomic()
3637 .fold(q!(|| 0), q!(|acc, v| *acc += v))
3638 .snapshot_atomic(&tick, nondet!()),
3639 )
3640 .all_ticks()
3641 .send_bincode_external(&external);
3642
3643 let nodes = flow
3644 .with_process(&node, deployment.Localhost())
3645 .with_external(&external, deployment.Localhost())
3646 .deploy(&mut deployment);
3647
3648 deployment.deploy().await.unwrap();
3649
3650 let mut external_in = nodes.connect(input_port).await;
3651 let mut external_out = nodes.connect(out).await;
3652
3653 deployment.start().await.unwrap();
3654
3655 external_in.send(1).await.unwrap();
3656 assert_eq!(external_out.next().await.unwrap(), (1, 6));
3657
3658 external_in.send(2).await.unwrap();
3659 assert_eq!(external_out.next().await.unwrap(), (2, 6));
3660 }
3661
3662 #[cfg(feature = "deploy")]
3663 #[tokio::test]
3664 async fn unbounded_scan_remembers_state() {
3665 let mut deployment = Deployment::new();
3666
3667 let mut flow = FlowBuilder::new();
3668 let node = flow.process::<()>();
3669 let external = flow.external::<()>();
3670
3671 let (input_port, input) = node.source_external_bincode(&external);
3672 let out = input
3673 .scan(
3674 q!(|| 0),
3675 q!(|acc, v| {
3676 *acc += v;
3677 Some(*acc)
3678 }),
3679 )
3680 .send_bincode_external(&external);
3681
3682 let nodes = flow
3683 .with_process(&node, deployment.Localhost())
3684 .with_external(&external, deployment.Localhost())
3685 .deploy(&mut deployment);
3686
3687 deployment.deploy().await.unwrap();
3688
3689 let mut external_in = nodes.connect(input_port).await;
3690 let mut external_out = nodes.connect(out).await;
3691
3692 deployment.start().await.unwrap();
3693
3694 external_in.send(1).await.unwrap();
3695 assert_eq!(external_out.next().await.unwrap(), 1);
3696
3697 external_in.send(2).await.unwrap();
3698 assert_eq!(external_out.next().await.unwrap(), 3);
3699 }
3700
3701 #[cfg(feature = "deploy")]
3702 #[tokio::test]
3703 async fn unbounded_enumerate_remembers_state() {
3704 let mut deployment = Deployment::new();
3705
3706 let mut flow = FlowBuilder::new();
3707 let node = flow.process::<()>();
3708 let external = flow.external::<()>();
3709
3710 let (input_port, input) = node.source_external_bincode(&external);
3711 let out = input.enumerate().send_bincode_external(&external);
3712
3713 let nodes = flow
3714 .with_process(&node, deployment.Localhost())
3715 .with_external(&external, deployment.Localhost())
3716 .deploy(&mut deployment);
3717
3718 deployment.deploy().await.unwrap();
3719
3720 let mut external_in = nodes.connect(input_port).await;
3721 let mut external_out = nodes.connect(out).await;
3722
3723 deployment.start().await.unwrap();
3724
3725 external_in.send(1).await.unwrap();
3726 assert_eq!(external_out.next().await.unwrap(), (0, 1));
3727
3728 external_in.send(2).await.unwrap();
3729 assert_eq!(external_out.next().await.unwrap(), (1, 2));
3730 }
3731
3732 #[cfg(feature = "deploy")]
3733 #[tokio::test]
3734 async fn unbounded_unique_remembers_state() {
3735 let mut deployment = Deployment::new();
3736
3737 let mut flow = FlowBuilder::new();
3738 let node = flow.process::<()>();
3739 let external = flow.external::<()>();
3740
3741 let (input_port, input) =
3742 node.source_external_bincode::<_, _, TotalOrder, ExactlyOnce>(&external);
3743 let out = input.unique().send_bincode_external(&external);
3744
3745 let nodes = flow
3746 .with_process(&node, deployment.Localhost())
3747 .with_external(&external, deployment.Localhost())
3748 .deploy(&mut deployment);
3749
3750 deployment.deploy().await.unwrap();
3751
3752 let mut external_in = nodes.connect(input_port).await;
3753 let mut external_out = nodes.connect(out).await;
3754
3755 deployment.start().await.unwrap();
3756
3757 external_in.send(1).await.unwrap();
3758 assert_eq!(external_out.next().await.unwrap(), 1);
3759
3760 external_in.send(2).await.unwrap();
3761 assert_eq!(external_out.next().await.unwrap(), 2);
3762
3763 external_in.send(1).await.unwrap();
3764 external_in.send(3).await.unwrap();
3765 assert_eq!(external_out.next().await.unwrap(), 3);
3766 }
3767
3768 #[cfg(feature = "sim")]
3769 #[test]
3770 #[should_panic]
3771 fn sim_batch_nondet_size() {
3772 let mut flow = FlowBuilder::new();
3773 let node = flow.process::<()>();
3774
3775 let (in_send, input) = node.sim_input::<_, TotalOrder, _>();
3776
3777 let tick = node.tick();
3778 let out_recv = input
3779 .batch(&tick, nondet!())
3780 .count()
3781 .all_ticks()
3782 .sim_output();
3783
3784 flow.sim().exhaustive(async || {
3785 in_send.send(());
3786 in_send.send(());
3787 in_send.send(());
3788
3789 assert_eq!(out_recv.next().await, 3); });
3791 }
3792
3793 #[cfg(feature = "sim")]
3794 #[test]
3795 fn sim_batch_preserves_order() {
3796 let mut flow = FlowBuilder::new();
3797 let node = flow.process::<()>();
3798
3799 let (in_send, input) = node.sim_input();
3800
3801 let tick = node.tick();
3802 let out_recv = input
3803 .batch(&tick, nondet!())
3804 .all_ticks()
3805 .sim_output();
3806
3807 flow.sim().exhaustive(async || {
3808 in_send.send(1);
3809 in_send.send(2);
3810 in_send.send(3);
3811
3812 out_recv.assert_yields_only([1, 2, 3]).await;
3813 });
3814 }
3815
3816 #[cfg(feature = "sim")]
3817 #[test]
3818 #[should_panic]
3819 fn sim_batch_unordered_shuffles() {
3820 let mut flow = FlowBuilder::new();
3821 let node = flow.process::<()>();
3822
3823 let (in_send, input) = node.sim_input::<_, NoOrder, _>();
3824
3825 let tick = node.tick();
3826 let batch = input.batch(&tick, nondet!());
3827 let out_recv = batch
3828 .clone()
3829 .min()
3830 .zip(batch.max())
3831 .all_ticks()
3832 .sim_output();
3833
3834 flow.sim().exhaustive(async || {
3835 in_send.send_many_unordered([1, 2, 3]);
3836
3837 if out_recv.collect::<Vec<_>>().await == vec![(1, 3), (2, 2)] {
3838 panic!("saw both (1, 3) and (2, 2), so batching must have shuffled the order");
3839 }
3840 });
3841 }
3842
3843 #[cfg(feature = "sim")]
3844 #[test]
3845 fn sim_batch_unordered_shuffles_count() {
3846 let mut flow = FlowBuilder::new();
3847 let node = flow.process::<()>();
3848
3849 let (in_send, input) = node.sim_input::<_, NoOrder, _>();
3850
3851 let tick = node.tick();
3852 let batch = input.batch(&tick, nondet!());
3853 let out_recv = batch.all_ticks().sim_output();
3854
3855 let instance_count = flow.sim().exhaustive(async || {
3856 in_send.send_many_unordered([1, 2, 3, 4]);
3857 out_recv.assert_yields_only_unordered([1, 2, 3, 4]).await;
3858 });
3859
3860 assert_eq!(
3861 instance_count,
3862 75 )
3864 }
3865
3866 #[cfg(feature = "sim")]
3867 #[test]
3868 #[should_panic]
3869 fn sim_observe_order_batched() {
3870 let mut flow = FlowBuilder::new();
3871 let node = flow.process::<()>();
3872
3873 let (in_send, input) = node.sim_input::<_, NoOrder, _>();
3874
3875 let tick = node.tick();
3876 let batch = input.batch(&tick, nondet!());
3877 let out_recv = batch
3878 .assume_ordering::<TotalOrder>(nondet!())
3879 .all_ticks()
3880 .sim_output();
3881
3882 flow.sim().exhaustive(async || {
3883 in_send.send_many_unordered([1, 2, 3, 4]);
3884 out_recv.assert_yields_only([1, 2, 3, 4]).await; });
3886 }
3887
3888 #[cfg(feature = "sim")]
3889 #[test]
3890 fn sim_observe_order_batched_count() {
3891 let mut flow = FlowBuilder::new();
3892 let node = flow.process::<()>();
3893
3894 let (in_send, input) = node.sim_input::<_, NoOrder, _>();
3895
3896 let tick = node.tick();
3897 let batch = input.batch(&tick, nondet!());
3898 let out_recv = batch
3899 .assume_ordering::<TotalOrder>(nondet!())
3900 .all_ticks()
3901 .sim_output();
3902
3903 let instance_count = flow.sim().exhaustive(async || {
3904 in_send.send_many_unordered([1, 2, 3, 4]);
3905 let _ = out_recv.collect::<Vec<_>>().await;
3906 });
3907
3908 assert_eq!(
3909 instance_count,
3910 192 )
3912 }
3913
3914 #[cfg(feature = "sim")]
3915 #[test]
3916 fn sim_unordered_count_instance_count() {
3917 let mut flow = FlowBuilder::new();
3918 let node = flow.process::<()>();
3919
3920 let (in_send, input) = node.sim_input::<_, NoOrder, _>();
3921
3922 let tick = node.tick();
3923 let out_recv = input
3924 .count()
3925 .snapshot(&tick, nondet!())
3926 .all_ticks()
3927 .sim_output();
3928
3929 let instance_count = flow.sim().exhaustive(async || {
3930 in_send.send_many_unordered([1, 2, 3, 4]);
3931 assert!(out_recv.collect::<Vec<_>>().await.last().unwrap() == &4);
3932 });
3933
3934 assert_eq!(
3935 instance_count,
3936 16 )
3938 }
3939
3940 #[cfg(feature = "sim")]
3941 #[test]
3942 fn sim_top_level_assume_ordering() {
3943 let mut flow = FlowBuilder::new();
3944 let node = flow.process::<()>();
3945
3946 let (in_send, input) = node.sim_input::<_, NoOrder, _>();
3947
3948 let out_recv = input
3949 .assume_ordering::<TotalOrder>(nondet!())
3950 .sim_output();
3951
3952 let instance_count = flow.sim().exhaustive(async || {
3953 in_send.send_many_unordered([1, 2, 3]);
3954 let mut out = out_recv.collect::<Vec<_>>().await;
3955 out.sort();
3956 assert_eq!(out, vec![1, 2, 3]);
3957 });
3958
3959 assert_eq!(instance_count, 6)
3960 }
3961
3962 #[cfg(feature = "sim")]
3963 #[test]
3964 fn sim_top_level_assume_ordering_cycle_back() {
3965 let mut flow = FlowBuilder::new();
3966 let node = flow.process::<()>();
3967 let node2 = flow.process::<()>();
3968
3969 let (in_send, input) = node.sim_input::<_, NoOrder, _>();
3970
3971 let (complete_cycle_back, cycle_back) =
3972 node.forward_ref::<super::Stream<_, _, _, NoOrder>>();
3973 let ordered = input
3974 .merge_unordered(cycle_back)
3975 .assume_ordering::<TotalOrder>(nondet!());
3976 complete_cycle_back.complete(
3977 ordered
3978 .clone()
3979 .map(q!(|v| v + 1))
3980 .filter(q!(|v| v % 2 == 1))
3981 .send(&node2, TCP.fail_stop().bincode())
3982 .send(&node, TCP.fail_stop().bincode()),
3983 );
3984
3985 let out_recv = ordered.sim_output();
3986
3987 let mut saw = false;
3988 let instance_count = flow.sim().exhaustive(async || {
3989 in_send.send_many_unordered([0, 2]);
3990 let out = out_recv.collect::<Vec<_>>().await;
3991
3992 if out.starts_with(&[0, 1, 2]) {
3993 saw = true;
3994 }
3995 });
3996
3997 assert!(saw, "did not see an instance with 0, 1, 2 in order");
3998 assert_eq!(instance_count, 6);
3999 }
4000
4001 #[cfg(feature = "sim")]
4002 #[test]
4003 fn sim_top_level_assume_ordering_cycle_back_tick() {
4004 let mut flow = FlowBuilder::new();
4005 let node = flow.process::<()>();
4006 let node2 = flow.process::<()>();
4007
4008 let (in_send, input) = node.sim_input::<_, NoOrder, _>();
4009
4010 let (complete_cycle_back, cycle_back) =
4011 node.forward_ref::<super::Stream<_, _, _, NoOrder>>();
4012 let ordered = input
4013 .merge_unordered(cycle_back)
4014 .assume_ordering::<TotalOrder>(nondet!());
4015 complete_cycle_back.complete(
4016 ordered
4017 .clone()
4018 .batch(&node.tick(), nondet!())
4019 .all_ticks()
4020 .map(q!(|v| v + 1))
4021 .filter(q!(|v| v % 2 == 1))
4022 .send(&node2, TCP.fail_stop().bincode())
4023 .send(&node, TCP.fail_stop().bincode()),
4024 );
4025
4026 let out_recv = ordered.sim_output();
4027
4028 let mut saw = false;
4029 let instance_count = flow.sim().exhaustive(async || {
4030 in_send.send_many_unordered([0, 2]);
4031 let out = out_recv.collect::<Vec<_>>().await;
4032
4033 if out.starts_with(&[0, 1, 2]) {
4034 saw = true;
4035 }
4036 });
4037
4038 assert!(saw, "did not see an instance with 0, 1, 2 in order");
4039 assert_eq!(instance_count, 58);
4040 }
4041
4042 #[cfg(feature = "sim")]
4043 #[test]
4044 fn sim_top_level_assume_ordering_multiple() {
4045 let mut flow = FlowBuilder::new();
4046 let node = flow.process::<()>();
4047 let node2 = flow.process::<()>();
4048
4049 let (in_send, input) = node.sim_input::<_, NoOrder, _>();
4050 let (_, input2) = node.sim_input::<_, NoOrder, _>();
4051
4052 let (complete_cycle_back, cycle_back) =
4053 node.forward_ref::<super::Stream<_, _, _, NoOrder>>();
4054 let input1_ordered = input
4055 .clone()
4056 .merge_unordered(cycle_back)
4057 .assume_ordering::<TotalOrder>(nondet!());
4058 let foo = input1_ordered
4059 .clone()
4060 .map(q!(|v| v + 3))
4061 .weaken_ordering::<NoOrder>()
4062 .merge_unordered(input2)
4063 .assume_ordering::<TotalOrder>(nondet!());
4064
4065 complete_cycle_back.complete(
4066 foo.filter(q!(|v| *v == 3))
4067 .send(&node2, TCP.fail_stop().bincode())
4068 .send(&node, TCP.fail_stop().bincode()),
4069 );
4070
4071 let out_recv = input1_ordered.sim_output();
4072
4073 let mut saw = false;
4074 let instance_count = flow.sim().exhaustive(async || {
4075 in_send.send_many_unordered([0, 1]);
4076 let out = out_recv.collect::<Vec<_>>().await;
4077
4078 if out.starts_with(&[0, 3, 1]) {
4079 saw = true;
4080 }
4081 });
4082
4083 assert!(saw, "did not see an instance with 0, 3, 1 in order");
4084 assert_eq!(instance_count, 24);
4085 }
4086
4087 #[cfg(feature = "sim")]
4088 #[test]
4089 fn sim_atomic_assume_ordering_cycle_back() {
4090 let mut flow = FlowBuilder::new();
4091 let node = flow.process::<()>();
4092 let node2 = flow.process::<()>();
4093
4094 let (in_send, input) = node.sim_input::<_, NoOrder, _>();
4095
4096 let (complete_cycle_back, cycle_back) =
4097 node.forward_ref::<super::Stream<_, _, _, NoOrder>>();
4098 let ordered = input
4099 .merge_unordered(cycle_back)
4100 .atomic()
4101 .assume_ordering::<TotalOrder>(nondet!())
4102 .end_atomic();
4103 complete_cycle_back.complete(
4104 ordered
4105 .clone()
4106 .map(q!(|v| v + 1))
4107 .filter(q!(|v| v % 2 == 1))
4108 .send(&node2, TCP.fail_stop().bincode())
4109 .send(&node, TCP.fail_stop().bincode()),
4110 );
4111
4112 let out_recv = ordered.sim_output();
4113
4114 let instance_count = flow.sim().exhaustive(async || {
4115 in_send.send_many_unordered([0, 2]);
4116 let out = out_recv.collect::<Vec<_>>().await;
4117 assert_eq!(out.len(), 4);
4118 });
4119 assert_eq!(instance_count, 22);
4120 }
4121
4122 #[cfg(feature = "deploy")]
4123 #[tokio::test]
4124 async fn partition_evens_odds() {
4125 let mut deployment = Deployment::new();
4126
4127 let mut flow = FlowBuilder::new();
4128 let node = flow.process::<()>();
4129 let external = flow.external::<()>();
4130
4131 let numbers = node.source_iter(q!(vec![1i32, 2, 3, 4, 5, 6]));
4132 let (evens, odds) = numbers.partition(q!(|x: &i32| x % 2 == 0));
4133 let evens_port = evens.send_bincode_external(&external);
4134 let odds_port = odds.send_bincode_external(&external);
4135
4136 let nodes = flow
4137 .with_process(&node, deployment.Localhost())
4138 .with_external(&external, deployment.Localhost())
4139 .deploy(&mut deployment);
4140
4141 deployment.deploy().await.unwrap();
4142
4143 let mut evens_out = nodes.connect(evens_port).await;
4144 let mut odds_out = nodes.connect(odds_port).await;
4145
4146 deployment.start().await.unwrap();
4147
4148 let mut even_results = Vec::new();
4149 for _ in 0..3 {
4150 even_results.push(evens_out.next().await.unwrap());
4151 }
4152 even_results.sort();
4153 assert_eq!(even_results, vec![2, 4, 6]);
4154
4155 let mut odd_results = Vec::new();
4156 for _ in 0..3 {
4157 odd_results.push(odds_out.next().await.unwrap());
4158 }
4159 odd_results.sort();
4160 assert_eq!(odd_results, vec![1, 3, 5]);
4161 }
4162
4163 #[cfg(feature = "deploy")]
4164 #[tokio::test]
4165 async fn unconsumed_inspect_still_runs() {
4166 use crate::deploy::DeployCrateWrapper;
4167
4168 let mut deployment = Deployment::new();
4169
4170 let mut flow = FlowBuilder::new();
4171 let node = flow.process::<()>();
4172
4173 node.source_iter(q!(0..5))
4176 .inspect(q!(|x| println!("inspect: {}", x)));
4177
4178 let nodes = flow
4179 .with_process(&node, deployment.Localhost())
4180 .deploy(&mut deployment);
4181
4182 deployment.deploy().await.unwrap();
4183
4184 let mut stdout = nodes.get_process(&node).stdout();
4185
4186 deployment.start().await.unwrap();
4187
4188 let mut lines = Vec::new();
4189 for _ in 0..5 {
4190 lines.push(stdout.recv().await.unwrap());
4191 }
4192 lines.sort();
4193 assert_eq!(
4194 lines,
4195 vec![
4196 "inspect: 0",
4197 "inspect: 1",
4198 "inspect: 2",
4199 "inspect: 3",
4200 "inspect: 4",
4201 ]
4202 );
4203 }
4204
4205 #[cfg(feature = "deploy")]
4206 #[tokio::test]
4207 async fn unconsumed_inspect_alive_at_deploy_still_runs() {
4208 use crate::deploy::DeployCrateWrapper;
4209
4210 let mut deployment = Deployment::new();
4211
4212 let mut flow = FlowBuilder::new();
4213 let node = flow.process::<()>();
4214
4215 let _inspected = node
4220 .source_iter(q!(0..5))
4221 .inspect(q!(|x| println!("inspect: {}", x)));
4222
4223 let nodes = flow
4224 .with_process(&node, deployment.Localhost())
4225 .deploy(&mut deployment);
4226
4227 deployment.deploy().await.unwrap();
4228
4229 let mut stdout = nodes.get_process(&node).stdout();
4230
4231 deployment.start().await.unwrap();
4232
4233 let mut lines = Vec::new();
4234 for _ in 0..5 {
4235 lines.push(stdout.recv().await.unwrap());
4236 }
4237 lines.sort();
4238 assert_eq!(
4239 lines,
4240 vec![
4241 "inspect: 0",
4242 "inspect: 1",
4243 "inspect: 2",
4244 "inspect: 3",
4245 "inspect: 4",
4246 ]
4247 );
4248 }
4249
4250 #[cfg(feature = "sim")]
4251 #[test]
4252 fn sim_limit() {
4253 let mut flow = FlowBuilder::new();
4254 let node = flow.process::<()>();
4255
4256 let (in_send, input) = node.sim_input();
4257
4258 let out_recv = input.limit(q!(3)).sim_output();
4259
4260 flow.sim().exhaustive(async || {
4261 in_send.send(1);
4262 in_send.send(2);
4263 in_send.send(3);
4264 in_send.send(4);
4265 in_send.send(5);
4266
4267 out_recv.assert_yields_only([1, 2, 3]).await;
4268 });
4269 }
4270
4271 #[cfg(feature = "sim")]
4272 #[test]
4273 fn sim_limit_zero() {
4274 let mut flow = FlowBuilder::new();
4275 let node = flow.process::<()>();
4276
4277 let (in_send, input) = node.sim_input();
4278
4279 let out_recv = input.limit(q!(0)).sim_output();
4280
4281 flow.sim().exhaustive(async || {
4282 in_send.send(1);
4283 in_send.send(2);
4284
4285 out_recv.assert_yields_only::<i32, _>([]).await;
4286 });
4287 }
4288
4289 #[cfg(feature = "sim")]
4290 #[test]
4291 fn sim_merge_ordered() {
4292 let mut flow = FlowBuilder::new();
4293 let node = flow.process::<()>();
4294
4295 let (in_send, input) = node.sim_input();
4296 let (in_send2, input2) = node.sim_input();
4297
4298 let out_recv = input
4299 .merge_ordered(input2, nondet!())
4300 .sim_output();
4301
4302 let mut saw_out_of_order = false;
4303 let instances = flow.sim().exhaustive(async || {
4304 in_send.send(1);
4305 in_send.send(2);
4306 in_send2.send(3);
4307 in_send2.send(4);
4308
4309 let out = out_recv.collect::<Vec<_>>().await;
4310
4311 if out == [1, 3, 2, 4] {
4312 saw_out_of_order = true;
4313 }
4314
4315 let mut first_elements = out.iter().filter(|v| **v <= 2).copied().collect::<Vec<_>>();
4318 let mut second_elements = out.iter().filter(|v| **v > 2).copied().collect::<Vec<_>>();
4319 assert_eq!(
4320 first_elements,
4321 vec![1, 2],
4322 "first input order violated: {:?}",
4323 out
4324 );
4325 assert_eq!(
4326 second_elements,
4327 vec![3, 4],
4328 "second input order violated: {:?}",
4329 out
4330 );
4331
4332 first_elements.append(&mut second_elements);
4333 first_elements.sort();
4334 assert_eq!(first_elements, vec![1, 2, 3, 4]);
4335 });
4336
4337 assert!(saw_out_of_order);
4338 assert_eq!(instances, 6);
4339 }
4340
4341 #[cfg(feature = "sim")]
4344 #[test]
4345 fn sim_merge_ordered_one_empty() {
4346 let mut flow = FlowBuilder::new();
4347 let node = flow.process::<()>();
4348
4349 let (in_send, input) = node.sim_input();
4350 let (_in_send2, input2) = node.sim_input();
4351
4352 let out_recv = input
4353 .merge_ordered(input2, nondet!())
4354 .sim_output();
4355
4356 let instances = flow.sim().exhaustive(async || {
4357 in_send.send(1);
4358 in_send.send(2);
4359
4360 let out = out_recv.collect::<Vec<_>>().await;
4361 assert_eq!(out, vec![1, 2]);
4362 });
4363
4364 assert_eq!(instances, 1);
4366 }
4367
4368 #[cfg(feature = "sim")]
4374 #[test]
4375 fn sim_merge_ordered_cycle_back() {
4376 let mut flow = FlowBuilder::new();
4377 let node = flow.process::<()>();
4378
4379 let (in_send, input) = node.sim_input();
4380
4381 let (complete_cycle_back, cycle_back) =
4383 node.forward_ref::<super::Stream<_, _, _, TotalOrder>>();
4384
4385 let merged = input.merge_ordered(cycle_back, nondet!());
4387
4388 complete_cycle_back.complete(merged.clone().filter(q!(|v| *v == 1)).map(q!(|v| v * 10)));
4390
4391 let out_recv = merged.sim_output();
4392
4393 let mut saw_cycle_before_second = false;
4396 flow.sim().exhaustive(async || {
4397 in_send.send(1);
4398 in_send.send(2);
4399
4400 let out = out_recv.collect::<Vec<_>>().await;
4401
4402 let pos_1 = out.iter().position(|v| *v == 1).unwrap();
4404 let pos_10 = out.iter().position(|v| *v == 10).unwrap();
4405 assert!(pos_1 < pos_10, "causal order violated: {:?}", out);
4406
4407 if out == [1, 10, 2] {
4409 saw_cycle_before_second = true;
4410 }
4411
4412 let mut sorted = out;
4413 sorted.sort();
4414 assert_eq!(sorted, vec![1, 2, 10]);
4415 });
4416
4417 assert!(
4418 saw_cycle_before_second,
4419 "never saw the cycled element arrive before the second input element"
4420 );
4421 }
4422
4423 #[cfg(feature = "sim")]
4427 #[test]
4428 fn sim_merge_ordered_delayed() {
4429 let mut flow = FlowBuilder::new();
4430 let node = flow.process::<()>();
4431
4432 let (in_send, input) = node.sim_input();
4433 let (in_send2, input2) = node.sim_input();
4434
4435 let out_recv = input
4436 .merge_ordered(input2, nondet!())
4437 .sim_output();
4438
4439 let mut saw_delayed_interleaving = false;
4440 flow.sim().exhaustive(async || {
4441 in_send.send(1);
4443 in_send2.send(3);
4444 in_send2.send(4);
4445
4446 let first_batch = out_recv.collect::<Vec<_>>().await;
4448
4449 in_send.send(2);
4451 let second_batch = out_recv.collect::<Vec<_>>().await;
4452
4453 let mut all: Vec<_> = first_batch
4454 .iter()
4455 .chain(second_batch.iter())
4456 .copied()
4457 .collect();
4458
4459 if all == [1, 3, 4, 2] {
4461 saw_delayed_interleaving = true;
4462 }
4463
4464 all.sort();
4465 assert_eq!(all, vec![1, 2, 3, 4]);
4466 });
4467
4468 assert!(saw_delayed_interleaving);
4469 }
4470
4471 #[cfg(feature = "deploy")]
4476 #[tokio::test]
4477 async fn deploy_merge_ordered_delayed() {
4478 let mut deployment = Deployment::new();
4479
4480 let mut flow = FlowBuilder::new();
4481 let node = flow.process::<()>();
4482 let external = flow.external::<()>();
4483
4484 let (input_a_port, input_a) = node.source_external_bincode(&external);
4485 let (input_b_port, input_b) = node.source_external_bincode(&external);
4486
4487 let out = input_a
4488 .assume_ordering(nondet!())
4489 .merge_ordered(
4490 input_b.assume_ordering(nondet!()),
4491 nondet!(),
4492 )
4493 .send_bincode_external(&external);
4494
4495 let nodes = flow
4496 .with_process(&node, deployment.Localhost())
4497 .with_external(&external, deployment.Localhost())
4498 .deploy(&mut deployment);
4499
4500 deployment.deploy().await.unwrap();
4501
4502 let mut ext_a = nodes.connect(input_a_port).await;
4503 let mut ext_b = nodes.connect(input_b_port).await;
4504 let mut ext_out = nodes.connect(out).await;
4505
4506 deployment.start().await.unwrap();
4507
4508 ext_a.send(1).await.unwrap();
4510 ext_b.send(3).await.unwrap();
4511 ext_b.send(4).await.unwrap();
4512
4513 let mut received = Vec::new();
4515 for _ in 0..3 {
4516 received.push(ext_out.next().await.unwrap());
4517 }
4518
4519 ext_a.send(2).await.unwrap();
4521 received.push(ext_out.next().await.unwrap());
4522
4523 received.sort();
4525 assert_eq!(received, vec![1, 2, 3, 4]);
4526 }
4527
4528 #[cfg(feature = "deploy")]
4529 #[tokio::test]
4530 async fn monotone_fold_threshold() {
4531 use crate::properties::manual_proof;
4532
4533 let mut deployment = Deployment::new();
4534
4535 let mut flow = FlowBuilder::new();
4536 let node = flow.process::<()>();
4537 let external = flow.external::<()>();
4538
4539 let in_unbounded: super::Stream<_, _> =
4540 node.source_iter(q!(vec![1i32, 2, 3, 4, 5, 6])).into();
4541 let sum = in_unbounded.fold(
4542 q!(|| 0),
4543 q!(
4544 |sum, v| {
4545 *sum += v;
4546 },
4547 monotone = manual_proof!()
4548 ),
4549 );
4550
4551 let threshold_out = sum
4552 .threshold_greater_or_equal(node.singleton(q!(7)))
4553 .send_bincode_external(&external);
4554
4555 let nodes = flow
4556 .with_process(&node, deployment.Localhost())
4557 .with_external(&external, deployment.Localhost())
4558 .deploy(&mut deployment);
4559
4560 deployment.deploy().await.unwrap();
4561
4562 let mut threshold_out = nodes.connect(threshold_out).await;
4563
4564 deployment.start().await.unwrap();
4565
4566 assert_eq!(threshold_out.next().await.unwrap(), 7);
4567 }
4568
4569 #[cfg(feature = "deploy")]
4570 #[tokio::test]
4571 async fn monotone_count_threshold() {
4572 let mut deployment = Deployment::new();
4573
4574 let mut flow = FlowBuilder::new();
4575 let node = flow.process::<()>();
4576 let external = flow.external::<()>();
4577
4578 let in_unbounded: super::Stream<_, _> =
4579 node.source_iter(q!(vec![1i32, 2, 3, 4, 5, 6])).into();
4580 let sum = in_unbounded.count();
4581
4582 let threshold_out = sum
4583 .threshold_greater_or_equal(node.singleton(q!(3)))
4584 .send_bincode_external(&external);
4585
4586 let nodes = flow
4587 .with_process(&node, deployment.Localhost())
4588 .with_external(&external, deployment.Localhost())
4589 .deploy(&mut deployment);
4590
4591 deployment.deploy().await.unwrap();
4592
4593 let mut threshold_out = nodes.connect(threshold_out).await;
4594
4595 deployment.start().await.unwrap();
4596
4597 assert_eq!(threshold_out.next().await.unwrap(), 3);
4598 }
4599
4600 #[cfg(feature = "deploy")]
4601 #[tokio::test]
4602 async fn monotone_map_order_preserving_threshold() {
4603 use crate::properties::manual_proof;
4604
4605 let mut deployment = Deployment::new();
4606
4607 let mut flow = FlowBuilder::new();
4608 let node = flow.process::<()>();
4609 let external = flow.external::<()>();
4610
4611 let in_unbounded: super::Stream<_, _> =
4612 node.source_iter(q!(vec![1i32, 2, 3, 4, 5, 6])).into();
4613 let sum = in_unbounded.fold(
4614 q!(|| 0),
4615 q!(
4616 |sum, v| {
4617 *sum += v;
4618 },
4619 monotone = manual_proof!()
4620 ),
4621 );
4622
4623 let doubled = sum.map(q!(
4625 |v| v * 2,
4626 order_preserving = manual_proof!()
4627 ));
4628
4629 let threshold_out = doubled
4630 .threshold_greater_or_equal(node.singleton(q!(14)))
4631 .send_bincode_external(&external);
4632
4633 let nodes = flow
4634 .with_process(&node, deployment.Localhost())
4635 .with_external(&external, deployment.Localhost())
4636 .deploy(&mut deployment);
4637
4638 deployment.deploy().await.unwrap();
4639
4640 let mut threshold_out = nodes.connect(threshold_out).await;
4641
4642 deployment.start().await.unwrap();
4643
4644 assert_eq!(threshold_out.next().await.unwrap(), 14);
4645 }
4646
4647 #[cfg(any(feature = "deploy", feature = "sim"))]
4650 mod join_ordering_type_tests {
4651 use crate::live_collections::boundedness::{Bounded, Unbounded};
4652 use crate::live_collections::stream::{ExactlyOnce, NoOrder, Stream, TotalOrder};
4653 use crate::location::{Location, Process};
4654
4655 #[expect(dead_code, reason = "compile-time type test")]
4656 fn join_unbounded_with_bounded_preserves_order<'a>(
4657 left: Stream<(i32, char), Process<'a>, Unbounded, TotalOrder, ExactlyOnce>,
4658 right: Stream<(i32, char), Process<'a>, Bounded, TotalOrder, ExactlyOnce>,
4659 ) -> Stream<(i32, (char, char)), Process<'a>, Unbounded, TotalOrder, ExactlyOnce> {
4660 left.join(right)
4661 }
4662
4663 #[expect(dead_code, reason = "compile-time type test")]
4664 fn join_unbounded_with_unbounded_is_no_order<'a>(
4665 left: Stream<(i32, char), Process<'a>, Unbounded, TotalOrder, ExactlyOnce>,
4666 right: Stream<(i32, char), Process<'a>, Unbounded, TotalOrder, ExactlyOnce>,
4667 ) -> Stream<(i32, (char, char)), Process<'a>, Unbounded, NoOrder, ExactlyOnce> {
4668 left.join(right)
4669 }
4670
4671 #[expect(dead_code, reason = "compile-time type test")]
4672 fn join_bounded_with_bounded_preserves_order<'a, L: Location<'a>>(
4673 left: Stream<(i32, char), L, Bounded, TotalOrder, ExactlyOnce>,
4674 right: Stream<(i32, char), L, Bounded, TotalOrder, ExactlyOnce>,
4675 ) -> Stream<(i32, (char, char)), L, Bounded, TotalOrder, ExactlyOnce> {
4676 left.join(right)
4677 }
4678
4679 #[expect(dead_code, reason = "compile-time type test")]
4680 fn join_unbounded_noorder_with_bounded<'a>(
4681 left: Stream<(i32, char), Process<'a>, Unbounded, NoOrder, ExactlyOnce>,
4682 right: Stream<(i32, char), Process<'a>, Bounded, NoOrder, ExactlyOnce>,
4683 ) -> Stream<(i32, (char, char)), Process<'a>, Unbounded, NoOrder, ExactlyOnce> {
4684 left.join(right)
4685 }
4686
4687 #[expect(dead_code, reason = "compile-time type test")]
4690 fn cross_product_unbounded_with_bounded_preserves_order<'a>(
4691 left: Stream<i32, Process<'a>, Unbounded, TotalOrder, ExactlyOnce>,
4692 right: Stream<char, Process<'a>, Bounded, TotalOrder, ExactlyOnce>,
4693 ) -> Stream<(i32, char), Process<'a>, Unbounded, TotalOrder, ExactlyOnce> {
4694 left.cross_product(right)
4695 }
4696
4697 #[expect(dead_code, reason = "compile-time type test")]
4698 fn cross_product_bounded_with_bounded_preserves_order<'a>(
4699 left: Stream<i32, Process<'a>, Bounded, TotalOrder, ExactlyOnce>,
4700 right: Stream<char, Process<'a>, Bounded, TotalOrder, ExactlyOnce>,
4701 ) -> Stream<(i32, char), Process<'a>, Bounded, TotalOrder, ExactlyOnce> {
4702 left.cross_product(right)
4703 }
4704
4705 #[expect(dead_code, reason = "compile-time type test")]
4706 fn cross_product_unbounded_with_unbounded_is_no_order<'a>(
4707 left: Stream<i32, Process<'a>, Unbounded, TotalOrder, ExactlyOnce>,
4708 right: Stream<char, Process<'a>, Unbounded, TotalOrder, ExactlyOnce>,
4709 ) -> Stream<(i32, char), Process<'a>, Unbounded, NoOrder, ExactlyOnce> {
4710 left.cross_product(right)
4711 }
4712 } #[cfg(feature = "sim")]
4717 #[test]
4718 fn cross_product_mixed_boundedness_correctness() {
4719 use stageleft::q;
4720
4721 use crate::compile::builder::FlowBuilder;
4722 use crate::nondet::nondet;
4723
4724 let mut flow = FlowBuilder::new();
4725 let process = flow.process::<()>();
4726 let tick = process.tick();
4727
4728 let left = process.source_iter(q!(vec![1, 2]));
4729 let right = process
4730 .source_iter(q!(vec!['a', 'b']))
4731 .batch(&tick, nondet!())
4732 .all_ticks();
4733
4734 let out = left.cross_product(right).sim_output();
4735
4736 flow.sim().exhaustive(async || {
4737 out.assert_yields_only_unordered(vec![(1, 'a'), (1, 'b'), (2, 'a'), (2, 'b')])
4738 .await;
4739 });
4740 }
4741
4742 #[cfg(feature = "sim")]
4743 #[test]
4744 fn join_mixed_boundedness_correctness() {
4745 use stageleft::q;
4746
4747 use crate::compile::builder::FlowBuilder;
4748 use crate::nondet::nondet;
4749
4750 let mut flow = FlowBuilder::new();
4751 let process = flow.process::<()>();
4752 let tick = process.tick();
4753
4754 let left = process.source_iter(q!(vec![(1, 'a'), (2, 'b')]));
4755 let right = process
4756 .source_iter(q!(vec![(1, 'x'), (2, 'y')]))
4757 .batch(&tick, nondet!())
4758 .all_ticks();
4759
4760 let out = left.join(right).sim_output();
4761
4762 flow.sim().exhaustive(async || {
4763 out.assert_yields_only_unordered(vec![(1, ('a', 'x')), (2, ('b', 'y'))])
4764 .await;
4765 });
4766 }
4767
4768 #[cfg(feature = "sim")]
4769 #[test]
4770 fn sim_merge_unordered_independent_atomics() {
4771 let mut flow = FlowBuilder::new();
4772 let node = flow.process::<()>();
4773
4774 let (in1_send, input1) = node.sim_input::<_, TotalOrder, _>();
4775 let (in2_send, input2) = node.sim_input::<_, TotalOrder, _>();
4776
4777 let out = input1
4778 .atomic()
4779 .merge_unordered(input2.atomic())
4780 .end_atomic()
4781 .sim_output();
4782
4783 flow.sim().exhaustive(async || {
4784 in1_send.send(1);
4785 in2_send.send(2);
4786
4787 out.assert_yields_only_unordered(vec![1, 2]).await;
4788 });
4789 }
4790
4791 #[cfg(feature = "deploy")]
4792 #[tokio::test]
4793 async fn test_stream_ref() {
4794 let mut deployment = Deployment::new();
4795
4796 let mut flow = FlowBuilder::new();
4797 let external = flow.external::<()>();
4798 let p1 = flow.process::<()>();
4799
4800 let my_stream = p1.source_iter(q!(1..=5i32));
4802
4803 let stream_ref = my_stream.by_ref();
4804
4805 let out_port = p1
4807 .source_iter(q!([()]))
4808 .map(q!(|_| stream_ref.len() as i32))
4809 .send_bincode_external(&external);
4810
4811 my_stream.for_each(q!(|_| {}));
4813
4814 let nodes = flow
4815 .with_default_optimize()
4816 .with_process(&p1, deployment.Localhost())
4817 .with_external(&external, deployment.Localhost())
4818 .deploy(&mut deployment);
4819
4820 deployment.deploy().await.unwrap();
4821
4822 let mut out_recv = nodes.connect(out_port).await;
4823
4824 deployment.start().await.unwrap();
4825
4826 let result = out_recv.next().await.unwrap();
4827 assert_eq!(result, 5);
4829 }
4830
4831 #[cfg(feature = "deploy")]
4832 #[tokio::test]
4833 async fn test_stream_ref_contents() {
4834 let mut deployment = Deployment::new();
4835
4836 let mut flow = FlowBuilder::new();
4837 let external = flow.external::<()>();
4838 let p1 = flow.process::<()>();
4839
4840 let my_stream = p1.source_iter(q!(1..=3i32));
4842
4843 let stream_ref = my_stream.by_ref();
4844
4845 let out_port = p1
4847 .source_iter(q!([()]))
4848 .map(q!(|_| stream_ref.iter().sum::<i32>()))
4849 .send_bincode_external(&external);
4850
4851 my_stream.for_each(q!(|_| {}));
4852
4853 let nodes = flow
4854 .with_default_optimize()
4855 .with_process(&p1, deployment.Localhost())
4856 .with_external(&external, deployment.Localhost())
4857 .deploy(&mut deployment);
4858
4859 deployment.deploy().await.unwrap();
4860
4861 let mut out_recv = nodes.connect(out_port).await;
4862
4863 deployment.start().await.unwrap();
4864
4865 let result = out_recv.next().await.unwrap();
4866 assert_eq!(result, 6);
4868 }
4869
4870 #[cfg(feature = "deploy")]
4871 #[tokio::test]
4872 async fn test_stream_ref_no_consumer() {
4873 let mut deployment = Deployment::new();
4874
4875 let mut flow = FlowBuilder::new();
4876 let external = flow.external::<()>();
4877 let p1 = flow.process::<()>();
4878
4879 let my_stream = p1.source_iter(q!(1..=4i32));
4881
4882 let stream_ref = my_stream.by_ref();
4883
4884 let out_port = p1
4885 .source_iter(q!([()]))
4886 .map(q!(|_| stream_ref.len() as i32))
4887 .send_bincode_external(&external);
4888
4889 let nodes = flow
4890 .with_default_optimize()
4891 .with_process(&p1, deployment.Localhost())
4892 .with_external(&external, deployment.Localhost())
4893 .deploy(&mut deployment);
4894
4895 deployment.deploy().await.unwrap();
4896
4897 let mut out_recv = nodes.connect(out_port).await;
4898
4899 deployment.start().await.unwrap();
4900
4901 let result = out_recv.next().await.unwrap();
4902 assert_eq!(result, 4);
4903 }
4904
4905 #[cfg(feature = "deploy")]
4906 #[tokio::test]
4907 async fn test_stream_mut() {
4908 let mut deployment = Deployment::new();
4909
4910 let mut flow = FlowBuilder::new();
4911 let external = flow.external::<()>();
4912 let p1 = flow.process::<()>();
4913
4914 let my_stream = p1.source_iter(q!(1..=5i32));
4916
4917 let stream_mut = my_stream.by_mut();
4918
4919 let out_port = p1
4921 .source_iter(q!([()]))
4922 .map(q!(|_| {
4923 stream_mut.retain(|x| *x > 3);
4924 stream_mut.len() as i32
4925 }))
4926 .send_bincode_external(&external);
4927
4928 my_stream.for_each(q!(|_| {}));
4929
4930 let nodes = flow
4931 .with_default_optimize()
4932 .with_process(&p1, deployment.Localhost())
4933 .with_external(&external, deployment.Localhost())
4934 .deploy(&mut deployment);
4935
4936 deployment.deploy().await.unwrap();
4937
4938 let mut out_recv = nodes.connect(out_port).await;
4939
4940 deployment.start().await.unwrap();
4941
4942 let result = out_recv.next().await.unwrap();
4943 assert_eq!(result, 2);
4945 }
4946
4947 #[cfg(feature = "sim")]
4951 #[test]
4952 fn sim_map_with_mut_on_unordered_explores_multiple_states() {
4953 use crate::live_collections::sliced::sliced;
4954 use crate::live_collections::stream::ExactlyOnce;
4955 use crate::properties::manual_proof;
4956
4957 let mut flow = FlowBuilder::new();
4958 let node = flow.process::<()>();
4959
4960 let (trigger_send, trigger) = node.sim_input::<i32, TotalOrder, ExactlyOnce>();
4961
4962 let out_recv = sliced! {
4963 let batch = use::batch(trigger, nondet!());
4964 let counter = batch.location().source_iter(q!(vec![0i32]))
4965 .fold(q!(|| 0i32), q!(|acc, v| *acc += v));
4966 let counter_mut = counter.by_mut();
4967 let items = batch.location().source_iter(q!(vec![1i32, 2])).weaken_ordering::<NoOrder>();
4968 items.map(q!(
4969 |x| {
4970 *counter_mut += x;
4971 *counter_mut
4972 },
4973 commutative = manual_proof!()
4974 ))
4975 }
4976 .sim_output();
4977
4978 let count = flow.sim().exhaustive(async || {
4979 trigger_send.send(1);
4980 let _all: Vec<i32> = out_recv.collect_sorted().await;
4981 });
4982
4983 assert_eq!(
4984 count, 2,
4985 "Expected 2 simulation instances due to mut on unordered input, got {}",
4986 count
4987 );
4988 }
4989
4990 #[cfg(feature = "sim")]
4994 #[test]
4995 fn sim_scan_with_ref_capture() {
4996 use crate::live_collections::sliced::sliced;
4997 use crate::live_collections::stream::ExactlyOnce;
4998
4999 let mut flow = FlowBuilder::new();
5000 let node = flow.process::<()>();
5001
5002 let (trigger_send, trigger) = node.sim_input::<i32, TotalOrder, ExactlyOnce>();
5003
5004 let out_recv = sliced! {
5005 let batch = use::batch(trigger, nondet!());
5006 let offset = batch
5007 .location()
5008 .source_iter(q!(vec![10i32]))
5009 .fold(q!(|| 0i32), q!(|acc, v| *acc += v));
5010 let offset_ref = offset.by_ref();
5011 batch
5012 .location()
5013 .source_iter(q!(vec![1i32, 2, 3]))
5014 .scan(
5015 q!(|| 0i32),
5016 q!(move |acc: &mut i32, x| {
5017 *acc += x + *offset_ref;
5018 Some(*acc)
5019 }),
5020 )
5021 }
5022 .sim_output();
5023
5024 let count = flow.sim().exhaustive(async || {
5025 trigger_send.send(1);
5026 let all: Vec<i32> = out_recv.collect().await;
5027 assert_eq!(all, vec![11, 23, 36]);
5032 });
5033
5034 assert_eq!(
5035 count, 1,
5036 "Expected a single simulation instance for a totally-ordered scan, got {}",
5037 count
5038 );
5039 }
5040
5041 #[cfg(feature = "sim")]
5045 #[test]
5046 #[ignore = "observe_nondet not yet supported for top-level bounded inputs (https://github.com/hydro-project/hydro/issues/2950)"]
5047 fn sim_map_with_mut_on_unordered_top_level() {
5048 use crate::properties::manual_proof;
5049
5050 let mut flow = FlowBuilder::new();
5051 let node = flow.process::<()>();
5052
5053 let counter = node
5054 .source_iter(q!(vec![0i32]))
5055 .fold(q!(|| 0i32), q!(|acc, v| *acc += v));
5056 let counter_mut = counter.by_mut();
5057
5058 let out_recv = node
5059 .source_iter(q!(vec![1i32, 2]))
5060 .weaken_ordering::<NoOrder>()
5061 .map(q!(
5062 |x| {
5063 *counter_mut += x;
5064 *counter_mut
5065 },
5066 commutative = manual_proof!()
5067 ))
5068 .assume_ordering::<TotalOrder>(nondet!())
5069 .sim_output();
5070
5071 counter.into_stream().for_each(q!(|_| {}));
5072
5073 let count = flow.sim().exhaustive(async || {
5074 let _all: Vec<i32> = out_recv.collect().await;
5075 });
5076
5077 assert_eq!(
5078 count, 2,
5079 "Expected 2 simulation instances due to mut on unordered input, got {}",
5080 count
5081 );
5082 }
5083}