nd_300/speedtest/
stat_primitives.rs1pub fn quantile(sorted: &[f64], p: f64) -> f64 {
27 let n = sorted.len();
28 if n == 0 {
29 return f64::NAN;
30 }
31 if n == 1 {
32 return sorted[0];
33 }
34 let h = (n - 1) as f64 * p;
35 let lo = h.floor() as usize;
36 let hi = h.ceil() as usize;
37 sorted[lo] + (h - lo as f64) * (sorted[hi] - sorted[lo])
38}
39
40pub const PCG32_MULT: u64 = 6364136223846793005;
44pub const PCG32_DEFAULT_STATE: u64 = 0x853c49e6748fea9b;
46pub const PCG32_DEFAULT_INC: u64 = 0xda3e39cb94b95bdb;
48
49pub fn lemire_bounded(u32_val: u32, n: usize) -> usize {
54 ((u32_val as u64 * n as u64) >> 32) as usize
55}
56
57#[derive(Debug, Clone)]
63pub struct Pcg32 {
64 state: u64,
65 inc: u64,
66}
67
68impl Pcg32 {
69 pub fn new() -> Self {
71 Self {
72 state: PCG32_DEFAULT_STATE,
73 inc: PCG32_DEFAULT_INC,
74 }
75 }
76
77 pub fn with_seed(state: u64, inc: u64) -> Self {
80 Self { state, inc }
81 }
82
83 pub fn next_u32(&mut self) -> u32 {
85 let old = self.state;
86 self.state = old.wrapping_mul(PCG32_MULT).wrapping_add(self.inc);
87 let xorshifted = ((((old >> 18) ^ old) >> 27) & 0xffff_ffff) as u32;
88 let rot = (old >> 59) as u32; xorshifted.rotate_right(rot)
90 }
91
92 pub fn bounded_index(&mut self, n: usize) -> usize {
94 lemire_bounded(self.next_u32(), n)
95 }
96
97 pub fn next_f64(&mut self) -> f64 {
99 self.next_u32() as f64 / 4_294_967_296.0
100 }
101}
102
103impl Default for Pcg32 {
104 fn default() -> Self {
105 Self::new()
106 }
107}
108
109#[allow(clippy::excessive_precision)]
117pub fn inv_normal(p: f64) -> f64 {
118 if p <= 0.0 {
119 return f64::NEG_INFINITY;
120 }
121 if p >= 1.0 {
122 return f64::INFINITY;
123 }
124
125 let a1 = -3.969683028665376e1;
126 let a2 = 2.209460984245205e2;
127 let a3 = -2.759285104469687e2;
128 let a4 = 1.38357751867269e2;
129 let a5 = -3.066479806614716e1;
130 let a6 = 2.506628277459239e0;
131 let b1 = -5.447609879822406e1;
132 let b2 = 1.615858368580409e2;
133 let b3 = -1.556989798598866e2;
134 let b4 = 6.680131188771972e1;
135 let b5 = -1.328068155288572e1;
136 let c1 = -7.784894002430293e-3;
137 let c2 = -3.223964580411365e-1;
138 let c3 = -2.400758277161838e0;
139 let c4 = -2.549732539343734e0;
140 let c5 = 4.374664141464968e0;
141 let c6 = 2.938163982698783e0;
142 let d1 = 7.784695709041462e-3;
143 let d2 = 3.224671290700398e-1;
144 let d3 = 2.445134137142996e0;
145 let d4 = 3.754408661907416e0;
146
147 let p_low = 0.02425;
148 let p_high = 1.0 - p_low;
149
150 if p < p_low {
151 let q = (-2.0 * p.ln()).sqrt();
152 (((((c1 * q + c2) * q + c3) * q + c4) * q + c5) * q + c6)
153 / ((((d1 * q + d2) * q + d3) * q + d4) * q + 1.0)
154 } else if p <= p_high {
155 let q = p - 0.5;
156 let r = q * q;
157 (((((a1 * r + a2) * r + a3) * r + a4) * r + a5) * r + a6) * q
158 / (((((b1 * r + b2) * r + b3) * r + b4) * r + b5) * r + 1.0)
159 } else {
160 let q = (-2.0 * (1.0 - p).ln()).sqrt();
161 -(((((c1 * q + c2) * q + c3) * q + c4) * q + c5) * q + c6)
162 / ((((d1 * q + d2) * q + d3) * q + d4) * q + 1.0)
163 }
164}
165
166#[allow(clippy::excessive_precision)]
171pub fn phi(x: f64) -> f64 {
172 let xa = x.abs();
173 if xa > 37.0 {
174 return if x > 0.0 { 1.0 } else { 0.0 };
175 }
176 let e = (-xa * xa / 2.0).exp();
177 let c: f64;
178 if xa < 7.07106781186547 {
179 let mut b = 3.52624965998911e-2 * xa + 0.700383064443688;
180 b = b * xa + 6.37396220353165;
181 b = b * xa + 33.912866078383;
182 b = b * xa + 112.079291497871;
183 b = b * xa + 221.213596169931;
184 b = b * xa + 220.206867912376;
185 let num = e * b;
186 b = 8.83883476483184e-2 * xa + 1.75566716318264;
187 b = b * xa + 16.064177579207;
188 b = b * xa + 86.7807322029461;
189 b = b * xa + 296.564248779674;
190 b = b * xa + 637.333633378831;
191 b = b * xa + 793.826512519948;
192 b = b * xa + 440.413735824752;
193 c = num / b;
194 } else {
195 let mut b = xa + 0.65;
196 b = xa + 4.0 / b;
197 b = xa + 3.0 / b;
198 b = xa + 2.0 / b;
199 b = xa + 1.0 / b;
200 c = e / b / 2.506628274631;
201 }
202 if x > 0.0 {
203 1.0 - c
204 } else {
205 c
206 }
207}
208
209pub fn t975(df: usize) -> f64 {
218 match df {
219 0 | 1 => 12.706,
220 2 => 4.303,
221 3 => 3.182,
222 4 => 2.776,
223 5 => 2.571,
224 6 => 2.447,
225 _ => 2.365,
226 }
227}
228
229pub fn sum(xs: &[f64]) -> f64 {
234 let mut s = 0.0;
235 for &x in xs {
236 s += x;
237 }
238 s
239}
240
241pub fn sample_mean(xs: &[f64]) -> f64 {
243 if xs.is_empty() {
244 return 0.0;
245 }
246 sum(xs) / xs.len() as f64
247}
248
249pub fn sample_variance(xs: &[f64]) -> f64 {
251 let n = xs.len();
252 if n < 2 {
253 return 0.0;
254 }
255 let m = sample_mean(xs);
256 let mut s = 0.0;
257 for &x in xs {
258 let d = x - m;
259 s += d * d;
260 }
261 s / (n - 1) as f64
262}
263
264#[cfg(test)]
265mod tests {
266 use super::*;
267
268 #[test]
269 fn quantile_hand_computed() {
270 assert_eq!(quantile(&[1.0, 2.0, 3.0, 4.0], 0.5), 2.5);
271 assert_eq!(quantile(&[1.0, 2.0, 3.0, 4.0], 0.25), 1.75);
272 assert_eq!(quantile(&[1.0, 2.0, 3.0, 4.0], 0.0), 1.0);
273 assert_eq!(quantile(&[1.0, 2.0, 3.0, 4.0], 1.0), 4.0);
274 assert_eq!(quantile(&[7.0], 0.5), 7.0);
275 assert!(quantile(&[], 0.5).is_nan());
276 }
277
278 #[test]
279 fn pcg32_first_outputs() {
280 let mut r = Pcg32::new();
281 assert_eq!(
282 [r.next_u32(), r.next_u32(), r.next_u32(), r.next_u32()],
283 [355248013, 41705475, 3406281715, 4186697710]
284 );
285 }
286
287 #[test]
288 fn lemire_bounds() {
289 assert_eq!(lemire_bounded(0, 100), 0);
290 assert_eq!(lemire_bounded(0xffff_ffff, 100), 99);
291 assert_eq!(lemire_bounded(0x8000_0000, 10), 5);
292 }
293
294 #[test]
295 fn inv_normal_and_phi_anchors() {
296 assert_eq!(inv_normal(0.5), 0.0);
297 assert_eq!(phi(0.0), 0.5);
298 assert_eq!(inv_normal(0.0), f64::NEG_INFINITY);
299 assert_eq!(inv_normal(1.0), f64::INFINITY);
300 assert!((inv_normal(0.975) - 1.959963984540054).abs() < 1e-8);
301 assert!((phi(1.959963984540054) - 0.975).abs() < 1e-9);
302 }
303
304 #[test]
305 fn t975_table_and_clamps() {
306 assert_eq!(t975(1), 12.706);
307 assert_eq!(t975(0), 12.706);
308 assert_eq!(t975(2), 4.303);
309 assert_eq!(t975(7), 2.365);
310 assert_eq!(t975(99), 2.365);
311 }
312
313 #[test]
314 fn fixed_order_sums() {
315 let xs = [2.0, 4.0, 4.0, 4.0, 5.0, 5.0, 7.0, 9.0];
316 assert_eq!(sum(&xs), 40.0);
317 assert_eq!(sample_mean(&xs), 5.0);
318 assert_eq!(sample_variance(&xs), 32.0 / 7.0);
319 assert_eq!(sum(&[0.1, 0.1, 0.1]), 0.30000000000000004);
321 }
322}