Skip to main content

moirai_iter/parallel/adapters/
pair.rs

1use super::super::{Consumer, ParallelIterator, VecParIter};
2
3fn interleave_all<T>(left: Vec<T>, right: Vec<T>) -> Vec<T> {
4    let len = left.len().checked_add(right.len()).expect("overflow");
5    let paired_len = left.len().min(right.len());
6    let mut left = left.into_iter();
7    let mut right = right.into_iter();
8    let mut output = Vec::with_capacity(len);
9
10    for _ in 0..paired_len {
11        output.push(left.next().expect("paired length is bounded by left input"));
12        output.push(
13            right
14                .next()
15                .expect("paired length is bounded by right input"),
16        );
17    }
18    output.extend(left);
19    output.extend(right);
20    output
21}
22
23fn interleave_shortest<T>(mut left: Vec<T>, mut right: Vec<T>) -> Vec<T> {
24    let left_take = if left.len() <= right.len() {
25        left.len()
26    } else {
27        right.len() + 1
28    };
29    let right_take = right.len().min(left.len());
30
31    let output_len = left_take.checked_add(right_take).expect("overflow");
32    left.truncate(left_take);
33    right.truncate(right_take);
34
35    let mut left = left.into_iter();
36    let mut right = right.into_iter();
37    let mut output = Vec::with_capacity(output_len);
38    for _ in 0..right_take {
39        output.push(left.next().expect("take count is bounded by left input"));
40        output.push(right.next().expect("take count is bounded by right input"));
41    }
42    output.extend(left);
43    output
44}
45
46/// Zip adapter with standard shortest-input value semantics.
47pub struct Zip<I, J> {
48    left: I,
49    right: J,
50}
51
52impl<I, J> Zip<I, J> {
53    pub(in crate::parallel) fn new(left: I, right: J) -> Self {
54        Self { left, right }
55    }
56}
57
58impl<I, J> ParallelIterator for Zip<I, J>
59where
60    I: ParallelIterator,
61    J: ParallelIterator,
62    I::Item: Sync + 'static,
63    J::Item: Sync + 'static,
64{
65    type Item = (I::Item, J::Item);
66
67    fn seq_items(self) -> Vec<Self::Item> {
68        self.left
69            .seq_items()
70            .into_iter()
71            .zip(self.right.seq_items())
72            .collect()
73    }
74
75    /// # Why this stays sequential (two sources, one split)
76    ///
77    /// Pairing needs both inputs split at the same logical positions. The
78    /// consumer protocol splits one source and hands the halves one consumer
79    /// each; it carries no way to divide a second, independently shaped
80    /// iterator in lockstep. Splitting the left input alone and re-driving the
81    /// right per shard would re-run the right input once per shard, which is a
82    /// different program, not a parallelisation of this one. `zip_eq`,
83    /// `interleave`, and `interleave_shortest` share this boundary; interleaving
84    /// additionally depends on alternation parity carrying across the shard
85    /// boundary.
86    fn drive<C, R>(self, consumer: C) -> R
87    where
88        C: Consumer<Self::Item, Result = R> + Send + Sync,
89        R: Send,
90    {
91        consumer.consume(VecParIter::new(self.seq_items()))
92    }
93}
94
95/// Zip adapter with Rayon-style equal-length value semantics.
96pub struct ZipEq<I, J> {
97    pub(in crate::parallel) left: I,
98    pub(in crate::parallel) right: J,
99}
100
101impl<I, J> ZipEq<I, J> {
102    pub(in crate::parallel) fn new(left: I, right: J) -> Self {
103        Self { left, right }
104    }
105}
106
107impl<I, J> ParallelIterator for ZipEq<I, J>
108where
109    I: ParallelIterator,
110    J: ParallelIterator,
111    I::Item: Sync + 'static,
112    J::Item: Sync + 'static,
113{
114    type Item = (I::Item, J::Item);
115
116    fn seq_items(self) -> Vec<Self::Item> {
117        let left = self.left.seq_items();
118        let right = self.right.seq_items();
119        assert_eq!(
120            left.len(),
121            right.len(),
122            "zip_eq requires equal input lengths"
123        );
124        left.into_iter().zip(right).collect()
125    }
126
127    /// # Why this stays sequential
128    ///
129    /// Two sources split in lockstep, per [`Zip`].
130    fn drive<C, R>(self, consumer: C) -> R
131    where
132        C: Consumer<Self::Item, Result = R> + Send + Sync,
133        R: Send,
134    {
135        consumer.consume(VecParIter::new(self.seq_items()))
136    }
137}
138
139/// Indexed interleave adapter with Rayon-style alternating value semantics.
140pub struct Interleave<I, J> {
141    pub(in crate::parallel) left: I,
142    pub(in crate::parallel) right: J,
143}
144
145impl<I, J> Interleave<I, J> {
146    pub(in crate::parallel) fn new(left: I, right: J) -> Self {
147        Self { left, right }
148    }
149}
150
151impl<I, J> ParallelIterator for Interleave<I, J>
152where
153    I: ParallelIterator,
154    J: ParallelIterator<Item = I::Item>,
155    I::Item: Sync + 'static,
156{
157    type Item = I::Item;
158
159    fn seq_items(self) -> Vec<Self::Item> {
160        interleave_all(self.left.seq_items(), self.right.seq_items())
161    }
162
163    /// # Why this stays sequential
164    ///
165    /// Two sources split in lockstep, per [`Zip`].
166    fn drive<C, R>(self, consumer: C) -> R
167    where
168        C: Consumer<Self::Item, Result = R> + Send + Sync,
169        R: Send,
170    {
171        consumer.consume(VecParIter::new(self.seq_items()))
172    }
173}
174
175/// Indexed interleave adapter that truncates at the shorter input boundary.
176pub struct InterleaveShortest<I, J> {
177    pub(in crate::parallel) left: I,
178    pub(in crate::parallel) right: J,
179}
180
181impl<I, J> InterleaveShortest<I, J> {
182    pub(in crate::parallel) fn new(left: I, right: J) -> Self {
183        Self { left, right }
184    }
185}
186
187impl<I, J> ParallelIterator for InterleaveShortest<I, J>
188where
189    I: ParallelIterator,
190    J: ParallelIterator<Item = I::Item>,
191    I::Item: Sync + 'static,
192{
193    type Item = I::Item;
194
195    fn seq_items(self) -> Vec<Self::Item> {
196        interleave_shortest(self.left.seq_items(), self.right.seq_items())
197    }
198
199    /// # Why this stays sequential
200    ///
201    /// Two sources split in lockstep, per [`Zip`].
202    fn drive<C, R>(self, consumer: C) -> R
203    where
204        C: Consumer<Self::Item, Result = R> + Send + Sync,
205        R: Send,
206    {
207        consumer.consume(VecParIter::new(self.seq_items()))
208    }
209}