Skip to content

Commit 99b35f5

Browse files
Philippe-Choletjswrenn
authored andcommitted
Move checked_binomial
Just cut/paste, and fixed imports. It is moved because it is now needed even without heap-allocation.
1 parent 154d15e commit 99b35f5

File tree

4 files changed

+41
-39
lines changed

4 files changed

+41
-39
lines changed

src/adaptors/mod.rs

Lines changed: 36 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,6 @@ pub use self::map::{map_into, map_ok, MapInto, MapOk};
1414
#[cfg(feature = "use_alloc")]
1515
pub use self::multi_product::*;
1616

17-
use crate::combinations::checked_binomial;
1817
use crate::size_hint::{self, SizeHint};
1918
use std::fmt;
2019
use std::iter::{FromIterator, Fuse, FusedIterator};
@@ -767,6 +766,42 @@ impl_tuple_combination!(Tuple10Combination Tuple9Combination; a b c d e f g h i)
767766
impl_tuple_combination!(Tuple11Combination Tuple10Combination; a b c d e f g h i j);
768767
impl_tuple_combination!(Tuple12Combination Tuple11Combination; a b c d e f g h i j k);
769768

769+
// https://en.wikipedia.org/wiki/Binomial_coefficient#In_programming_languages
770+
pub(crate) fn checked_binomial(mut n: usize, mut k: usize) -> Option<usize> {
771+
if n < k {
772+
return Some(0);
773+
}
774+
// `factorial(n) / factorial(n - k) / factorial(k)` but trying to avoid it overflows:
775+
k = (n - k).min(k); // symmetry
776+
let mut c = 1;
777+
for i in 1..=k {
778+
c = (c / i)
779+
.checked_mul(n)?
780+
.checked_add((c % i).checked_mul(n)? / i)?;
781+
n -= 1;
782+
}
783+
Some(c)
784+
}
785+
786+
#[test]
787+
fn test_checked_binomial() {
788+
// With the first row: [1, 0, 0, ...] and the first column full of 1s, we check
789+
// row by row the recurrence relation of binomials (which is an equivalent definition).
790+
// For n >= 1 and k >= 1 we have:
791+
// binomial(n, k) == binomial(n - 1, k - 1) + binomial(n - 1, k)
792+
const LIMIT: usize = 500;
793+
let mut row = vec![Some(0); LIMIT + 1];
794+
row[0] = Some(1);
795+
for n in 0..=LIMIT {
796+
for k in 0..=LIMIT {
797+
assert_eq!(row[k], checked_binomial(n, k));
798+
}
799+
row = std::iter::once(Some(1))
800+
.chain((1..=LIMIT).map(|k| row[k - 1]?.checked_add(row[k]?)))
801+
.collect();
802+
}
803+
}
804+
770805
/// An iterator adapter to filter values within a nested `Result::Ok`.
771806
///
772807
/// See [`.filter_ok()`](crate::Itertools::filter_ok) for more information.

src/combinations.rs

Lines changed: 2 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,8 @@ use std::iter::FusedIterator;
44
use super::lazy_buffer::LazyBuffer;
55
use alloc::vec::Vec;
66

7+
use crate::adaptors::checked_binomial;
8+
79
/// An iterator to iterate through all the `k`-length combinations in an iterator.
810
///
911
/// See [`.combinations()`](crate::Itertools::combinations) for more information.
@@ -160,42 +162,6 @@ where
160162
{
161163
}
162164

163-
// https://en.wikipedia.org/wiki/Binomial_coefficient#In_programming_languages
164-
pub(crate) fn checked_binomial(mut n: usize, mut k: usize) -> Option<usize> {
165-
if n < k {
166-
return Some(0);
167-
}
168-
// `factorial(n) / factorial(n - k) / factorial(k)` but trying to avoid it overflows:
169-
k = (n - k).min(k); // symmetry
170-
let mut c = 1;
171-
for i in 1..=k {
172-
c = (c / i)
173-
.checked_mul(n)?
174-
.checked_add((c % i).checked_mul(n)? / i)?;
175-
n -= 1;
176-
}
177-
Some(c)
178-
}
179-
180-
#[test]
181-
fn test_checked_binomial() {
182-
// With the first row: [1, 0, 0, ...] and the first column full of 1s, we check
183-
// row by row the recurrence relation of binomials (which is an equivalent definition).
184-
// For n >= 1 and k >= 1 we have:
185-
// binomial(n, k) == binomial(n - 1, k - 1) + binomial(n - 1, k)
186-
const LIMIT: usize = 500;
187-
let mut row = vec![Some(0); LIMIT + 1];
188-
row[0] = Some(1);
189-
for n in 0..=LIMIT {
190-
for k in 0..=LIMIT {
191-
assert_eq!(row[k], checked_binomial(n, k));
192-
}
193-
row = std::iter::once(Some(1))
194-
.chain((1..=LIMIT).map(|k| row[k - 1]?.checked_add(row[k]?)))
195-
.collect();
196-
}
197-
}
198-
199165
/// For a given size `n`, return the count of remaining combinations or None if it would overflow.
200166
fn remaining_for(n: usize, first: bool, indices: &[usize]) -> Option<usize> {
201167
let k = indices.len();

src/combinations_with_replacement.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ use std::fmt;
33
use std::iter::FusedIterator;
44

55
use super::lazy_buffer::LazyBuffer;
6-
use crate::combinations::checked_binomial;
6+
use crate::adaptors::checked_binomial;
77

88
/// An iterator to iterate through all the `n`-length combinations in an iterator, with replacement.
99
///

src/powerset.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,8 @@ use std::fmt;
33
use std::iter::FusedIterator;
44
use std::usize;
55

6-
use super::combinations::{checked_binomial, combinations, Combinations};
6+
use super::combinations::{combinations, Combinations};
7+
use crate::adaptors::checked_binomial;
78
use crate::size_hint::{self, SizeHint};
89

910
/// An iterator to iterate through the powerset of the elements from an iterator.

0 commit comments

Comments
 (0)