1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
//! Shared cast-free numeric primitives: count widening and the sample mean and
//! (Bessel-corrected) variance.
//!
//! These are the canonical implementations of three tiny statistics that were
//! previously re-derived in several modules (the resampling schemes, the
//! parametric tests, the algorithms layer). Centralising them here removes the
//! duplication while keeping every call site bit-for-bit identical: the widening
//! splits a `usize` into 32-bit halves so it never uses an `as` cast (the
//! protected `style.rs` guard bans `as` in `src/`), and the mean/variance use the
//! same two-pass formula and the same widening the old inline copies used.
//!
//! The module itself is private (`mod numeric;`) and its items are `pub(crate)`
//! so the whole crate can share them while they stay out of the public API.
//!
//! `#[allow(clippy::redundant_pub_crate)]` is required and justified: for a
//! crate-wide helper in a *private* module the `redundant_pub_crate` lint (which
//! would have us write `pub`) and `unreachable_pub` (which forbids a bare `pub`
//! that cannot escape the crate) are mutually exclusive — CI runs
//! `clippy -- -D warnings`, so both are hard errors. Keeping the honest
//! `pub(crate)` and silencing the redundant-ness lint is the only conflict-free
//! option short of exposing the module publicly.
/// `2^32`, used to split an integer-valued `f64` into 32-bit halves.
const TWO_POW_32: f64 = 4_294_967_296.0;
/// Widens a `usize` count to `f64` without an `as` cast.
///
/// Splits the value into its high and low 32-bit halves — each losslessly
/// representable as an `f64` — and recombines them with a single fused
/// multiply-add. This routes every conversion through `From`/`TryFrom` and so
/// satisfies the `style.rs` no-`as` guard.
///
/// # Arguments
///
/// * `n` — the count to widen.
///
/// # Returns
///
/// `n` as an `f64`. The result is *exact* for every `n < 2^53` (the `f64`
/// integer-exactness bound), which covers every realistic sample, cluster, or
/// iteration count this crate handles; above `2^53` the value rounds to the
/// nearest representable `f64` like any other `usize`-to-`f64` conversion.
pub
/// Returns the arithmetic mean of `xs`, or `0.0` for an empty slice.
///
/// The empty-slice case returns `0.0` rather than `NaN` so callers that have
/// already validated non-emptiness (every current caller has) get the plain
/// average, and callers that have not get a benign sentinel instead of a
/// propagating `NaN`.
///
/// # Arguments
///
/// * `xs` — the observations to average.
///
/// # Returns
///
/// `Σ xs / xs.len()`, or `0.0` when `xs` is empty.
pub
/// Returns the unbiased (Bessel-corrected, `ddof = 1`) sample variance of `xs`.
///
/// Uses the classic two-pass estimator: first the [`mean`], then the mean of the
/// squared deviations divided by `n − 1`. Returns `0.0` for fewer than two
/// observations, where the `n − 1` denominator is undefined; callers that require
/// a positive variance check the result and raise a typed error themselves.
///
/// # Arguments
///
/// * `xs` — the observations whose spread to measure.
///
/// # Returns
///
/// `Σ (xᵢ − x̄)² / (n − 1)` with `n = xs.len()`, or `0.0` when `n < 2`.
pub