Skip to content

Commit 47f44a6

Browse files
committed
🚧 Test cycling iterator instead.
1 parent a7d2f5f commit 47f44a6

File tree

1 file changed

+126
-98
lines changed

1 file changed

+126
-98
lines changed

src/cartesian_power.rs

Lines changed: 126 additions & 98 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
use alloc::vec::Vec;
22
use std::fmt;
3-
use std::iter::FusedIterator;
43

54
/// An adaptor iterating through all the ordered `n`-length lists of items
65
/// yielded by the underlying iterator, including repetitions.
@@ -15,9 +14,19 @@ where
1514
I::Item: Clone,
1615
{
1716
pow: usize,
18-
iter: Option<I>, // Inner iterator. Forget once consumed after 'base' iterations.
19-
items: Vec<I::Item>, // Fill from iter. Clear once adaptor is exhausted. Final length is 'base'.
20-
indices: Vec<usize>, // Indices just yielded. Clear once adaptor is exhausted. Length is 'pow'.
17+
iter: Option<I>, // Inner iterator. Forget once consumed after 'base' iterations.
18+
items: Option<Vec<I::Item>>, // Fill from iter. Final length is 'base'.
19+
// None means that collection has not started yet.
20+
// Some(empty) means that collection would have started but pow = 0.
21+
22+
// Indices just yielded. Length is 'pow'.
23+
// 0 0 .. 0 0 means that the first combination has been yielded.
24+
// 0 0 .. 0 1 means that the second combination has been yielded.
25+
// m m .. m m means that the last combination has just been yielded (m = base - 1).
26+
// b 0 .. 0 0 means that 'None' has just been yielded (b = base).
27+
// The latter is a special value marking the renewal of the iterator,
28+
// which can cycle again through another full round, ad libitum.
29+
indices: Vec<usize>,
2130
}
2231

2332
/// Create a new `CartesianPower` from an iterator of clonables.
@@ -29,7 +38,7 @@ where
2938
CartesianPower {
3039
pow,
3140
iter: Some(iter),
32-
items: Vec::new(),
41+
items: None,
3342
indices: Vec::new(),
3443
}
3544
}
@@ -53,70 +62,99 @@ where
5362
items,
5463
indices,
5564
} = self;
56-
match (*pow, iter, items.len()) {
57-
// Final stable state: underlying iterator and items forgotten.
58-
(_, None, 0) => None,
65+
println!(
66+
"^{pow}: {} {indices:?}\t\t{:?}",
67+
if iter.is_some() { 'S' } else { 'N' },
68+
items.as_ref().map(Vec::len)
69+
);
5970

60-
// Degenerated 0th power iteration.
61-
(0, Some(_), _) => {
62-
self.iter = None; // Forget without even consuming.
63-
Some((indices, items))
71+
// (weird 'items @' bindings circumvent NLL limitations, unneeded with polonius)
72+
match (*pow, iter, &mut *items) {
73+
// First iteration with degenerated 0th power.
74+
(0, Some(_), items @ None) => {
75+
self.iter = None; // Forget about underlying iteration immediately.
76+
let empty = items.insert(Vec::new()); // Raise this value as a boolean flag.
77+
Some((indices, empty)) // Yield empty list.
6478
}
6579

66-
(pow, Some(it), 0) => {
80+
// Subsequent degenerated 0th power iteration.
81+
// Use the Some<(empty)Vec> as a flag to alternate between yielding [] or None.
82+
(0, None, items @ Some(_)) => {
83+
*items = None;
84+
None
85+
}
86+
(0, None, items @ None) => Some((indices, items.insert(Vec::new()))),
87+
88+
// First iteration in the general case.
89+
(pow, Some(it), items @ None) => {
6790
// Check whether there is at least one element in the iterator.
6891
if let Some(first) = it.next() {
69-
// Allocate buffer to hold items about to be yielded.
70-
items.reserve_exact(it.size_hint().0);
71-
items.push(first);
72-
// Same for indices to be yielded.
92+
items // Collect it.
93+
.insert(Vec::with_capacity(it.size_hint().0))
94+
.push(first);
95+
// Prepare indices to be yielded.
7396
indices.reserve_exact(pow);
7497
for _ in 0..pow {
7598
indices.push(0);
7699
}
77-
return Some((indices, items));
100+
Some((indices, items.as_ref().unwrap()))
101+
} else {
102+
// Degenerated iteration over an empty set:
103+
// 'base = 0', yet with non-null power.
104+
self.iter = None;
105+
None
78106
}
79-
// Degenerated iteration over an empty set, yet with non-null power.
80-
self.iter = None;
81-
None
82107
}
83108

84-
(pow, Some(it), base) => {
109+
// Stable iteration in the degenerated case 'base = 0'.
110+
(_, None, None) => None,
111+
112+
// Subsequent iteration in the general case.
113+
(pow, Some(it), Some(items)) => {
85114
// We are still unsure whether all items have been collected.
86-
// As a consequence, 'base' is still uncertain,
115+
// As a consequence, the exact value of 'base' is still uncertain,
87116
// but then we know that indices haven't started wrapping around yet.
88117
if let Some(next) = it.next() {
89118
items.push(next);
90119
indices[pow - 1] += 1;
91120
return Some((indices, items));
92121
}
93122

94-
// All items have just been collected.
123+
// The item collected on previous call was the last one.
95124
self.iter = None;
125+
let base = items.len(); // Definitive 'base' value.
96126
if base == 1 || pow == 1 {
97-
// End of iteration.
98-
items.clear();
99-
indices.clear();
127+
// Early end of singleton iteration.
128+
indices[0] = base; // Mark to cycle again on next iteration.
100129
return None;
101130
}
102131

103132
// First wrap around.
104133
indices[pow - 1] = 0;
105-
indices[pow - 2] += 1;
134+
indices[pow - 2] = 1;
106135
Some((indices, items))
107136
}
108137

109-
(_, None, b) => {
138+
// Subsequent iteration in the general case after all items have been collected.
139+
(_, None, Some(items)) => {
140+
let base = items.len();
141+
if indices[0] == base {
142+
// Special marker that iteration can start over for a new round.
143+
indices[0] = 0;
144+
return Some((indices, items));
145+
}
110146
// Keep yielding items list, incrementing indices rightmost first.
111147
for index in indices.iter_mut().rev() {
112148
*index += 1;
113-
if *index < b {
149+
if *index < base {
114150
return Some((indices, items));
115151
}
116152
*index = 0; // Wrap and increment left.
117153
}
118-
items.clear();
119-
indices.clear();
154+
// Iteration is over.
155+
// Mark a special index value to not fuse the iterator
156+
// and make it possible to cycle through all results again.
157+
indices[0] = base;
120158
None
121159
}
122160
}
@@ -187,13 +225,6 @@ where
187225
}
188226
}
189227

190-
impl<I> FusedIterator for CartesianPower<I>
191-
where
192-
I: Iterator,
193-
I::Item: Clone,
194-
{
195-
}
196-
197228
#[cfg(test)]
198229
mod tests {
199230
//! Use chars and string to ease testing of every yielded iterator values.
@@ -202,41 +233,50 @@ mod tests {
202233
use crate::Itertools;
203234
use core::str::Chars;
204235

205-
fn check_fused(mut exhausted_it: CartesianPower<Chars>, context: String) {
206-
for i in 0..100 {
207-
let act = exhausted_it.next();
208-
assert!(
209-
act.is_none(),
210-
"Iteration {} after expected exhaustion of {} \
211-
yielded {:?} instead of None. ",
212-
i,
213-
context,
214-
act,
215-
);
216-
}
217-
}
218-
219236
#[test]
220237
fn basic() {
221238
fn check(origin: &str, pow: usize, expected: &[&str]) {
222-
let mut it = origin.chars().cartesian_power(pow);
223-
let mut i = 0;
224-
for exp in expected {
225-
let act = it.next();
226-
if act != Some(exp.chars().collect()) {
227-
panic!(
228-
"Failed iteration {} for {:?}^{}. \
229-
Expected {:?}, got {:?} instead.",
230-
i, origin, pow, exp, act,
231-
);
239+
println!("================== ({origin:?}^{pow})");
240+
let mut it_act = origin.chars().cartesian_power(pow);
241+
// Check thrice that it's cycling.
242+
for r in 1..=3 {
243+
println!("- - {r} - - - - - -");
244+
let mut it_exp = expected.iter();
245+
let mut i = 0;
246+
loop {
247+
match (it_exp.next(), it_act.next()) {
248+
(Some(exp), Some(act)) => {
249+
if act != exp.chars().collect::<Vec<_>>() {
250+
panic!(
251+
"Failed iteration {} (repetition {}) for {:?}^{}. \
252+
Expected {:?}, got {:?} instead.",
253+
i, r, origin, pow, exp, act,
254+
);
255+
}
256+
i += 1;
257+
}
258+
(None, Some(act)) => {
259+
panic!(
260+
"Failed iteration {} (repetition {}) for {:?}^{}. \
261+
Expected None, got {:?} instead.",
262+
i, r, origin, pow, act,
263+
);
264+
}
265+
(Some(exp), None) => {
266+
panic!(
267+
"Failed iteration {} (repetition {}) for {:?}^{}. \
268+
Expected {:?}, got None instead.",
269+
i, r, origin, pow, exp,
270+
);
271+
}
272+
(None, None) => break,
273+
}
232274
}
233-
i += 1;
234275
}
235-
check_fused(it, format!("iteration {} or {:?}^{}", i, origin, pow));
236276
}
237277

238278
// Empty underlying iterator.
239-
check("", 0, &[""]);
279+
check("", 0, &[""]); // 0^0 = 1.
240280
check("", 1, &[]);
241281
check("", 2, &[]);
242282
check("", 3, &[]);
@@ -281,42 +321,30 @@ mod tests {
281321
fn check(origin: &str, pow: usize, expected: &[(usize, Option<&str>)]) {
282322
let mut it = origin.chars().cartesian_power(pow);
283323
let mut total_n = Vec::new();
284-
for &(n, exp) in expected {
285-
let act = it.nth(n);
286-
total_n.push(n);
287-
if act != exp.map(|s| s.chars().collect::<Vec<_>>()) {
288-
panic!(
289-
"Failed nth({}) iteration for {:?}^{}. \
290-
Expected {:?}, got {:?} instead.",
291-
total_n
292-
.iter()
293-
.map(ToString::to_string)
294-
.collect::<Vec<_>>()
295-
.join(", "),
296-
origin,
297-
pow,
298-
exp,
299-
act,
300-
);
324+
for r in 1..=3 {
325+
for &(n, exp) in expected {
326+
let act = it.nth(n);
327+
total_n.push(n);
328+
if act != exp.map(|s| s.chars().collect::<Vec<_>>()) {
329+
panic!(
330+
"Failed nth({}) iteration (repetition {}) for {:?}^{}. \
331+
Expected {:?}, got {:?} instead.",
332+
total_n
333+
.iter()
334+
.map(ToString::to_string)
335+
.collect::<Vec<_>>()
336+
.join(", "),
337+
r,
338+
origin,
339+
pow,
340+
exp,
341+
act
342+
);
343+
}
301344
}
302345
}
303-
check_fused(
304-
it,
305-
format!(
306-
"nth({}) iteration of {:?}^{}",
307-
total_n
308-
.iter()
309-
.map(ToString::to_string)
310-
.collect::<Vec<_>>()
311-
.join(", "),
312-
origin,
313-
pow
314-
),
315-
);
316346
}
317347

318-
// HERE: make it work with the new implementation.
319-
320348
// Check degenerated cases.
321349
check("", 0, &[(0, Some("")), (0, None)]);
322350
check("", 0, &[(0, Some("")), (1, None)]);

0 commit comments

Comments
 (0)