Skip to content

Commit

Permalink
Move 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 1493a16 commit 02fcc37
Show file tree
Hide file tree
Showing 17 changed files with 194 additions and 157 deletions.
4 changes: 4 additions & 0 deletions Cargo.toml
Expand Up @@ -42,6 +42,7 @@ full = [
"cartesian_product",
"circular_array_windows",
"combinations",
"combinations_with_reps",
"min_max",
"next_chunk",
"sorted",
Expand All @@ -68,6 +69,9 @@ circular_array_windows = ["array_windows"]
# Enables the `.combinations()` adaptor for iterators
combinations = ["alloc"]

# Enables the `.combinations_with_reps()` adaptor for iterators
combinations_with_reps = ["alloc"]

# Enables the `.min_max()` and friends methods on iterators
min_max = []

Expand Down
2 changes: 1 addition & 1 deletion README.md
Expand Up @@ -89,7 +89,7 @@ itermore = { version = "*", default-features = false, features = ["array_combin
[`cartesian_product`]: IterCartesianProduct::cartesian_product
[`circular_array_windows`]: IterCircularArrayWindows::circular_array_windows
[`combinations`]: IterCombinations::combinations
[`combinations_with_reps`]: IterCombinations::combinations_with_reps
[`combinations_with_reps`]: IterCombinationsWithReps::combinations_with_reps

## License

Expand Down
91 changes: 1 addition & 90 deletions src/adaptors/combinations.rs
Expand Up @@ -3,11 +3,9 @@ use core::iter::FusedIterator;

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

/// An extension trait that provides the [`combinations`] and
/// [`combinations_with_reps`] methods for iterators.
/// An extension trait that provides the [`combinations`] method for iterators.
///
/// [`combinations`]: IterCombinations::combinations
/// [`combinations_with_reps`]: IterCombinations::combinations_with_reps
#[cfg_attr(docsrs, doc(cfg(feature = "combinations")))]
pub trait IterCombinations: Iterator {
/// Returns an iterator adaptor that iterates over `k` length combinations
Expand Down Expand Up @@ -42,40 +40,6 @@ pub trait IterCombinations: Iterator {
{
Combinations::new(self, k)
}

/// 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::IterCombinations;
///
/// let mut iter = "ab".chars().combinations_with_reps(3);
/// assert_eq!(iter.next(), Some(vec!['a', 'a', 'a']));
/// assert_eq!(iter.next(), Some(vec!['a', 'a', 'b']));
/// assert_eq!(iter.next(), Some(vec!['a', 'b', 'a']));
/// assert_eq!(iter.next(), Some(vec!['a', 'b', 'b']));
/// assert_eq!(iter.next(), Some(vec!['b', 'a', 'a']));
/// // etc
/// ```
#[inline]
fn combinations_with_reps(self, k: usize) -> CombinationsWithReps<Self>
where
Self: Sized,
Self::Item: Clone,
{
CombinationsWithReps::new(self, k)
}
}

impl<I: ?Sized> IterCombinations for I where I: Iterator {}
Expand Down Expand Up @@ -143,56 +107,3 @@ where
I::Item: Clone,
{
}

/// 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 [`combinations_with_reps`] method on
/// iterators. See its documentation for more.
///
/// [`combinations_with_reps`]: IterCombinations::combinations_with_reps
#[cfg_attr(docsrs, doc(cfg(feature = "combinations")))]
#[must_use = "iterators are lazy and do nothing unless consumed"]
pub struct CombinationsWithReps<I>(GenericCombinations<I, Vec<usize>>)
where
I: Iterator;

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

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

impl<I> Iterator for CombinationsWithReps<I>
where
I: Iterator,
I::Item: Clone,
{
type Item = Vec<I::Item>;

fn next(&mut self) -> Option<Self::Item> {
self.0.fill_next_with_reps().map(Vec::from_iter)
}
}

impl<I> FusedIterator for CombinationsWithReps<I>
where
I: Iterator,
I::Item: Clone,
{
}
100 changes: 100 additions & 0 deletions src/adaptors/combinations_with_reps.rs
@@ -0,0 +1,100 @@
use core::fmt;
use core::iter::FusedIterator;

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

/// An extension trait that provides the [`combinations_with_reps`] method for
/// iterators.
///
/// [`combinations_with_reps`]: IterCombinationsWithReps::combinations_with_reps
#[cfg_attr(docsrs, doc(cfg(feature = "combinations_with_reps")))]
pub trait IterCombinationsWithReps: 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::IterCombinationsWithReps;
///
/// let mut iter = "ab".chars().combinations_with_reps(3);
/// assert_eq!(iter.next(), Some(vec!['a', 'a', 'a']));
/// assert_eq!(iter.next(), Some(vec!['a', 'a', 'b']));
/// assert_eq!(iter.next(), Some(vec!['a', 'b', 'a']));
/// assert_eq!(iter.next(), Some(vec!['a', 'b', 'b']));
/// assert_eq!(iter.next(), Some(vec!['b', 'a', 'a']));
/// // etc
/// ```
#[inline]
fn combinations_with_reps(self, k: usize) -> CombinationsWithReps<Self>
where
Self: Sized,
Self::Item: Clone,
{
CombinationsWithReps::new(self, k)
}
}

impl<I: ?Sized> IterCombinationsWithReps 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 [`combinations_with_reps`] method on
/// iterators. See its documentation for more.
///
/// [`combinations_with_reps`]: IterCombinationsWithReps::combinations_with_reps
#[cfg_attr(docsrs, doc(cfg(feature = "combinations_with_reps")))]
#[must_use = "iterators are lazy and do nothing unless consumed"]
pub struct CombinationsWithReps<I>(GenericCombinations<I, Vec<usize>>)
where
I: Iterator;

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

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

impl<I> Iterator for CombinationsWithReps<I>
where
I: Iterator,
I::Item: Clone,
{
type Item = Vec<I::Item>;

fn next(&mut self) -> Option<Self::Item> {
self.0.fill_next_with_reps().map(Vec::from_iter)
}
}

impl<I> FusedIterator for CombinationsWithReps<I>
where
I: Iterator,
I::Item: Clone,
{
}
17 changes: 13 additions & 4 deletions src/adaptors/generic_combinations.rs
Expand Up @@ -34,7 +34,10 @@ where
enum State {
First,
Normal,
#[cfg(any(feature = "array_combinations_with_reps", feature = "combinations"))]
#[cfg(any(
feature = "array_combinations_with_reps",
feature = "combinations_with_reps",
))]
Done,
}

Expand Down Expand Up @@ -74,8 +77,11 @@ where
let k = self.comb.as_ref().len();

match self.state {
#[cfg(any(feature = "array_combinations_with_reps", feature = "combinations"))]
State::Done => return None,
#[cfg(any(
feature = "array_combinations_with_reps",
feature = "combinations_with_reps",
))]
State::Done => unreachable!(),

State::First => {
// Fill the buffer with k elements from the iterator.
Expand Down Expand Up @@ -144,7 +150,10 @@ where
Some(self.comb.as_ref().iter().map(|&d| self.buf[d].clone()))
}

#[cfg(any(feature = "array_combinations_with_reps", feature = "combinations"))]
#[cfg(any(
feature = "array_combinations_with_reps",
feature = "combinations_with_reps",
))]
pub fn fill_next_with_reps(&mut self) -> Option<impl Iterator<Item = I::Item> + '_>
where
I::Item: Clone,
Expand Down
5 changes: 4 additions & 1 deletion src/adaptors/mod.rs
Expand Up @@ -12,9 +12,12 @@ pub mod cartesian_product;
pub mod circular_array_windows;
#[cfg(feature = "combinations")]
pub mod combinations;
#[cfg(feature = "combinations_with_reps")]
pub mod combinations_with_reps;
#[cfg(any(
feature = "array_combinations",
feature = "array_combinations_with_reps",
feature = "combinations"
feature = "combinations",
feature = "combinations_with_reps"
))]
mod generic_combinations;
10 changes: 8 additions & 2 deletions src/lib.rs
Expand Up @@ -78,7 +78,7 @@
//! [`cartesian_product`]: IterCartesianProduct::cartesian_product
//! [`circular_array_windows`]: IterCircularArrayWindows::circular_array_windows
//! [`combinations`]: IterCombinations::combinations
//! [`combinations_with_reps`]: IterCombinations::combinations_with_reps
//! [`combinations_with_reps`]: IterCombinationsWithReps::combinations_with_reps
//! [`min_max`]: IterMinMax::min_max
//! [`sorted`]: IterSorted::sorted

Expand Down Expand Up @@ -122,7 +122,10 @@ pub use crate::adaptors::cartesian_product::{CartesianProduct, IterCartesianProd
pub use crate::adaptors::circular_array_windows::{CircularArrayWindows, IterCircularArrayWindows};

#[cfg(feature = "combinations")]
pub use crate::adaptors::combinations::{Combinations, CombinationsWithReps, IterCombinations};
pub use crate::adaptors::combinations::{Combinations, IterCombinations};

#[cfg(feature = "combinations_with_reps")]
pub use crate::adaptors::combinations_with_reps::{CombinationsWithReps, IterCombinationsWithReps};

#[cfg(feature = "min_max")]
pub use crate::xtraits::min_max::IterMinMax;
Expand Down Expand Up @@ -163,6 +166,9 @@ pub mod prelude {
#[cfg(feature = "combinations")]
pub use super::IterCombinations;

#[cfg(feature = "combinations_with_reps")]
pub use super::IterCombinationsWithReps;

#[cfg(feature = "min_max")]
pub use super::IterMinMax;

Expand Down
2 changes: 1 addition & 1 deletion tests/array_chunks.rs
Expand Up @@ -3,7 +3,7 @@

use core::iter;

use itermore::IterArrayChunks;
use itermore::prelude::*;

#[test]
fn array_chunks_infer() {
Expand Down
2 changes: 1 addition & 1 deletion tests/array_combinations.rs
@@ -1,6 +1,6 @@
#![cfg(feature = "array_combinations")]

use itermore::IterArrayCombinations;
use itermore::prelude::*;

#[test]
#[should_panic]
Expand Down
2 changes: 1 addition & 1 deletion tests/array_combinations_with_reps.rs
@@ -1,6 +1,6 @@
#![cfg(feature = "array_combinations_with_reps")]

use itermore::IterArrayCombinationsWithReps;
use itermore::prelude::*;

#[test]
fn array_combinations_with_reps_smoke() {
Expand Down
2 changes: 1 addition & 1 deletion tests/array_windows.rs
Expand Up @@ -2,7 +2,7 @@

use core::iter;

use itermore::IterArrayWindows;
use itermore::prelude::*;

#[test]
fn array_windows_infer() {
Expand Down
3 changes: 2 additions & 1 deletion tests/cartesian_product.rs
Expand Up @@ -3,7 +3,8 @@

use std::iter;

use itermore::{cartesian_product, IterCartesianProduct};
use itermore::cartesian_product;
use itermore::prelude::*;

#[test]
fn cartesian_product() {
Expand Down
2 changes: 1 addition & 1 deletion tests/circular_array_windows.rs
@@ -1,6 +1,6 @@
#![cfg(feature = "circular_array_windows")]

use itermore::IterCircularArrayWindows;
use itermore::prelude::*;

#[test]
fn circular_array_windows_infer() {
Expand Down

0 comments on commit 02fcc37

Please sign in to comment.