Skip to content
Merged
20 changes: 20 additions & 0 deletions src/combinations_with_replacement.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ use std::fmt;
use std::iter::FusedIterator;

use super::lazy_buffer::LazyBuffer;
use crate::combinations::checked_binomial;

/// An iterator to iterate through all the `n`-length combinations in an iterator, with replacement.
///
Expand Down Expand Up @@ -107,3 +108,22 @@ where
I: Iterator,
I::Item: Clone,
{}

/// For a given size `n`, return the count of remaining combinations with replacement or None if it would overflow.
fn remaining_for(n: usize, first: bool, indices: &[usize]) -> Option<usize> {
let count = |n: usize, k: usize| checked_binomial((n + k).saturating_sub(1), k);
let k = indices.len();
if first {
count(n, k)
} else {
indices
.iter()
.enumerate()
// TODO: Once the MSRV hits 1.37.0, we can sum options instead:
// .map(|(i, n0)| count(n - 1 - *n0, k - i))
// .sum()
.fold(Some(0), |sum, (i, n0)| {
sum.and_then(|s| s.checked_add(count(n - 1 - *n0, k - i)?))
})
}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I believe you that this is correct, but I have a hard time comprehending this without comments. Is it the "stars and sticks" thing that leads to binomial(n+k-1, k)?

Could you comment the loop similar to what we did in the other cases?

In addition: Could n+k overflow? And if so, shouldn't we return None here instead of panicking?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I chose the expression "stars and bars" from wikipedia, expression I did not know. I added a comment.
Good catch n+k overflowing, handled.
And I copied/updated the previous comment about the loop.

}