Skip to content

Commit

Permalink
🚀 Rebase, document: ready for review!
Browse files Browse the repository at this point in the history
  • Loading branch information
iago-lito committed Dec 8, 2024
1 parent aa75f69 commit 360f14b
Show file tree
Hide file tree
Showing 3 changed files with 647 additions and 40 deletions.
118 changes: 97 additions & 21 deletions src/cartesian_power.rs
Original file line number Diff line number Diff line change
@@ -1,9 +1,49 @@
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.
/// Yield all indices combinations of size `pow` from `0..base`.
/// This "lending" iterator only allocates once,
/// and yields references to its internal owned slice of updated indices.
/// See [streaming iterator](https://docs.rs/streaming-iterator/latest/streaming_iterator/).
///
/// The resulting iterator is "cycling",
/// meaning that, after the last `.next()` call has yielded `None`,
/// you can call `.next()` again to resume iteration from the start,
/// as many times as needed.
///
/// This is the iterator internally used by [`CartesianPower`],
/// ```
/// use itertools::CartesianPowerIndices;
///
/// let mut it = CartesianPowerIndices::new(3, 2);
/// assert_eq!(it.next(), Some(&[0, 0][..]));
/// assert_eq!(it.next(), Some(&[0, 1][..]));
/// assert_eq!(it.next(), Some(&[0, 2][..]));
/// assert_eq!(it.next(), Some(&[1, 0][..]));
/// assert_eq!(it.next(), Some(&[1, 1][..]));
/// assert_eq!(it.next(), Some(&[1, 2][..]));
/// assert_eq!(it.next(), Some(&[2, 0][..]));
/// assert_eq!(it.next(), Some(&[2, 1][..]));
/// assert_eq!(it.next(), Some(&[2, 2][..]));
/// assert_eq!(it.next(), None); // End of iteration.
/// assert_eq!(it.next(), Some(&[0, 0][..])); // Cycle: start over, forever.
/// assert_eq!(it.next(), Some(&[0, 1][..]));
/// // ...
///
/// let mut it = CartesianPowerIndices::new(2, 3);
/// assert_eq!(it.next(), Some(&[0, 0, 0][..]));
/// assert_eq!(it.next(), Some(&[0, 0, 1][..]));
/// assert_eq!(it.next(), Some(&[0, 1, 0][..]));
/// assert_eq!(it.next(), Some(&[0, 1, 1][..]));
/// assert_eq!(it.next(), Some(&[1, 0, 0][..]));
/// assert_eq!(it.next(), Some(&[1, 0, 1][..]));
/// assert_eq!(it.next(), Some(&[1, 1, 0][..]));
/// assert_eq!(it.next(), Some(&[1, 1, 1][..]));
/// assert_eq!(it.next(), None);
/// assert_eq!(it.next(), Some(&[0, 0, 0][..]));
/// assert_eq!(it.next(), Some(&[0, 0, 1][..]));
/// // ...
/// ```
#[derive(Debug, Clone)]
pub struct Indices {
// May be incremented by owner on first pass as long as exact value is unknown.
Expand All @@ -21,13 +61,18 @@ pub struct Indices {
}

impl Indices {
/// Create a new `base^pow` lending iterator.
pub fn new(base: usize, pow: u32) -> Self {
Self {
base,
pow,
values: None,
}
}

/// Step the iterator, yielding a reference to internal updated indices,
/// or `None` if the iteration is exhausted.
#[allow(clippy::should_implement_trait)] // <- Intended `.next` name "like Iterator::next".
pub fn next(&mut self) -> Option<&[usize]> {
let Self { base, pow, values } = self;

Expand Down Expand Up @@ -67,6 +112,25 @@ impl Indices {
}
}

/// Same as [`next`][crate::CartesianPowerIndices::next],
/// but skip `n` steps.
/// Return `None` if this would lead to iterator exhaustion.
/// Saturates in case of overflow:
/// the iterator is cycling,
/// but if you skip past the last iteration,
/// you'll obtain `None` no matter how far you skip.
/// Iteration will only resume on further calls to `.next()` and `.nth()`.
///
/// ```
/// use itertools::CartesianPowerIndices;
///
/// let mut it = CartesianPowerIndices::new(3, 2);
/// assert_eq!(it.nth(0), Some(&[0, 0][..]));
/// assert_eq!(it.nth(1), Some(&[0, 2][..]));
/// assert_eq!(it.nth(2), Some(&[1, 2][..]));
/// assert_eq!(it.nth(9), None); // Overshoot, but don't resume cycling yet.
/// assert_eq!(it.nth(2), Some(&[0, 2][..])); // Only resume cycling now.
/// ```
pub fn nth(&mut self, n: usize) -> Option<&[usize]> {
let Self { base, pow, values } = self;
match (base, pow, values, n) {
Expand Down Expand Up @@ -110,7 +174,8 @@ impl Indices {
}
}

fn size_hint(&self) -> (usize, Option<usize>) {
/// Akin to [`Iterator::size_hint`].
pub fn size_hint(&self) -> (usize, Option<usize>) {
self.size_hint_with_base(self.base)
}

Expand Down Expand Up @@ -195,11 +260,12 @@ fn inbounds_increment_by(n: usize, indices: &mut [usize], base: usize) -> bool {
false
}

/// An adaptor iterating through all the ordered `n`-length lists of items
/// yielded by the underlying iterator, including repetitions.
/// Works by cloning the items into a fresh Vector on every `.next()`.
/// If this is too much allocation for you,
/// consider using the underlying lending [`Indices`] iterator instead.
/// The adaptor adaptor yielded by
/// [`.cartesian_power()`](crate::Itertools::cartesian_power).
///
/// This iterator is *cycling*,
/// meaning that, once consumed after `.next()` returns `None`,
/// you can call `.next()` again to resume iteration from the start.
///
/// See [`.cartesian_power()`](crate::Itertools::cartesian_power)
/// for more information.
Expand All @@ -217,24 +283,19 @@ where
indices: Indices,
}

/// Create a new `CartesianPower` from an iterator of clonables.
pub fn cartesian_power<I>(iter: I, pow: u32) -> CartesianPower<I>
impl<I> CartesianPower<I>
where
I: Iterator,
I::Item: Clone,
{
CartesianPower {
iter: Some(iter),
items: None,
indices: Indices::new(0, pow),
pub(crate) fn new(iter: I, pow: u32) -> CartesianPower<I> {
CartesianPower {
iter: Some(iter),
items: None,
indices: Indices::new(0, pow),
}
}
}

impl<I> CartesianPower<I>
where
I: Iterator,
I::Item: Clone,
{
/// Increments internal indices to advance to the next list to be yielded.
/// This collects new items from the underlying iterator
/// if they were not all already collected.
Expand Down Expand Up @@ -849,4 +910,19 @@ mod tests {
check!("abc", 3 => 27 o 27 o 27 o 27 o);
check!("abc", 3 => 28 o 28 o 28 o 28 o);
}

#[test]
fn f() {
use crate::CartesianPowerIndices;
let mut it = CartesianPowerIndices::new(3, 2);
while let Some(indices) = it.next() {
println!("&{indices:?}");
}

let mut it = CartesianPowerIndices::new(2, 3);
while let Some(indices) = it.next() {
println!("&{indices:?}");
}
panic!("STOP HERE");
}
}
83 changes: 64 additions & 19 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,6 @@ extern crate alloc;
#[cfg(feature = "use_alloc")]
use alloc::{collections::VecDeque, string::String, vec::Vec};

use cartesian_power::CartesianPower;
pub use either::Either;

use core::borrow::Borrow;
Expand Down Expand Up @@ -91,7 +90,10 @@ pub use std::iter as __std_iter;
/// The concrete iterator types.
pub mod structs {
#[cfg(feature = "use_alloc")]
pub use crate::adaptors::MultiProduct;
pub use crate::{
cartesian_power::{CartesianPower, Indices as CartesianPowerIndices},
adaptors::MultiProduct
};
pub use crate::adaptors::{
Batching, Coalesce, Dedup, DedupBy, DedupByWithCount, DedupWithCount, FilterMapOk,
FilterOk, Interleave, InterleaveShortest, MapInto, MapOk, Positions, Product, PutBack,
Expand Down Expand Up @@ -1215,6 +1217,66 @@ pub trait Itertools: Iterator {
adaptors::cartesian_product(self, other.into_iter())
}

/// Return an iterator adaptor that iterates over the given cartesian power of
/// the element yielded by `self`.
///
/// Iterator element type is a collection `Vec<Self::Item>` of size `pow`.
/// The collection is a fresh vector
/// allocated on every `.next()` call
/// and populated with `Self::Item::clone`
/// until the cartesian power is exhausted.
/// The underlying iterator is only consumed once,
/// and all its yielded items are stored within the adaptor for future cloning.
///
/// If this is too much allocation for you,
/// you may try the underlying streaming
/// [`CartesianPowerIndices`] instead.
///
/// The resulting iterator is "cycling",
/// meaning that, after the last `.next()` call has yielded `None`,
/// you can call `.next()` again to resume iteration from the start,
/// as many times as needed.
///
/// ```
/// use itertools::Itertools;
///
/// let mut it = "abc".chars().cartesian_power(2);
/// assert_eq!(it.next(), Some(vec!['a', 'a']));
/// assert_eq!(it.next(), Some(vec!['a', 'b']));
/// assert_eq!(it.next(), Some(vec!['a', 'c'])); // Underlying a"abc".chars()` consumed.
/// assert_eq!(it.next(), Some(vec!['b', 'a']));
/// assert_eq!(it.next(), Some(vec!['b', 'b']));
/// assert_eq!(it.next(), Some(vec!['b', 'c']));
/// assert_eq!(it.next(), Some(vec!['c', 'a']));
/// assert_eq!(it.next(), Some(vec!['c', 'b']));
/// assert_eq!(it.next(), Some(vec!['c', 'c']));
/// assert_eq!(it.next(), None); // Cartesian product exhausted.
/// assert_eq!(it.next(), Some(vec!['a', 'a'])); // Cycle: start over, *ad libitum*.
/// assert_eq!(it.next(), Some(vec!['a', 'b']));
/// // ...
///
/// let mut it = "ab".chars().cartesian_power(3);
/// assert_eq!(it.next(), Some(vec!['a', 'a', 'a']));
/// assert_eq!(it.next(), Some(vec!['a', 'a', 'b']));
/// assert_eq!(it.next(), Some(vec!['a', 'b', 'a']));
/// assert_eq!(it.next(), Some(vec!['a', 'b', 'b']));
/// assert_eq!(it.next(), Some(vec!['b', 'a', 'a']));
/// assert_eq!(it.next(), Some(vec!['b', 'a', 'b']));
/// assert_eq!(it.next(), Some(vec!['b', 'b', 'a']));
/// assert_eq!(it.next(), Some(vec!['b', 'b', 'b']));
/// assert_eq!(it.next(), None);
/// assert_eq!(it.next(), Some(vec!['a', 'a', 'a']));
/// assert_eq!(it.next(), Some(vec!['a', 'a', 'b']));
/// // ...
/// ```
fn cartesian_power(self, pow: u32) -> CartesianPower<Self>
where
Self: Sized,
Self::Item: Clone,
{
CartesianPower::new(self, pow)
}

/// Return an iterator adaptor that iterates over the cartesian product of
/// all subiterators returned by meta-iterator `self`.
///
Expand Down Expand Up @@ -1795,23 +1857,6 @@ pub trait Itertools: Iterator {
combinations_with_replacement::combinations_with_replacement(self, k)
}

/// Returns an iterator yielding the successive elements
/// of the cartesian power of the set described by the original iterator.
///
/// ```
/// use itertools::Itertools;
///
/// TODO: illustrative example.
/// ```
#[cfg(feature = "use_alloc")]
fn cartesian_power(self, pow: u32) -> CartesianPower<Self>
where
Self: Sized,
Self::Item: Clone,
{
cartesian_power::cartesian_power(self, pow)
}

/// Return an iterator adaptor that iterates over all k-permutations of the
/// elements from an iterator.
///
Expand Down
Loading

0 comments on commit 360f14b

Please sign in to comment.