Skip to content

Commit

Permalink
Move array combinations with reps to own adaptor
Browse files Browse the repository at this point in the history
  • Loading branch information
rossmacarthur committed Dec 2, 2023
1 parent 72a0c8c commit 9bc84b2
Show file tree
Hide file tree
Showing 7 changed files with 171 additions and 148 deletions.
2 changes: 2 additions & 0 deletions Cargo.toml
Expand Up @@ -25,6 +25,7 @@ default = [

"array_chunks",
"array_combinations",
"array_combinations_with_reps",
"array_windows",
"cartesian_product",
"circular_array_windows",
Expand All @@ -38,6 +39,7 @@ alloc = []

array_chunks = ["dep:arrays"]
array_combinations = ["alloc", "dep:arrays"]
array_combinations_with_reps = ["alloc", "dep:arrays"]
array_windows = ["dep:arrays"]
cartesian_product = []
circular_array_windows = ["array_windows"]
Expand Down
95 changes: 0 additions & 95 deletions src/adaptors/array_combinations.rs
Expand Up @@ -41,40 +41,6 @@ pub trait IterArrayCombinations: Iterator {
{
ArrayCombinations::new(self)
}

/// Returns an iterator adaptor that iterates over `K` length combinations
/// with repetitions/replacements of all the elements in the underlying
/// iterator.
///
/// The iterator is consumed as elements are required.
///
/// # Panics
///
/// If called with `K = 0`.
///
/// # Examples
///
/// Basic usage:
///
/// ```
/// use itermore::IterArrayCombinations;
///
/// let mut iter = "ab".chars().array_combinations_with_reps();
/// assert_eq!(iter.next(), Some(['a', 'a', 'a']));
/// assert_eq!(iter.next(), Some(['a', 'a', 'b']));
/// assert_eq!(iter.next(), Some(['a', 'b', 'a']));
/// assert_eq!(iter.next(), Some(['a', 'b', 'b']));
/// assert_eq!(iter.next(), Some(['b', 'a', 'a']));
/// // etc
/// ```
#[inline]
fn array_combinations_with_reps<const K: usize>(self) -> ArrayCombinationsWithReps<Self, K>
where
Self: Sized,
Self::Item: Clone,
{
ArrayCombinationsWithReps::new(self)
}
}

impl<I: ?Sized> IterArrayCombinations for I where I: Iterator {}
Expand Down Expand Up @@ -154,64 +120,3 @@ where
I::Item: Clone,
{
}

////////////////////////////////////////////////////////////////////////////////
// With repetitions/replacement
////////////////////////////////////////////////////////////////////////////////

/// An iterator that iterates over `K` length combinations with
/// repetitions/replacements of all the elements in the underlying iterator.
///
/// This struct is created by the [`array_combinations_with_reps`] method on
/// iterators. See its documentation for more.
///
/// [`array_combinations_with_reps`]: IterArrayCombinations::array_combinations_with_reps
#[cfg_attr(docsrs, doc(cfg(feature = "array_combinations")))]
#[must_use = "iterators are lazy and do nothing unless consumed"]
pub struct ArrayCombinationsWithReps<I, const K: usize>(GenericCombinations<I, [usize; K]>)
where
I: Iterator;

impl<I, const K: usize> ArrayCombinationsWithReps<I, K>
where
I: Iterator,
{
#[track_caller]
pub(crate) fn new(iter: I) -> Self {
assert!(K != 0, "combination size must be non-zero");
Self(GenericCombinations::new(iter, [0; K]))
}
}

impl<I, const K: usize> fmt::Debug for ArrayCombinationsWithReps<I, K>
where
I: Iterator + fmt::Debug,
I::Item: fmt::Debug,
{
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
self.0.fmt_with(f, "ArrayCombinationsWithReps")
}
}

impl<I, const K: usize> Iterator for ArrayCombinationsWithReps<I, K>
where
I: Iterator,
I::Item: Clone,
{
type Item = [I::Item; K];

fn next(&mut self) -> Option<Self::Item> {
self.0.fill_next_with_reps().map(|it| {
// SAFETY: The iterator is guaranteed to yield K elements because
// it is derived from `self.0.comb` which is an array of length K.
unsafe { arrays::collect_unchecked(it) }
})
}
}

impl<I, const K: usize> FusedIterator for ArrayCombinationsWithReps<I, K>
where
I: Iterator,
I::Item: Clone,
{
}
104 changes: 104 additions & 0 deletions src/adaptors/array_combinations_with_reps.rs
@@ -0,0 +1,104 @@
use core::fmt;
use core::iter::FusedIterator;

use crate::adaptors::generic_combinations::GenericCombinations;

/// An extension trait that provides the [`array_combinations_with_reps`] method for
/// iterators.
///
/// [`array_combinations_with_reps`]: IterArrayCombinationsWithReps::array_combinations_with_reps
#[cfg_attr(docsrs, doc(cfg(feature = "array_combinations_with_reps")))]
pub trait IterArrayCombinationsWithReps: Iterator {
/// Returns an iterator adaptor that iterates over `K` length combinations
/// with repetitions/replacements of all the elements in the underlying
/// iterator.
///
/// The iterator is consumed as elements are required.
///
/// # Panics
///
/// If called with `K = 0`.
///
/// # Examples
///
/// Basic usage:
///
/// ```
/// use itermore::IterArrayCombinationsWithReps;
///
/// let mut iter = "ab".chars().array_combinations_with_reps();
/// assert_eq!(iter.next(), Some(['a', 'a', 'a']));
/// assert_eq!(iter.next(), Some(['a', 'a', 'b']));
/// assert_eq!(iter.next(), Some(['a', 'b', 'a']));
/// assert_eq!(iter.next(), Some(['a', 'b', 'b']));
/// assert_eq!(iter.next(), Some(['b', 'a', 'a']));
/// // etc
/// ```
#[inline]
fn array_combinations_with_reps<const K: usize>(self) -> ArrayCombinationsWithReps<Self, K>
where
Self: Sized,
Self::Item: Clone,
{
ArrayCombinationsWithReps::new(self)
}
}

impl<I: ?Sized> IterArrayCombinationsWithReps for I where I: Iterator {}

/// An iterator that iterates over `K` length combinations with
/// repetitions/replacements of all the elements in the underlying iterator.
///
/// This struct is created by the [`array_combinations_with_reps`] method on
/// iterators. See its documentation for more.
///
/// [`array_combinations_with_reps`]: IterArrayCombinationsWithReps::array_combinations_with_reps
#[cfg_attr(docsrs, doc(cfg(feature = "array_combinations_with_reps")))]
#[must_use = "iterators are lazy and do nothing unless consumed"]
pub struct ArrayCombinationsWithReps<I, const K: usize>(GenericCombinations<I, [usize; K]>)
where
I: Iterator;

impl<I, const K: usize> ArrayCombinationsWithReps<I, K>
where
I: Iterator,
{
#[track_caller]
pub(crate) fn new(iter: I) -> Self {
assert!(K != 0, "combination size must be non-zero");
Self(GenericCombinations::new(iter, [0; K]))
}
}

impl<I, const K: usize> fmt::Debug for ArrayCombinationsWithReps<I, K>
where
I: Iterator + fmt::Debug,
I::Item: fmt::Debug,
{
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
self.0.fmt_with(f, "ArrayCombinationsWithReps")
}
}

impl<I, const K: usize> Iterator for ArrayCombinationsWithReps<I, K>
where
I: Iterator,
I::Item: Clone,
{
type Item = [I::Item; K];

fn next(&mut self) -> Option<Self::Item> {
self.0.fill_next_with_reps().map(|it| {
// SAFETY: The iterator is guaranteed to yield K elements because
// it is derived from `self.0.comb` which is an array of length K.
unsafe { arrays::collect_unchecked(it) }
})
}
}

impl<I, const K: usize> FusedIterator for ArrayCombinationsWithReps<I, K>
where
I: Iterator,
I::Item: Clone,
{
}
8 changes: 7 additions & 1 deletion src/adaptors/mod.rs
Expand Up @@ -2,6 +2,8 @@
pub mod array_chunks;
#[cfg(feature = "array_combinations")]
pub mod array_combinations;
#[cfg(feature = "array_combinations_with_reps")]
pub mod array_combinations_with_reps;
#[cfg(feature = "array_windows")]
pub mod array_windows;
#[cfg(feature = "cartesian_product")]
Expand All @@ -10,5 +12,9 @@ pub mod cartesian_product;
pub mod circular_array_windows;
#[cfg(feature = "combinations")]
pub mod combinations;
#[cfg(any(feature = "array_combinations", feature = "combinations"))]
#[cfg(any(
feature = "array_combinations",
feature = "array_combinations_with_reps",
feature = "combinations"
))]
mod generic_combinations;
9 changes: 6 additions & 3 deletions src/lib.rs
Expand Up @@ -73,7 +73,7 @@
//! [`next_chunk`]: IterArrayChunks::next_chunk
//! [`array_chunks`]: IterArrayChunks::array_chunks
//! [`array_combinations`]: IterArrayCombinations::array_combinations
//! [`array_combinations_with_reps`]: IterArrayCombinations::array_combinations_with_reps
//! [`array_combinations_with_reps`]: IterArrayCombinationsWithReps::array_combinations_with_reps
//! [`array_windows`]: IterArrayWindows::array_windows
//! [`cartesian_product`]: IterCartesianProduct::cartesian_product
//! [`circular_array_windows`]: IterCircularArrayWindows::circular_array_windows
Expand Down Expand Up @@ -105,8 +105,11 @@ pub use core;
pub use crate::adaptors::array_chunks::{ArrayChunks, IterArrayChunks};

#[cfg(feature = "array_combinations")]
pub use crate::adaptors::array_combinations::{
ArrayCombinations, ArrayCombinationsWithReps, IterArrayCombinations,
pub use crate::adaptors::array_combinations::{ArrayCombinations, IterArrayCombinations};

#[cfg(feature = "array_combinations_with_reps")]
pub use crate::adaptors::array_combinations_with_reps::{
ArrayCombinationsWithReps, IterArrayCombinationsWithReps,
};

#[cfg(feature = "array_windows")]
Expand Down
49 changes: 0 additions & 49 deletions tests/array_combinations.rs
Expand Up @@ -31,55 +31,6 @@ fn array_combinations_smoke() {
assert!(v.is_empty());
}

#[test]
fn array_combinations_with_reps_smoke() {
// N = 4, K = 1
let v = Vec::from_iter((1..5).array_combinations_with_reps());
assert_eq!(v, [[1], [2], [3], [4]]);

// N = 2, K = 2
let v = Vec::from_iter((1..3).array_combinations_with_reps());
assert_eq!(v, [[1, 1], [1, 2], [2, 1], [2, 2]]);

// N = 3, K = 2
let v = Vec::from_iter((1..4).array_combinations_with_reps());
assert_eq!(
v,
[
[1, 1],
[1, 2],
[1, 3],
[2, 1],
[2, 2],
[2, 3],
[3, 1],
[3, 2],
[3, 3]
]
);

// N = 3, K = 3
let v = Vec::from_iter((1..4).array_combinations_with_reps());
assert_eq!(v.len(), 27);
assert_eq!(
&v[..8],
[
[1, 1, 1],
[1, 1, 2],
[1, 1, 3],
[1, 2, 1],
[1, 2, 2],
[1, 2, 3],
[1, 3, 1],
[1, 3, 2]
]
);

// N = 4, K = 4
let v = Vec::from_iter((1..5).array_combinations_with_reps::<4>());
assert_eq!(v.len(), 256);
}

#[test]
fn array_combinations_edge_cases() {
// N = 1, K = 1
Expand Down
52 changes: 52 additions & 0 deletions tests/array_combinations_with_reps.rs
@@ -0,0 +1,52 @@
#![cfg(feature = "array_combinations_with_reps")]

use itermore::IterArrayCombinationsWithReps;

#[test]
fn array_combinations_with_reps_smoke() {
// N = 4, K = 1
let v = Vec::from_iter((1..5).array_combinations_with_reps());
assert_eq!(v, [[1], [2], [3], [4]]);

// N = 2, K = 2
let v = Vec::from_iter((1..3).array_combinations_with_reps());
assert_eq!(v, [[1, 1], [1, 2], [2, 1], [2, 2]]);

// N = 3, K = 2
let v = Vec::from_iter((1..4).array_combinations_with_reps());
assert_eq!(
v,
[
[1, 1],
[1, 2],
[1, 3],
[2, 1],
[2, 2],
[2, 3],
[3, 1],
[3, 2],
[3, 3]
]
);

// N = 3, K = 3
let v = Vec::from_iter((1..4).array_combinations_with_reps());
assert_eq!(v.len(), 27);
assert_eq!(
&v[..8],
[
[1, 1, 1],
[1, 1, 2],
[1, 1, 3],
[1, 2, 1],
[1, 2, 2],
[1, 2, 3],
[1, 3, 1],
[1, 3, 2]
]
);

// N = 4, K = 4
let v = Vec::from_iter((1..5).array_combinations_with_reps::<4>());
assert_eq!(v.len(), 256);
}

0 comments on commit 9bc84b2

Please sign in to comment.