statrs/function/
factorial.rs1use crate::function::gamma;
5
6pub const MAX_FACTORIAL: usize = 170;
10
11pub fn factorial(x: u64) -> f64 {
19 let x = x as usize;
20 FCACHE.get(x).map_or(f64::INFINITY, |&fac| fac)
21}
22
23pub fn ln_factorial(x: u64) -> f64 {
30 let x = x as usize;
31 FCACHE
32 .get(x)
33 .map_or_else(|| gamma::ln_gamma(x as f64 + 1.0), |&fac| fac.ln())
34}
35
36pub fn binomial(n: u64, k: u64) -> f64 {
43 if k > n {
44 0.0
45 } else {
46 (0.5 + (ln_factorial(n) - ln_factorial(k) - ln_factorial(n - k)).exp()).floor()
47 }
48}
49
50pub fn ln_binomial(n: u64, k: u64) -> f64 {
57 if k > n {
58 f64::NEG_INFINITY
59 } else {
60 ln_factorial(n) - ln_factorial(k) - ln_factorial(n - k)
61 }
62}
63
64pub fn multinomial(n: u64, ni: &[u64]) -> f64 {
70 checked_multinomial(n, ni).unwrap()
71}
72
73pub fn checked_multinomial(n: u64, ni: &[u64]) -> Option<f64> {
77 let (sum, ret) = ni.iter().fold((0, ln_factorial(n)), |acc, &x| {
78 (acc.0 + x, acc.1 - ln_factorial(x))
79 });
80
81 if sum == n {
82 Some((0.5 + ret.exp()).floor())
83 } else {
84 None
85 }
86}
87
88const FCACHE: [f64; MAX_FACTORIAL + 1] = {
91 let mut fcache = [1.0; MAX_FACTORIAL + 1];
92
93 let mut i = 1;
95 while i < MAX_FACTORIAL + 1 {
96 fcache[i] = fcache[i - 1] * i as f64;
97 i += 1;
98 }
99
100 fcache
101};
102
103#[cfg(test)]
104mod tests {
105 use super::*;
106
107 #[test]
108 fn test_fcache() {
109 assert!((FCACHE[0] - 1.0).abs() < f64::EPSILON);
110 assert!((FCACHE[1] - 1.0).abs() < f64::EPSILON);
111 assert!((FCACHE[2] - 2.0).abs() < f64::EPSILON);
112 assert!((FCACHE[3] - 6.0).abs() < f64::EPSILON);
113 assert!((FCACHE[4] - 24.0).abs() < f64::EPSILON);
114 assert!((FCACHE[70] - 1197857166996989e85).abs() < f64::EPSILON);
115 assert!((FCACHE[170] - 7257415615307994e291).abs() < f64::EPSILON);
116 }
117
118 #[test]
119 fn test_factorial_and_ln_factorial() {
120 let mut fac = 1.0;
121 assert_eq!(factorial(0), fac);
122 for i in 1..171 {
123 fac *= i as f64;
124 assert_eq!(factorial(i), fac);
125 assert_eq!(ln_factorial(i), fac.ln());
126 }
127 }
128
129 #[test]
130 fn test_factorial_overflow() {
131 assert_eq!(factorial(172), f64::INFINITY);
132 assert_eq!(factorial(u64::MAX), f64::INFINITY);
133 }
134
135 #[test]
136 fn test_ln_factorial_does_not_overflow() {
137 assert_eq!(ln_factorial(1 << 10), 6078.2118847500501140);
138 assert_almost_eq!(ln_factorial(1 << 12), 29978.648060844048236, 1e-11);
139 assert_eq!(ln_factorial(1 << 15), 307933.81973375485425);
140 assert_eq!(ln_factorial(1 << 17), 1413421.9939462073242);
141 }
142
143 #[test]
144 fn test_binomial() {
145 assert_eq!(binomial(1, 1), 1.0);
146 assert_eq!(binomial(5, 2), 10.0);
147 assert_eq!(binomial(7, 3), 35.0);
148 assert_eq!(binomial(1, 0), 1.0);
149 assert_eq!(binomial(0, 1), 0.0);
150 assert_eq!(binomial(5, 7), 0.0);
151 }
152
153 #[test]
154 fn test_ln_binomial() {
155 assert_eq!(ln_binomial(1, 1), 1f64.ln());
156 assert_almost_eq!(ln_binomial(5, 2), 10f64.ln(), 1e-14);
157 assert_almost_eq!(ln_binomial(7, 3), 35f64.ln(), 1e-14);
158 assert_eq!(ln_binomial(1, 0), 1f64.ln());
159 assert_eq!(ln_binomial(0, 1), 0f64.ln());
160 assert_eq!(ln_binomial(5, 7), 0f64.ln());
161 }
162
163 #[test]
164 fn test_multinomial() {
165 assert_eq!(1.0, multinomial(1, &[1, 0]));
166 assert_eq!(10.0, multinomial(5, &[3, 2]));
167 assert_eq!(10.0, multinomial(5, &[2, 3]));
168 assert_eq!(35.0, multinomial(7, &[3, 4]));
169 }
170
171 #[test]
172 #[should_panic]
173 fn test_multinomial_bad_ni() {
174 multinomial(1, &[1, 1]);
175 }
176
177 #[test]
178 fn test_checked_multinomial_bad_ni() {
179 assert!(checked_multinomial(1, &[1, 1]).is_none());
180 }
181}