Skip to content

Commit

Permalink
🚧 Implement .size_hint() (untested yet).
Browse files Browse the repository at this point in the history
  • Loading branch information
iago-lito committed Oct 3, 2024
1 parent 8631037 commit ac30a04
Show file tree
Hide file tree
Showing 2 changed files with 87 additions and 22 deletions.
107 changes: 86 additions & 21 deletions src/cartesian_power.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ where
I: Iterator,
I::Item: Clone,
{
pow: usize,
pow: u32,
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.
Expand All @@ -30,7 +30,7 @@ where
}

/// Create a new `CartesianPower` from an iterator of clonables.
pub fn cartesian_power<I>(iter: I, pow: usize) -> CartesianPower<I>
pub fn cartesian_power<I>(iter: I, pow: u32) -> CartesianPower<I>
where
I: Iterator,
I::Item: Clone,
Expand Down Expand Up @@ -62,14 +62,11 @@ where
items,
indices,
} = self;
println!(
"^{pow}: {} {indices:?}\t\t{:?}",
if iter.is_some() { 'S' } else { 'N' },
items.as_ref().map(Vec::len)
);

let pow = *pow as usize;

// (weird 'items @' bindings circumvent NLL limitations, unneeded with polonius)
match (*pow, iter, &mut *items) {
match (pow, iter, &mut *items) {
// First iteration with degenerated 0th power.
(0, Some(_), items @ None) => {
self.iter = None; // Forget about underlying iteration immediately.
Expand Down Expand Up @@ -197,7 +194,9 @@ where
indices,
} = self;

match (*pow, iter, &mut *items, n) {
let pow = *pow as usize;

match (pow, iter, &mut *items, n) {
// First iteration with degenerated 0th power.
(0, Some(_), items @ None, 0) => {
// Same as .next().
Expand All @@ -213,9 +212,7 @@ where

// Subsequent degenerated 0th power iteration.
// Same as `.next()`.
(0, None, items @ None, 0) => {
Some((indices, items.insert(Vec::new())))
}
(0, None, items @ None, 0) => Some((indices, items.insert(Vec::new()))),
// Saturate.
(0, None, items, _) => {
*items = None;
Expand Down Expand Up @@ -264,9 +261,7 @@ 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.
Expand Down Expand Up @@ -348,7 +343,77 @@ where
}

fn size_hint(&self) -> (usize, Option<usize>) {
todo!()
let Self {
pow,
iter,
items,
indices,
} = self;

// The following case analysis matches implementation of `.next()`.
match (*pow, iter, items) {
// First iteration with degenerated 0th power.
(0, Some(_), None) => (1, Some(1)),

// Subsequent degenerated 0th power iteration.
// Alternating for cycling behaviour.
(0, None, Some(_)) => (0, Some(0)),
(0, None, None) => (1, Some(1)),

// First iteration in the general case.
(pow, Some(it), None) => {
let (a, b) = it.size_hint();
(
a.checked_pow(pow).unwrap_or(usize::MAX),
b.map(|b| b.checked_pow(pow)).flatten(),
)
}

// Stable iteration in the degenerated case 'base = 0'.
(_, None, None) => (0, Some(0)),

// Subsequent iteration in the general case.
(pow, Some(it), Some(items)) => {
let already = items.len();
let minus_already = |total| total - already;
let (a, b) = it.size_hint();
(
(a + already)
.checked_pow(pow)
.map(minus_already)
.unwrap_or(usize::MAX),
b.map(|b| (b + already).checked_pow(pow).map(minus_already))
.flatten(),
)
}

// Subsequent iteration in the general case after all items have been collected.
(pow, None, Some(items)) => {
let base = items.len();
if indices[0] == base {
// Fresh re-start.
let r = base.checked_pow(pow);
return (r.unwrap_or(usize::MAX), r);
}
// Count what's left from current indices.
// This is subtracting the current iteration number base^pow,
// using the complement method.
let calc = || -> Option<usize> {
// (closure-wrap to ease interruption on overflow with ?-operator)
let comp = base - 1;
let mut r = 1usize;
for (&i, rank) in indices.iter().rev().zip(0u32..) {
let increment = (comp - i).checked_pow(rank)?;
r = r.checked_add(increment)?;
}
Some(r)
};
let Some(r) = calc() else {
return (usize::MAX, None);
};
(r, Some(r))
}
}
}

fn count(self) -> usize {
Expand Down Expand Up @@ -378,19 +443,19 @@ where
}
}

/// Use chars and string to ease testing of every yielded iterator values.
#[cfg(test)]
mod tests {
//! Use chars and string to ease testing of every yielded iterator values.

use super::CartesianPower;
use crate::Itertools;
use core::str::Chars;

#[test]
fn basic() {
fn check(origin: &str, pow: usize, expected: &[&str]) {
fn check(origin: &str, pow: u32, expected: &[&str]) {
println!("================== ({origin:?}^{pow})");
let mut it_act = origin.chars().cartesian_power(pow);
// Check size_hint on the fly.
let e_hint = expected.len(); // HERE: do.
// Check thrice that it's cycling.
for r in 1..=3 {
println!("- - {r} - - - - - -");
Expand Down Expand Up @@ -473,7 +538,7 @@ mod tests {

#[test]
fn nth() {
fn check(origin: &str, pow: usize, expected: &[(usize, Option<&str>)]) {
fn check(origin: &str, pow: u32, expected: &[(usize, Option<&str>)]) {
println!("================== ({origin:?}^{pow})");
let mut it = origin.chars().cartesian_power(pow);
let mut total_n = Vec::new();
Expand Down
2 changes: 1 addition & 1 deletion src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1757,7 +1757,7 @@ pub trait Itertools: Iterator {
/// TODO: illustrative example.
/// ```
#[cfg(feature = "use_alloc")]
fn cartesian_power(self, pow: usize) -> CartesianPower<Self>
fn cartesian_power(self, pow: u32) -> CartesianPower<Self>
where
Self: Sized,
Self::Item: Clone,
Expand Down

0 comments on commit ac30a04

Please sign in to comment.