Skip to content

Commit

Permalink
🚧 Test cycling iterator instead.
Browse files Browse the repository at this point in the history
  • Loading branch information
iago-lito committed Sep 19, 2024
1 parent 8957f70 commit 5b51535
Showing 1 changed file with 197 additions and 92 deletions.
289 changes: 197 additions & 92 deletions src/cartesian_power.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,58 @@
use alloc::vec::Vec;
use std::fmt;
use std::iter::FusedIterator;

// (base: 0) pow: 0
// iter indices items
// init S · N
// next(C) N 🗙 · [] -> []
// next(K) N · N -> None
// next(L) N · [] -> []

// (base: 2) pow: 0
// iter indices items
// init S · N
// next(C) N 🗙 · [] -> []
// next(K) N · N -> None
// next(L) N · [] -> []

// (base: 0) pow: 2
// iter indices items
// init S · N
// next(D) N! · N -> None
// next(B) N · N -> None
// next(B) N · N -> None

// (base: 1) pow: 1
// iter indices items
// init S · N
// next(D) S 0 a -> [a]
// next(E) N! 1 ! a -> None
// next(G) N 0 a -> [a]
// next(G) N 1 ! a -> None

// (base: 2) pow: 1
// iter indices items
// init S · N
// next(D) S 0 a -> [a]
// next(E) S 1 a b -> [b]
// next(E) N! 2 ! a b -> None
// next(G) N 0 a b -> [a]
// next(G) N 1 a b -> [b]
// next(G) N 2 ! a b -> None

// (base: 2) pow: 3
// iter indices items
// init S · N
// next(D) S 0 0 0 a -> [a, a, a]
// next(E) S 0 0 1 a b -> [a, a, b]
// next(E) N! 0 1 0 a b -> [a, b, a]
// next(G) N 0 1 1 a b -> [a, b, b]
// next(G) N 1 0 0 a b -> [b, a, a]
// next(G) N 1 0 1 a b -> [b, a, b]
// next(G) N 1 1 0 a b -> [b, b, a]
// next(G) N 1 1 1 a b -> [b, b, b]
// next(G) N 2 0 0 ! a b -> None
// next(G) N 0 0 0 a b -> [a, a, a]

/// An adaptor iterating through all the ordered `n`-length lists of items
/// yielded by the underlying iterator, including repetitions.
Expand All @@ -15,9 +67,19 @@ where
I::Item: Clone,
{
pow: usize,
iter: Option<I>, // Inner iterator. Forget once consumed after 'base' iterations.
items: Vec<I::Item>, // Fill from iter. Clear once adaptor is exhausted. Final length is 'base'.
indices: Vec<usize>, // Indices just yielded. Clear once adaptor is exhausted. Length is 'pow'.
iter: Option<I>, // Inner iterator. Forget once consumed after 'base' iterations.
items: Option<Vec<I::Item>>, // Fill from iter. Final length is 'base'.
// None means that collection has not started yet.
// Some(empty) means that collection would have started but pow = 0.

// Indices just yielded. Length is 'pow'.
// 0 0 .. 0 0 means that the first combination has been yielded.
// 0 0 .. 0 1 means that the second combination has been yielded.
// m m .. m m means that the last combination has just been yielded (m = base - 1).
// b 0 .. 0 0 means that 'None' has just been yielded (b = base).
// The latter is a special value marking the renewal of the iterator,
// which can cycle again through another full round, ad libitum.
indices: Vec<usize>,
}

/// Create a new `CartesianPower` from an iterator of clonables.
Expand All @@ -29,7 +91,7 @@ where
CartesianPower {
pow,
iter: Some(iter),
items: Vec::new(),
items: None,
indices: Vec::new(),
}
}
Expand All @@ -53,35 +115,63 @@ where
items,
indices,
} = self;
match (*pow, iter, items.len()) {
// Final stable state: underlying iterator and items forgotten.
(_, None, 0) => None,
println!(
"^{pow}: {indices:?}\t{}\t{:?}",
if iter.is_some() { 'S' } else { 'N' },
items.as_ref().map(|v| v.len())
);
match (*pow, iter, items) {
// BBBBBBBBBBBB
// Stable degenerated state: 0^pow with pow > 0;
(_, None, None) => None,

// CCCCCCCCCCCC
// Degenerated 0th power iteration.
(0, Some(_), _) => {
self.iter = None; // Forget without even consuming.
Some((indices, items))
(0, Some(_), None) => {
self.iter = None; // Forget about underlying iteration immediately.
*items = Some(Vec::new()); // Raise this value as a boolean flag.
Some((indices, &[])) // Yield empty list.
}

(pow, Some(it), 0) => {
// KKKKKKKKKKKK
// Degenerated 0th power iteration, after the dummy item was collected.
// Use the Some<(empty)Vec> as a flag to alternate between yielding [] or None.
(0, None, Some(_)) => {
*items = None;
None
}
// LLLLLLLLLLLL
(0, None, None) => {
*items = Some(Vec::new());
Some((indices, &[]))
}

// DDDDDDDDDDDD
// First iteration.
(pow, Some(it), None) => {
// Check whether there is at least one element in the iterator.
if let Some(first) = it.next() {
// Allocate buffer to hold items about to be yielded.
items.reserve_exact(it.size_hint().0);
items.push(first);
*items = Some({
let mut v = Vec::with_capacity(it.size_hint().0);
v.push(first);
v
});
// Same for indices to be yielded.
indices.reserve_exact(pow);
for _ in 0..pow {
indices.push(0);
}
return Some((indices, items));
Some((indices, &items.unwrap())) // HERE: can I rebase to get a smarter borrowck?
} else {
// Degenerated iteration over an empty set, yet with non-null power.
self.iter = None;
None
}
// Degenerated iteration over an empty set, yet with non-null power.
self.iter = None;
None
}

(pow, Some(it), base) => {
// EEEEEEEEEEEE
(pow, Some(it), Some(items)) => {
// We are still unsure whether all items have been collected.
// As a consequence, 'base' is still uncertain,
// but then we know that indices haven't started wrapping around yet.
Expand All @@ -91,32 +181,55 @@ where
return Some((indices, items));
}

// All items have just been collected.
self.iter = None;
if base == 1 || pow == 1 {
// The item collected on previous call was the last one.
self.iter = None; // Forget about the underlying iterator.
if pow == 1 {
// End of iteration.
items.clear();
indices.clear();
let base = items.len();
indices[0] = base; // Mark to cycle again on next iteration.
return None;
}

// First wrap around.
indices[pow - 1] = 0;
indices[pow - 2] += 1;
indices[pow - 2] = 1;
Some((indices, items))
}

(_, None, b) => {
// // FFFFFFFFFFFF
// (_, None, 1) => {
// // Flip the only indice to keep cycling.
// let ind = &mut indices[0];
// if *ind == 1 {
// *ind = 0;
// Some((indices, items))
// } else {
// *ind = 1;
// None
// }
// }

// GGGGGGGGGGGG
(_, None, Some(items)) => {
let base = items.len();
if indices[0] == base {
// Special marker that iteration can start over for a new round.
indices[0] = 0;
return Some((indices, items));
}
// Keep yielding items list, incrementing indices rightmost first.
for index in indices.iter_mut().rev() {
*index += 1;
if *index < b {
if *index < base {
return Some((indices, items));
}
*index = 0; // Wrap and increment left.
}
items.clear();
indices.clear();
// Iteration is over.
// But don't clear the collected items,
// and mark a special index value to not fuse the iterator
// but make it possibly cycle through all again.
indices[0] = base;
None
}
}
Expand Down Expand Up @@ -187,13 +300,6 @@ where
}
}

impl<I> FusedIterator for CartesianPower<I>
where
I: Iterator,
I::Item: Clone,
{
}

#[cfg(test)]
mod tests {
//! Use chars and string to ease testing of every yielded iterator values.
Expand All @@ -202,37 +308,46 @@ mod tests {
use crate::Itertools;
use core::str::Chars;

fn check_fused(mut exhausted_it: CartesianPower<Chars>, context: String) {
for i in 0..100 {
let act = exhausted_it.next();
assert!(
act.is_none(),
"Iteration {} after expected exhaustion of {} \
yielded {:?} instead of None. ",
i,
context,
act,
);
}
}

#[test]
fn basic() {
fn check(origin: &str, pow: usize, expected: &[&str]) {
let mut it = origin.chars().cartesian_power(pow);
let mut i = 0;
for exp in expected {
let act = it.next();
if act != Some(exp.chars().collect()) {
panic!(
"Failed iteration {} for {:?}^{}. \
Expected {:?}, got {:?} instead.",
i, origin, pow, exp, act,
);
println!("================== ({origin:?}^{pow})");
let mut it_act = origin.chars().cartesian_power(pow);
// Check thrice that it's cycling.
for r in 1..=3 {
println!("- - {r} - - - - - -");
let mut it_exp = expected.iter();
let mut i = 0;
loop {
match (it_exp.next(), it_act.next()) {
(Some(exp), Some(act)) => {
if act != exp.chars().collect::<Vec<_>>() {
panic!(
"Failed iteration {} (repetition {}) for {:?}^{}. \
Expected {:?}, got {:?} instead.",
i, r, origin, pow, exp, act,
);
}
i += 1;
}
(None, Some(act)) => {
panic!(
"Failed iteration {} (repetition {}) for {:?}^{}. \
Expected None, got {:?} instead.",
i, r, origin, pow, act,
);
}
(Some(exp), None) => {
panic!(
"Failed iteration {} (repetition {}) for {:?}^{}. \
Expected {:?}, got None instead.",
i, r, origin, pow, exp,
);
}
(None, None) => break,
}
}
i += 1;
}
check_fused(it, format!("iteration {} or {:?}^{}", i, origin, pow));
}

// Empty underlying iterator.
Expand Down Expand Up @@ -281,38 +396,28 @@ mod tests {
fn check(origin: &str, pow: usize, expected: &[(usize, Option<&str>)]) {
let mut it = origin.chars().cartesian_power(pow);
let mut total_n = Vec::new();
for &(n, exp) in expected {
let act = it.nth(n);
total_n.push(n);
if act != exp.map(|s| s.chars().collect::<Vec<_>>()) {
panic!(
"Failed nth({}) iteration for {:?}^{}. \
Expected {:?}, got {:?} instead.",
total_n
.iter()
.map(ToString::to_string)
.collect::<Vec<_>>()
.join(", "),
origin,
pow,
exp,
act,
);
for r in 1..=3 {
for &(n, exp) in expected {
let act = it.nth(n);
total_n.push(n);
if act != exp.map(|s| s.chars().collect::<Vec<_>>()) {
panic!(
"Failed nth({}) iteration (repetition {}) for {:?}^{}. \
Expected {:?}, got {:?} instead.",
total_n
.iter()
.map(ToString::to_string)
.collect::<Vec<_>>()
.join(", "),
r,
origin,
pow,
exp,
act
);
}
}
}
check_fused(
it,
format!(
"nth({}) iteration of {:?}^{}",
total_n
.iter()
.map(ToString::to_string)
.collect::<Vec<_>>()
.join(", "),
origin,
pow
),
);
}

// HERE: make it work with the new implementation.
Expand Down

0 comments on commit 5b51535

Please sign in to comment.