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
//! Seeded resampling schemes: with-replacement bootstrap, Fisher–Yates
//! permutation, and k-fold cross-validation splits.
//!
//! Every draw comes from the deterministic [`SplitMix64`] PRNG, so two calls with
//! identically seeded generators produce identical output.
use uniform_index;
use crateSplitMix64;
/// Draws `b` bootstrap resamples of `n` with-replacement indices.
///
/// Each resample is an independent length-`n` vector of indices drawn uniformly
/// from `0..n` with replacement, the standard nonparametric bootstrap scheme.
/// The draws come from `rng`, so two calls with identically seeded generators
/// produce identical collections.
///
/// # Arguments
///
/// * `n` — sample size; each resample has this many indices. With `n == 0` every
/// resample is empty.
/// * `b` — number of resamples to draw.
/// * `rng` — the deterministic generator driving the draws.
///
/// # Returns
///
/// A `b`-element vector of length-`n` index vectors.
///
/// # Examples
///
/// ```
/// use stats_claw::resampling::bootstrap_indices;
/// use stats_claw::rng::SplitMix64;
///
/// let a = bootstrap_indices(10, 3, &mut SplitMix64::new(7));
/// let b = bootstrap_indices(10, 3, &mut SplitMix64::new(7));
/// assert_eq!(a, b); // identical seeds reproduce the draws
/// assert_eq!(a.len(), 3);
/// assert!(a.iter().all(|r| r.len() == 10 && r.iter().all(|&i| i < 10)));
/// ```
/// Draws a uniform random permutation of `0..n` via the Fisher–Yates shuffle.
///
/// Walks the index vector from the back, swapping each position with a uniformly
/// chosen earlier-or-equal position, which yields each of the `n!` orderings with
/// equal probability. Reproducible for a fixed seed.
///
/// # Arguments
///
/// * `n` — number of elements to permute. With `n == 0` the result is empty.
/// * `rng` — the deterministic generator driving the swaps.
///
/// # Returns
///
/// A vector containing `0..n` in a uniformly random order.
///
/// # Examples
///
/// ```
/// use stats_claw::resampling::permutation;
/// use stats_claw::rng::SplitMix64;
///
/// let mut p = permutation(6, &mut SplitMix64::new(42));
/// p.sort_unstable();
/// assert_eq!(p, vec![0, 1, 2, 3, 4, 5]); // a permutation contains each index once
/// ```
/// Partitions `0..n` into `k` cross-validation folds.
///
/// Shuffles the indices once, then assigns position `p` to test fold `p % k`, so
/// every observation lands in exactly one test fold and the corresponding train
/// set is its complement. The split is reproducible for a fixed seed.
///
/// # Arguments
///
/// * `n` — number of observations.
/// * `k` — number of folds; must be `> 0` (callers guarantee this). With `k == 0`
/// the result is empty.
/// * `rng` — the deterministic generator driving the shuffle.
///
/// # Returns
///
/// A `k`-element vector of `(train_indices, test_indices)` pairs whose test sets
/// form a partition of `0..n`.
///
/// # Examples
///
/// ```
/// use stats_claw::resampling::kfold_indices;
/// use stats_claw::rng::SplitMix64;
///
/// let folds = kfold_indices(10, 5, &mut SplitMix64::new(1));
/// let total_test: usize = folds.iter().map(|(_, test)| test.len()).sum();
/// assert_eq!(total_test, 10); // test sets partition the data
/// ```