Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Move array combinations with reps to own adaptor
- Loading branch information
1 parent
72a0c8c
commit 9bc84b2
Showing
7 changed files
with
171 additions
and
148 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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, | ||
{ | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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); | ||
} |