Skip to content

Commit

Permalink
Distinguish different degenerated cases.
Browse files Browse the repository at this point in the history
  • Loading branch information
iago-lito committed Oct 9, 2020
1 parent 8c5f630 commit 9027ef0
Show file tree
Hide file tree
Showing 2 changed files with 28 additions and 11 deletions.
32 changes: 21 additions & 11 deletions src/adaptors/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -373,19 +373,24 @@ pub enum Power<I>
where
I: Iterator,
{
/// The adaptor is empty if either:
/// - pow is 0
/// - the original iterator is empty
/// - the adaptor is exhausted
/// The adaptor is 'Empty' if either:
/// - The original iterator is empty.
/// - The adaptor is exhausted.
Empty,
/// Otherwise, there will be items yielded.
/// The adaptor is 'Degenerated' if the given pow == 0.
/// In this case, it yields 1 empty item before switching to Empty itself.
/// This ensures that cartesian_power(it, n).count() == it.count().pow(n) for all n.
/// In the case it.is_empty() && n == 0, we choose convention 0^0=1.
Degenerated,
/// In any other case, there will be non-empty items yielded.
Filled {
/// Copy of the original iterator, never consumed.
original: I,
/// Clones of the original iterator, scrolled at various speeds
/// Clones of the original iterator,
/// scrolled at various speeds from left to right
/// and regularly replaced by fresh clones once exhausted.
clones: Vec<I>,
/// Current state of the iterator: the next item to be yielded.
/// Current state of the iterator: the next non-empty item to be yielded.
state: Vec<I::Item>,
},
}
Expand All @@ -399,14 +404,13 @@ where
I::Item: Clone,
{
match pow {
// Trivial case.
0 => Power::Empty,
0 => Power::Degenerated,
pow => {
// Test one clone first to determine whether
// some values are actually yielded by the given iterator.
let mut first_clone = it.clone();
match first_clone.next() {
// New trivial case: the given iterator is empty.
// No item will be yielded if the iterator is empty.
None => Power::Empty,
Some(first_state) => {
// Produce other clones until we have `pow` of them.
Expand Down Expand Up @@ -471,13 +475,19 @@ where
}
Some(res)
}
// Check trivial case last as it should be less frequent.
// Check les frequent cases last.
Power::Empty => None,
Power::Degenerated => {
// Yield One empty item and get exhausted.
*self = Power::Empty;
Some(Vec::new())
}
}
}

fn size_hint(&self) -> (usize, Option<usize>) {
match self {
Power::Degenerated => (1, Some(1)),
Power::Empty => (0, Some(0)),
Power::Filled {
original, clones, ..
Expand Down
7 changes: 7 additions & 0 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1003,6 +1003,13 @@ pub trait Itertools : Iterator {
/// ['b', 'b', 'b'],
/// ],
/// );
///
/// // Watch various possible degenerated cases:
/// let empty_result: Vec<Vec<char>> = vec![];
/// let singleton_result: Vec<Vec<char>> = vec![vec![]];
/// itertools::assert_equal("".chars().cartesian_power(3), empty_result.clone());
/// itertools::assert_equal("ab".chars().cartesian_power(0), singleton_result.clone());
/// itertools::assert_equal("".chars().cartesian_power(0), singleton_result.clone());
/// ```
fn cartesian_power(self, pow: usize) -> Power<Self>
where
Expand Down

0 comments on commit 9027ef0

Please sign in to comment.