Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Complete Permutations::size_hint #739

Merged
merged 9 commits into from
Sep 6, 2023
76 changes: 33 additions & 43 deletions src/permutations.rs
Original file line number Diff line number Diff line change
Expand Up @@ -47,11 +47,6 @@ enum CompleteState {
}
}

enum CompleteStateRemaining {
Known(usize),
Overflow,
}

Philippe-Cholet marked this conversation as resolved.
Show resolved Hide resolved
impl<I> fmt::Debug for Permutations<I>
where I: Iterator + fmt::Debug,
I::Item: fmt::Debug,
Expand All @@ -72,14 +67,8 @@ pub fn permutations<I: Iterator>(iter: I, k: usize) -> Permutations<I> {
};
}

let mut enough_vals = true;

while vals.len() < k {
if !vals.get_next() {
enough_vals = false;
break;
}
}
vals.prefill(k);
let enough_vals = vals.len() == k;

let state = if enough_vals {
PermutationState::StartUnknownLen { k }
Expand Down Expand Up @@ -123,12 +112,7 @@ where

fn count(self) -> usize {
fn from_complete(complete_state: CompleteState) -> usize {
match complete_state.remaining() {
CompleteStateRemaining::Known(count) => count,
CompleteStateRemaining::Overflow => {
panic!("Iterator count greater than usize::MAX");
}
}
complete_state.remaining().expect("Iterator count greater than usize::MAX")
}

let Permutations { vals, state } = self;
Expand All @@ -153,11 +137,29 @@ where

fn size_hint(&self) -> (usize, Option<usize>) {
match self.state {
PermutationState::StartUnknownLen { .. } |
PermutationState::OngoingUnknownLen { .. } => (0, None), // TODO can we improve this lower bound?
PermutationState::StartUnknownLen { k } => {
let (mut low, mut upp) = self.vals.size_hint();
low = CompleteState::Start { n: low, k }.remaining().unwrap_or(usize::MAX);
upp = upp.and_then(|n| CompleteState::Start { n, k }.remaining());
(low, upp)
}
PermutationState::OngoingUnknownLen { k, min_n } => {
let prev_iteration_count = min_n - k + 1;
let (mut low, mut upp) = self.vals.size_hint();
low = CompleteState::Start { n: low, k }
.remaining()
.unwrap_or(usize::MAX)
.saturating_sub(prev_iteration_count);
upp = upp.and_then(|n| {
CompleteState::Start { n, k }
.remaining()
.map(|count| count.saturating_sub(prev_iteration_count))
jswrenn marked this conversation as resolved.
Show resolved Hide resolved
});
(low, upp)
}
PermutationState::Complete(ref state) => match state.remaining() {
CompleteStateRemaining::Known(count) => (count, Some(count)),
CompleteStateRemaining::Overflow => (::std::usize::MAX, None)
Some(count) => (count, Some(count)),
None => (::std::usize::MAX, None)
}
PermutationState::Empty => (0, Some(0))
}
jswrenn marked this conversation as resolved.
Show resolved Hide resolved
Expand Down Expand Up @@ -238,39 +240,27 @@ impl CompleteState {
}
}

fn remaining(&self) -> CompleteStateRemaining {
use self::CompleteStateRemaining::{Known, Overflow};

/// Returns the count of remaining permutations, or None if it would overflow.
fn remaining(&self) -> Option<usize> {
match *self {
CompleteState::Start { n, k } => {
if n < k {
return Known(0);
return Some(0);
}

let count: Option<usize> = (n - k + 1..n + 1).fold(Some(1), |acc, i| {
(n - k + 1..n + 1).fold(Some(1), |acc, i| {
acc.and_then(|acc| acc.checked_mul(i))
});

match count {
Some(count) => Known(count),
None => Overflow
}
})
}
CompleteState::Ongoing { ref indices, ref cycles } => {
let mut count: usize = 0;

for (i, &c) in cycles.iter().enumerate() {
let radix = indices.len() - i;
let next_count = count.checked_mul(radix)
.and_then(|count| count.checked_add(c));

count = match next_count {
Some(count) => count,
None => { return Overflow; }
};
count = count.checked_mul(radix)
.and_then(|count| count.checked_add(c))?;
}

Known(count)
Some(count)
}
}
}
Expand Down
26 changes: 26 additions & 0 deletions tests/test_std.rs
Original file line number Diff line number Diff line change
Expand Up @@ -986,6 +986,32 @@ fn permutations_zero() {
it::assert_equal((0..0).permutations(0), vec![vec![]]);
}

#[test]
fn permutations_range_count() {
for n in 0..=7 {
for k in 0..=7 {
let len = if k <= n {
(n - k + 1..=n).product()
} else {
0
};
let mut it = (0..n).permutations(k);
assert_eq!(len, it.clone().count());
assert_eq!(len, it.size_hint().0);
assert_eq!(Some(len), it.size_hint().1);
for count in (0..len).rev() {
let elem = it.next();
assert!(elem.is_some());
assert_eq!(count, it.clone().count());
assert_eq!(count, it.size_hint().0);
assert_eq!(Some(count), it.size_hint().1);
}
let should_be_none = it.next();
assert!(should_be_none.is_none());
}
}
}
jswrenn marked this conversation as resolved.
Show resolved Hide resolved

#[test]
fn combinations_with_replacement() {
// Pool smaller than n
Expand Down