Skip to content

Commit

Permalink
🚧 Refactor in terms of underlying pure-indices indices streaming iter…
Browse files Browse the repository at this point in the history
…ator.
  • Loading branch information
iago-lito committed Oct 18, 2024
1 parent 3efe36c commit a81cfcd
Showing 1 changed file with 164 additions and 52 deletions.
216 changes: 164 additions & 52 deletions src/cartesian_power.rs
Original file line number Diff line number Diff line change
@@ -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<Vec<usize>>,
}

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.
///
Expand Down Expand Up @@ -66,24 +197,24 @@ 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.
}

// 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.
Expand All @@ -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.
Expand All @@ -133,15 +264,15 @@ 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.
indices[0] = 0;
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.
Expand All @@ -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.
Expand All @@ -196,33 +297,33 @@ 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
}

// 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
}

// 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));
Expand All @@ -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.
Expand All @@ -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 {
Expand All @@ -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.
Expand All @@ -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).
Expand All @@ -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.
Expand Down Expand Up @@ -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]) {
Expand Down

0 comments on commit a81cfcd

Please sign in to comment.