From a81cfcd2b65469e6a4d71146953be4384771397d Mon Sep 17 00:00:00 2001 From: Iago-lito Date: Fri, 18 Oct 2024 20:10:50 +0200 Subject: [PATCH] =?UTF-8?q?=F0=9F=9A=A7=20Refactor=20in=20terms=20of=20und?= =?UTF-8?q?erlying=20pure-indices=20indices=20streaming=20iterator.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/cartesian_power.rs | 216 +++++++++++++++++++++++++++++++---------- 1 file changed, 164 insertions(+), 52 deletions(-) diff --git a/src/cartesian_power.rs b/src/cartesian_power.rs index ec05d00a5..d81c76319 100644 --- a/src/cartesian_power.rs +++ b/src/cartesian_power.rs @@ -1,6 +1,137 @@ use alloc::vec::Vec; use std::fmt; +/// Pseudo-iterator owned by [`CartesianPower`], +/// yielding underlying indices by references to itself, +/// the [streaming iterator](https://docs.rs/streaming-iterator/latest/streaming_iterator/) way. +pub struct Indices { + pow: u32, + // May be incremented by owner on first pass as long as exact value is unknown. + base: usize, + values: Option>, +} + +impl Indices { + pub fn new(base: usize, pow: u32) -> Self { + Self { + base, + pow, + values: None, + } + } + pub fn next(&mut self) -> Option<&[usize]> { + let Self { base, pow, values } = self; + match (base, pow, values) { + // First iteration with degenerated 0th power. + (_, 0, values @ None) => Some(values.insert(Vec::new())), + + // Last degenerated 0th power iteration. + // Use the Some<(empty)Vec> as a flag to alternate between yielding [] or None. + (_, 0, values @ Some(_)) => { + *values = None; + None + } + + // Stable iteration in 0-base. + (0, _, _) => None, + + // First iteration in the general case. + (_, pow, values @ None) => Some(values.insert(vec![0; *pow as usize])), + + // Subsequent iteration in the general case. + (&mut base, _, Some(values)) => { + if values[0] == base { + // Special marker that iteration can start over for a new round. + values[0] = 0; + return Some(values); + } + if inbounds_increment(values, base) { + return Some(values); + } + // Iteration is over. + // Mark a special index value to not fuse the iterator + // and make it possible to cycle through all results again. + values[0] = base; + None + } + } + } + + pub fn nth(&mut self, n: usize) -> Option<&[usize]> { + let Self { base, pow, values } = self; + match (base, pow, values, n) { + // First iteration with degenerated 0th power. + (_, 0, values @ None, 0) => { + // Same as .next() + Some(values.insert(Vec::new())) + } + // Saturate. + (_, 0, values @ Some(_), _) => { + *values = None; + None + } + // Stable iteration in 0-base. + (0, _, _, _) => None, + // First iteration in the general case. + (&mut base, pow, values @ None, n) => { + let values = values.insert(vec![0; *pow as usize]); + if inbounds_increment_by(n, values, base) { + return Some(values); + } + // Immediate saturation. + values[0] = base; + None + } + // Subsequent iteration in the general case. + (&mut base, _, Some(values), n) => { + let shift = if values[0] == base { + // Start over for a new round (already counted then). + values[0] = 0; + 0 + } else { + 1 + }; + if inbounds_increment_by(n + shift, values, base) { + return Some(values); + } + // Immediate re-saturation. + values[0] = base; + None + } + } + } +} + +/// Increment indices, returning false in case of overflow. +fn inbounds_increment(indices: &mut [usize], base: usize) -> bool { + for index in indices.iter_mut().rev() { + *index += 1; + if *index < base { + return true; + } + *index = 0; // Wrap and increment left. + } + false +} + +/// Increment indices by n, returning false in case of (saturating) overflow. +fn inbounds_increment_by(n: usize, indices: &mut [usize], base: usize) -> bool { + let mut q = n; + for index in indices.iter_mut().rev() { + let s = *index + q; + q = s / base; + *index = s % base; + if q == 0 { + return true; + } + } + // Saturation requires a second pass to reset all indices. + for index in indices.iter_mut() { + *index = 0; + } + false +} + /// An adaptor iterating through all the ordered `n`-length lists of items /// yielded by the underlying iterator, including repetitions. /// @@ -66,9 +197,9 @@ where let pow = *pow as usize; // (weird 'items @' bindings circumvent NLL limitations, unneeded with polonius) - match (pow, iter, &mut *items) { + match (&mut *items, pow, iter) { // First iteration with degenerated 0th power. - (0, Some(_), items @ None) => { + (items @ None, 0, Some(_)) => { self.iter = None; // Forget about underlying iteration immediately. let empty = items.insert(Vec::new()); // Raise this value as a boolean flag. Some((indices, empty)) // Yield empty list. @@ -76,14 +207,14 @@ where // Subsequent degenerated 0th power iteration. // Use the Some<(empty)Vec> as a flag to alternate between yielding [] or None. - (0, None, items @ Some(_)) => { + (items @ Some(_), 0, None) => { *items = None; None } - (0, None, items @ None) => Some((indices, items.insert(Vec::new()))), + (items @ None, 0, None) => Some((indices, items.insert(Vec::new()))), // First iteration in the general case. - (pow, Some(it), items @ None) => { + (items @ None, pow, Some(it)) => { // Check whether there is at least one element in the iterator. if let Some(first) = it.next() { items // Collect it. @@ -104,10 +235,10 @@ where } // Stable iteration in the degenerated case 'base = 0'. - (_, None, None) => None, + (None, _, None) => None, // Subsequent iteration in the general case. - (pow, Some(it), Some(items)) => { + (Some(items), pow, Some(it)) => { // We are still unsure whether all items have been collected. // As a consequence, the exact value of 'base' is still uncertain, // but then we know that indices haven't started wrapping around yet. @@ -133,7 +264,7 @@ where } // Subsequent iteration in the general case after all items have been collected. - (_, None, Some(items)) => { + (Some(items), _, None) => { let base = items.len(); if indices[0] == base { // Special marker that iteration can start over for a new round. @@ -141,7 +272,7 @@ where return Some((indices, items)); } // Keep yielding items list, incrementing indices rightmost first. - if Self::inbounds_increment(indices, base) { + if inbounds_increment(indices, base) { return Some((indices, items)); } // Iteration is over. @@ -153,36 +284,6 @@ where } } - /// Increment indices, returning false in case of overflow. - fn inbounds_increment(indices: &mut [usize], base: usize) -> bool { - for index in indices.iter_mut().rev() { - *index += 1; - if *index < base { - return true; - } - *index = 0; // Wrap and increment left. - } - false - } - - /// Increment indices by n, returning false in case of (saturating) overflow. - fn inbounds_increment_by(n: usize, indices: &mut [usize], base: usize) -> bool { - let mut q = n; - for index in indices.iter_mut().rev() { - let s = *index + q; - q = s / base; - *index = s % base; - if q == 0 { - return true; - } - } - // Saturation requires a second pass to reset all indices. - for index in indices.iter_mut() { - *index = 0; - } - false - } - /// Same as [`increment_indices`], but does n increments at once. /// The iterator is cycling, but `.nth()` does not 'wrap' /// and 'saturates' to None instead. @@ -196,15 +297,15 @@ where let pow = *pow as usize; - match (pow, iter, &mut *items, n) { + match (&mut *items, pow, iter, n) { // First iteration with degenerated 0th power. - (0, Some(_), items @ None, 0) => { + (items @ None, 0, Some(_), 0) => { // Same as .next(). self.iter = None; let empty = items.insert(Vec::new()); Some((indices, empty)) } - (0, Some(_), None, _) => { + (None, 0, Some(_), _) => { // Saturate. self.iter = None; None @@ -212,9 +313,9 @@ where // Subsequent degenerated 0th power iteration. // Same as `.next()`. - (0, None, items @ None, 0) => Some((indices, items.insert(Vec::new()))), + (items @ None, 0, None, 0) => Some((indices, items.insert(Vec::new()))), // Saturate. - (0, None, items, _) => { + (items, 0, None, _) => { *items = None; None } @@ -222,7 +323,7 @@ where // First iterations in the general case. // Possibly this will consume the entire underlying iterator, // but we need to consume to check. - (pow, Some(it), items @ None, mut remaining) => { + (items @ None, pow, Some(it), mut remaining) => { if let Some(first) = it.next() { // There is at least one element in the iterator, prepare collection + indices. let items = items.insert(Vec::with_capacity(it.size_hint().0)); @@ -246,7 +347,7 @@ where // Collection completed, but we need to go further. self.iter = None; let base = items.len(); - if Self::inbounds_increment_by(n, indices, base) { + if inbounds_increment_by(n, indices, base) { return Some((indices, items)); } // Immediate saturation. @@ -261,11 +362,11 @@ where } // Stable iteration in the degenerated case 'base = 0'. - (_, None, None, _) => None, + (None, _, None, _) => None, // Subsequent iteration in the general case. // Again, immediate saturation is an option. - (pow, Some(it), Some(items), mut remaining) => { + (Some(items), pow, Some(it), mut remaining) => { if let Some(next) = it.next() { items.push(next); loop { @@ -284,7 +385,7 @@ where // Collection completed. self.iter = None; let base = items.len(); - if Self::inbounds_increment_by(n + 1, indices, base) { + if inbounds_increment_by(n + 1, indices, base) { return Some((indices, items)); } // Saturate. @@ -294,7 +395,7 @@ where // Subsequent iteration in the general case // after all items have been collected. - (_, None, Some(items), n) => { + (Some(items), _, None, n) => { let base = items.len(); let shift = if indices[0] == base { // Start over for a new round (already counted then). @@ -303,7 +404,7 @@ where } else { 1 }; - if Self::inbounds_increment_by(n + shift, indices, base) { + if inbounds_increment_by(n + shift, indices, base) { return Some((indices, items)); } // Immediate re-saturation. @@ -446,8 +547,19 @@ where #[cfg(test)] mod tests { - use crate::Itertools; + use crate::{cartesian_power::Indices, Itertools}; + #[test] + fn indices() { + let mut it = Indices::new(3, 2); + for i in 0..30 { + println!("{i}: {:?}", it.next()); + } + for i in 0..30 { + println!("{i}: {:?}", it.nth(2)); + } + panic!("STOP HERE"); + } #[test] fn basic() { fn check(origin: &str, pow: u32, expected: &[&str]) {