Skip to content

Commit 6273611

Browse files
committed
🚧 Implement .nth() with cycling.
1 parent 47f44a6 commit 6273611

File tree

1 file changed

+219
-59
lines changed

1 file changed

+219
-59
lines changed

src/cartesian_power.rs

+219-59
Original file line numberDiff line numberDiff line change
@@ -144,12 +144,8 @@ where
144144
return Some((indices, items));
145145
}
146146
// Keep yielding items list, incrementing indices rightmost first.
147-
for index in indices.iter_mut().rev() {
148-
*index += 1;
149-
if *index < base {
150-
return Some((indices, items));
151-
}
152-
*index = 0; // Wrap and increment left.
147+
if Self::inbounds_increment(indices, base) {
148+
return Some((indices, items));
153149
}
154150
// Iteration is over.
155151
// Mark a special index value to not fuse the iterator
@@ -160,9 +156,176 @@ where
160156
}
161157
}
162158

159+
/// Increment indices, returning false in case of overflow.
160+
fn inbounds_increment(indices: &mut [usize], base: usize) -> bool {
161+
for index in indices.iter_mut().rev() {
162+
*index += 1;
163+
if *index < base {
164+
return true;
165+
}
166+
*index = 0; // Wrap and increment left.
167+
}
168+
false
169+
}
170+
171+
/// Increment indices by n, returning false in case of (saturating) overflow.
172+
fn inbounds_increment_by(n: usize, indices: &mut [usize], base: usize) -> bool {
173+
let mut q = n;
174+
for index in indices.iter_mut().rev() {
175+
q = (*index + q) / base;
176+
*index = (*index + n) % base;
177+
if q == 0 {
178+
return true;
179+
}
180+
}
181+
// Saturation requires a second pass to reset all indices.
182+
for index in indices.iter_mut() {
183+
*index = 0;
184+
}
185+
false
186+
}
187+
163188
/// Same as [`increment_indices`], but does n increments at once.
189+
/// The iterator is cycling, but `.nth()` does not 'wrap'
190+
/// and 'saturates' to None instead.
191+
#[allow(clippy::too_many_lines)] // HERE: fix when tests pass.
164192
fn increment_indices_by_n(&mut self, n: usize) -> Option<(&[usize], &[I::Item])> {
165-
todo!()
193+
let Self {
194+
pow,
195+
iter,
196+
items,
197+
indices,
198+
} = self;
199+
print!(
200+
"^{pow}: +{n} {} {indices:?}\t{:?}\t-> ",
201+
if iter.is_some() { 'S' } else { 'N' },
202+
items.as_ref().map(Vec::len)
203+
);
204+
205+
match (*pow, iter, &mut *items, n) {
206+
// First iteration with degenerated 0th power.
207+
(0, Some(_), items @ None, 0) => {
208+
println!("AAA");
209+
// Same as .next().
210+
self.iter = None;
211+
let empty = items.insert(Vec::new());
212+
Some((indices, empty))
213+
}
214+
(0, Some(_), None, _) => {
215+
println!("BBB");
216+
// Saturate.
217+
self.iter = None;
218+
None
219+
}
220+
221+
// Subsequent degenerated 0th power iteration.
222+
// Same as `.next()`.
223+
(0, None, items @ None, 0) => {
224+
println!("CCC");
225+
Some((indices, items.insert(Vec::new())))
226+
}
227+
// Saturate.
228+
(0, None, items, _) => {
229+
println!("DDD");
230+
*items = None;
231+
None
232+
}
233+
234+
// First iterations in the general case.
235+
// Possibly this will consume the entire underlying iterator,
236+
// but we need to consume to check.
237+
(pow, Some(it), items @ None, mut remaining) => {
238+
println!("EEE");
239+
if let Some(first) = it.next() {
240+
// There is at least one element in the iterator, prepare collection + indices.
241+
let items = items.insert(Vec::with_capacity(it.size_hint().0));
242+
items.push(first);
243+
indices.reserve_exact(pow);
244+
for _ in 0..pow {
245+
indices.push(0);
246+
}
247+
// Collect more.
248+
loop {
249+
if remaining == 0 {
250+
// Stop before collection completion.
251+
indices[pow - 1] = n; // Hasn't wrapped yet.
252+
return Some((indices, items));
253+
}
254+
if let Some(next) = it.next() {
255+
items.push(next);
256+
remaining -= 1;
257+
continue;
258+
}
259+
// Collection completed, but we need to go further.
260+
self.iter = None;
261+
let base = items.len();
262+
if Self::inbounds_increment_by(n, indices, base) {
263+
return Some((indices, items));
264+
}
265+
// Immediate saturation.
266+
indices[0] = base;
267+
return None;
268+
}
269+
} else {
270+
// Degenerated iteration over an empty set.
271+
self.iter = None;
272+
None
273+
}
274+
}
275+
276+
// Stable iteration in the degenerated case 'base = 0'.
277+
(_, None, None, _) => {
278+
println!("FFF");
279+
None
280+
}
281+
282+
// Subsequent iteration in the general case.
283+
// Again, immediate saturation is an option.
284+
(pow, Some(it), Some(items), mut remaining) => {
285+
println!("GGG");
286+
if let Some(next) = it.next() {
287+
items.push(next);
288+
loop {
289+
if remaining == 0 {
290+
indices[pow - 1] += n; // Hasn't wrapped yet.
291+
return Some((indices, items));
292+
}
293+
if let Some(next) = it.next() {
294+
items.push(next);
295+
remaining -= 1;
296+
continue;
297+
}
298+
break;
299+
}
300+
}
301+
// Collection completed.
302+
self.iter = None;
303+
let base = items.len();
304+
if Self::inbounds_increment_by(n, indices, base) {
305+
return Some((indices, items));
306+
}
307+
// Saturate.
308+
indices[0] = base;
309+
None
310+
}
311+
312+
// Subsequent iteration in the general case
313+
// after all items have been collected.
314+
(_, None, Some(items), n) => {
315+
println!("HHH");
316+
let base = items.len();
317+
if indices[0] == base {
318+
// Start over for a new round.
319+
indices[0] = 0;
320+
}
321+
if Self::inbounds_increment_by(n, indices, base) {
322+
return Some((indices, items));
323+
}
324+
// Immediate re-saturation.
325+
indices[0] = base;
326+
None
327+
}
328+
}
166329
}
167330
}
168331

@@ -319,66 +482,63 @@ mod tests {
319482
#[test]
320483
fn nth() {
321484
fn check(origin: &str, pow: usize, expected: &[(usize, Option<&str>)]) {
485+
println!("================== ({origin:?}^{pow})");
322486
let mut it = origin.chars().cartesian_power(pow);
323487
let mut total_n = Vec::new();
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 {:?}^{}. \
488+
for &(n, exp) in expected {
489+
let act = it.nth(n);
490+
total_n.push(n);
491+
if act != exp.map(|s| s.chars().collect::<Vec<_>>()) {
492+
panic!(
493+
"Failed nth({}) iteration for {:?}^{}. \
331494
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-
}
495+
total_n
496+
.iter()
497+
.map(ToString::to_string)
498+
.collect::<Vec<_>>()
499+
.join(", "),
500+
origin,
501+
pow,
502+
exp,
503+
act
504+
);
344505
}
345506
}
346507
}
347508

348-
// Check degenerated cases.
349-
check("", 0, &[(0, Some("")), (0, None)]);
350-
check("", 0, &[(0, Some("")), (1, None)]);
351-
check("", 0, &[(0, Some("")), (2, None)]);
352-
check("", 0, &[(1, None), (0, None)]);
353-
check("", 0, &[(1, None), (1, None)]);
354-
check("", 0, &[(1, None), (2, None)]);
355-
check("", 0, &[(2, None), (0, None)]);
356-
check("", 0, &[(2, None), (1, None)]);
357-
check("", 0, &[(2, None), (2, None)]);
358-
359-
check("a", 0, &[(0, Some("")), (0, None)]);
360-
check("a", 0, &[(0, Some("")), (1, None)]);
361-
check("a", 0, &[(0, Some("")), (2, None)]);
362-
check("a", 0, &[(1, None), (0, None)]);
363-
check("a", 0, &[(1, None), (1, None)]);
364-
check("a", 0, &[(1, None), (2, None)]);
365-
check("a", 0, &[(2, None), (0, None)]);
366-
check("a", 0, &[(2, None), (1, None)]);
367-
check("a", 0, &[(2, None), (2, None)]);
509+
// Ease test read/write.
510+
macro_rules! check {
511+
($base:expr, $pow:expr => $( $n:literal $expected:expr)+ ) => {
512+
check($base, $pow, &[$(($n, $expected)),+]);
513+
};
514+
}
515+
516+
// Degenerated cases.
517+
for base in ["", "a", "ab"] {
518+
check!(base, 0 => 0 Some("") 0 None 0 Some("") 0 None );
519+
check!(base, 0 => 0 Some("") 1 None 0 Some("") 1 None );
520+
check!(base, 0 => 0 Some("") 2 None 1 None 0 Some(""));
521+
check!(base, 0 => 1 None 0 Some("") 0 None 1 None );
522+
check!(base, 0 => 1 None 1 None 0 Some("") 0 None );
523+
check!(base, 0 => 1 None 2 None 0 Some("") 1 None );
524+
check!(base, 0 => 2 None 0 Some("") 1 None 0 Some(""));
525+
check!(base, 0 => 2 None 1 None 2 None 0 Some(""));
526+
check!(base, 0 => 2 None 2 None 0 Some("") 2 None );
527+
}
368528

369529
// Unit power.
370-
check("a", 1, &[(0, Some("a")), (0, None)]);
371-
check("a", 1, &[(0, Some("a")), (1, None)]);
372-
check("a", 1, &[(0, Some("a")), (2, None)]);
373-
check("a", 1, &[(1, None), (0, None)]);
374-
check("a", 1, &[(1, None), (1, None)]);
375-
check("a", 1, &[(1, None), (2, None)]);
376-
check("a", 1, &[(2, None), (0, None)]);
377-
check("a", 1, &[(2, None), (1, None)]);
378-
check("a", 1, &[(2, None), (2, None)]);
379-
380-
check("ab", 1, &[(0, Some("a")), (0, Some("b")), (0, None)]);
381-
check("ab", 1, &[(1, Some("b")), (0, None), (0, None)]);
382-
check("ab", 1, &[(2, None), (0, None), (0, None)]);
530+
check!("a", 1 => 0 Some("a") 0 None 0 Some("a") 0 None ); // HERE: fix.
531+
check!("a", 1 => 0 Some("a") 1 None 0 Some("a") 1 None );
532+
check!("a", 1 => 0 Some("a") 2 None 1 None 0 Some("a"));
533+
check!("a", 1 => 1 None 0 Some("a") 0 None 1 None );
534+
check!("a", 1 => 1 None 1 None 0 Some("a") 0 None );
535+
check!("a", 1 => 1 None 2 None 0 Some("a") 1 None );
536+
check!("a", 1 => 2 None 0 Some("a") 1 None 0 Some("a"));
537+
check!("a", 1 => 2 None 1 None 2 None 0 Some("a"));
538+
check!("a", 1 => 2 None 2 None 0 Some("a") 2 None );
539+
540+
check!("ab", 1 => 0 Some("a") 0 Some("b") 0 None);
541+
check!("ab", 1 => 1 Some("b") 0 None 0 None);
542+
check!("ab", 1 => 2 None 0 None 0 None);
383543
}
384544
}

0 commit comments

Comments
 (0)