statrs/function/
factorial.rs1use crate::error::StatsError;
5use crate::function::gamma;
6use crate::Result;
7use core::f64::INFINITY as INF;
8
9pub const MAX_FACTORIAL: usize = 170;
13
14pub fn factorial(x: u64) -> f64 {
22 let x = x as usize;
23 FCACHE.get(x).map_or(INF, |&fac| fac)
24}
25
26pub fn ln_factorial(x: u64) -> f64 {
33 let x = x as usize;
34 FCACHE
35 .get(x)
36 .map_or_else(|| gamma::ln_gamma(x as f64 + 1.0), |&fac| fac.ln())
37}
38
39pub fn binomial(n: u64, k: u64) -> f64 {
46 if k > n {
47 0.0
48 } else {
49 (0.5 + (ln_factorial(n) - ln_factorial(k) - ln_factorial(n - k)).exp()).floor()
50 }
51}
52
53pub fn ln_binomial(n: u64, k: u64) -> f64 {
60 if k > n {
61 f64::NEG_INFINITY
62 } else {
63 ln_factorial(n) - ln_factorial(k) - ln_factorial(n - k)
64 }
65}
66
67pub fn multinomial(n: u64, ni: &[u64]) -> f64 {
73 checked_multinomial(n, ni).unwrap()
74}
75
76pub fn checked_multinomial(n: u64, ni: &[u64]) -> Result<f64> {
82 let (sum, ret) = ni.iter().fold((0, ln_factorial(n)), |acc, &x| {
83 (acc.0 + x, acc.1 - ln_factorial(x))
84 });
85 if sum != n {
86 Err(StatsError::ContainerExpectedSumVar("ni", "n"))
87 } else {
88 Ok((0.5 + ret.exp()).floor())
89 }
90}
91
92lazy_static! {
95 static ref FCACHE: [f64; MAX_FACTORIAL + 1] = {
96 let mut fcache = [1.0; MAX_FACTORIAL + 1];
97 fcache
98 .iter_mut()
99 .enumerate()
100 .skip(1)
101 .fold(1.0, |acc, (i, elt)| {
102 let fac = acc * i as f64;
103 *elt = fac;
104 fac
105 });
106 fcache
107 };
108}
109
110#[cfg(test)]
111mod tests {
112 use super::*;
113
114 #[test]
115 fn test_factorial_and_ln_factorial() {
116 let mut fac = 1.0;
117 assert_eq!(factorial(0), fac);
118 for i in 1..171 {
119 fac *= i as f64;
120 assert_eq!(factorial(i), fac);
121 assert_eq!(ln_factorial(i), fac.ln());
122 }
123 }
124
125 #[test]
126 fn test_factorial_overflow() {
127 assert_eq!(factorial(172), INF);
128 assert_eq!(factorial(u64::MAX), INF);
129 }
130
131 #[test]
132 fn test_ln_factorial_does_not_overflow() {
133 assert_eq!(ln_factorial(1 << 10), 6078.2118847500501140);
134 assert_almost_eq!(ln_factorial(1 << 12), 29978.648060844048236, 1e-11);
135 assert_eq!(ln_factorial(1 << 15), 307933.81973375485425);
136 assert_eq!(ln_factorial(1 << 17), 1413421.9939462073242);
137 }
138
139 #[test]
140 fn test_binomial() {
141 assert_eq!(binomial(1, 1), 1.0);
142 assert_eq!(binomial(5, 2), 10.0);
143 assert_eq!(binomial(7, 3), 35.0);
144 assert_eq!(binomial(1, 0), 1.0);
145 assert_eq!(binomial(0, 1), 0.0);
146 assert_eq!(binomial(5, 7), 0.0);
147 }
148
149 #[test]
150 fn test_ln_binomial() {
151 assert_eq!(ln_binomial(1, 1), 1f64.ln());
152 assert_almost_eq!(ln_binomial(5, 2), 10f64.ln(), 1e-14);
153 assert_almost_eq!(ln_binomial(7, 3), 35f64.ln(), 1e-14);
154 assert_eq!(ln_binomial(1, 0), 1f64.ln());
155 assert_eq!(ln_binomial(0, 1), 0f64.ln());
156 assert_eq!(ln_binomial(5, 7), 0f64.ln());
157 }
158
159 #[test]
160 fn test_multinomial() {
161 assert_eq!(1.0, multinomial(1, &[1, 0]));
162 assert_eq!(10.0, multinomial(5, &[3, 2]));
163 assert_eq!(10.0, multinomial(5, &[2, 3]));
164 assert_eq!(35.0, multinomial(7, &[3, 4]));
165 }
166
167 #[test]
168 #[should_panic]
169 fn test_multinomial_bad_ni() {
170 multinomial(1, &[1, 1]);
171 }
172
173 #[test]
174 fn test_checked_multinomial_bad_ni() {
175 assert!(checked_multinomial(1, &[1, 1]).is_err());
176 }
177}