Skip to content

Commit

Permalink
Move checked_binomial
Browse files Browse the repository at this point in the history
Just cut/paste, and fixed imports.
It is moved because it is now needed even without heap-allocation.
  • Loading branch information
Philippe-Cholet authored and jswrenn committed Sep 28, 2023
1 parent 154d15e commit 99b35f5
Show file tree
Hide file tree
Showing 4 changed files with 41 additions and 39 deletions.
37 changes: 36 additions & 1 deletion src/adaptors/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,6 @@ pub use self::map::{map_into, map_ok, MapInto, MapOk};
#[cfg(feature = "use_alloc")]
pub use self::multi_product::*;

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

// https://en.wikipedia.org/wiki/Binomial_coefficient#In_programming_languages
pub(crate) fn checked_binomial(mut n: usize, mut k: usize) -> Option<usize> {
if n < k {
return Some(0);
}
// `factorial(n) / factorial(n - k) / factorial(k)` but trying to avoid it overflows:
k = (n - k).min(k); // symmetry
let mut c = 1;
for i in 1..=k {
c = (c / i)
.checked_mul(n)?
.checked_add((c % i).checked_mul(n)? / i)?;
n -= 1;
}
Some(c)
}

#[test]
fn test_checked_binomial() {
// With the first row: [1, 0, 0, ...] and the first column full of 1s, we check
// row by row the recurrence relation of binomials (which is an equivalent definition).
// For n >= 1 and k >= 1 we have:
// binomial(n, k) == binomial(n - 1, k - 1) + binomial(n - 1, k)
const LIMIT: usize = 500;
let mut row = vec![Some(0); LIMIT + 1];
row[0] = Some(1);
for n in 0..=LIMIT {
for k in 0..=LIMIT {
assert_eq!(row[k], checked_binomial(n, k));
}
row = std::iter::once(Some(1))
.chain((1..=LIMIT).map(|k| row[k - 1]?.checked_add(row[k]?)))
.collect();
}
}

/// An iterator adapter to filter values within a nested `Result::Ok`.
///
/// See [`.filter_ok()`](crate::Itertools::filter_ok) for more information.
Expand Down
38 changes: 2 additions & 36 deletions src/combinations.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@ use std::iter::FusedIterator;
use super::lazy_buffer::LazyBuffer;
use alloc::vec::Vec;

use crate::adaptors::checked_binomial;

/// An iterator to iterate through all the `k`-length combinations in an iterator.
///
/// See [`.combinations()`](crate::Itertools::combinations) for more information.
Expand Down Expand Up @@ -160,42 +162,6 @@ where
{
}

// https://en.wikipedia.org/wiki/Binomial_coefficient#In_programming_languages
pub(crate) fn checked_binomial(mut n: usize, mut k: usize) -> Option<usize> {
if n < k {
return Some(0);
}
// `factorial(n) / factorial(n - k) / factorial(k)` but trying to avoid it overflows:
k = (n - k).min(k); // symmetry
let mut c = 1;
for i in 1..=k {
c = (c / i)
.checked_mul(n)?
.checked_add((c % i).checked_mul(n)? / i)?;
n -= 1;
}
Some(c)
}

#[test]
fn test_checked_binomial() {
// With the first row: [1, 0, 0, ...] and the first column full of 1s, we check
// row by row the recurrence relation of binomials (which is an equivalent definition).
// For n >= 1 and k >= 1 we have:
// binomial(n, k) == binomial(n - 1, k - 1) + binomial(n - 1, k)
const LIMIT: usize = 500;
let mut row = vec![Some(0); LIMIT + 1];
row[0] = Some(1);
for n in 0..=LIMIT {
for k in 0..=LIMIT {
assert_eq!(row[k], checked_binomial(n, k));
}
row = std::iter::once(Some(1))
.chain((1..=LIMIT).map(|k| row[k - 1]?.checked_add(row[k]?)))
.collect();
}
}

/// For a given size `n`, return the count of remaining combinations or None if it would overflow.
fn remaining_for(n: usize, first: bool, indices: &[usize]) -> Option<usize> {
let k = indices.len();
Expand Down
2 changes: 1 addition & 1 deletion src/combinations_with_replacement.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ use std::fmt;
use std::iter::FusedIterator;

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

/// An iterator to iterate through all the `n`-length combinations in an iterator, with replacement.
///
Expand Down
3 changes: 2 additions & 1 deletion src/powerset.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,8 @@ use std::fmt;
use std::iter::FusedIterator;
use std::usize;

use super::combinations::{checked_binomial, combinations, Combinations};
use super::combinations::{combinations, Combinations};
use crate::adaptors::checked_binomial;
use crate::size_hint::{self, SizeHint};

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

0 comments on commit 99b35f5

Please sign in to comment.