extern crate alloc;
use alloc::vec::Vec;
use rand::Rng;
use crate::math;
use crate::types::TimingSample;
#[inline]
pub fn counter_rng_seed(base_seed: u64, counter: u64) -> u64 {
let mut z = base_seed.wrapping_add(counter.wrapping_mul(0x9e3779b97f4a7c15));
z = (z ^ (z >> 30)).wrapping_mul(0xbf58476d1ce4e5b9);
z = (z ^ (z >> 27)).wrapping_mul(0x94d049bb133111eb);
z ^ (z >> 31)
}
pub fn compute_block_size(n: usize) -> usize {
let block_size = math::ceil(1.3 * math::cbrt(n as f64)) as usize;
block_size.max(1)
}
pub fn block_bootstrap_resample_into<R: Rng>(
data: &[f64],
block_size: usize,
rng: &mut R,
out: &mut [f64],
) {
assert_eq!(
out.len(),
data.len(),
"Output buffer must have same length as input data"
);
if data.is_empty() {
return;
}
let n = data.len();
let block_size = block_size.max(1).min(n);
let num_block_starts = n.saturating_sub(block_size) + 1;
if num_block_starts == 0 {
out.copy_from_slice(data);
return;
}
let mut pos = 0;
while pos < n {
let start = rng.random_range(0..num_block_starts);
let block_end = (start + block_size).min(n);
let block_len = block_end - start;
let copy_len = block_len.min(n - pos);
out[pos..pos + copy_len].copy_from_slice(&data[start..start + copy_len]);
pos += copy_len;
}
}
pub fn block_bootstrap_resample<R: Rng>(data: &[f64], block_size: usize, rng: &mut R) -> Vec<f64> {
if data.is_empty() {
return Vec::new();
}
let n = data.len();
let block_size = block_size.max(1).min(n);
let num_block_starts = n.saturating_sub(block_size) + 1;
if num_block_starts == 0 {
return data.to_vec();
}
let mut result = Vec::with_capacity(n);
while result.len() < n {
let start = rng.random_range(0..num_block_starts);
let end = (start + block_size).min(n);
for &value in data.iter().take(end).skip(start) {
if result.len() >= n {
break;
}
result.push(value);
}
}
result
}
#[cfg(test)]
pub fn stratified_block_bootstrap<R: Rng>(
fixed_data: &[f64],
random_data: &[f64],
block_size: usize,
rng: &mut R,
) -> (Vec<f64>, Vec<f64>) {
let resampled_fixed = block_bootstrap_resample(fixed_data, block_size, rng);
let resampled_random = block_bootstrap_resample(random_data, block_size, rng);
(resampled_fixed, resampled_random)
}
#[allow(dead_code)]
pub fn block_bootstrap_resample_joint<R: Rng>(
interleaved: &[TimingSample],
block_size: usize,
rng: &mut R,
) -> Vec<TimingSample> {
if interleaved.is_empty() {
return Vec::new();
}
let n = interleaved.len();
let block_size = block_size.max(1).min(n);
let num_block_starts = n.saturating_sub(block_size) + 1;
if num_block_starts == 0 {
return interleaved.to_vec();
}
let mut result = Vec::with_capacity(n);
while result.len() < n {
let start = rng.random_range(0..num_block_starts);
let end = (start + block_size).min(n);
for sample in interleaved.iter().take(end).skip(start) {
if result.len() >= n {
break;
}
result.push(*sample);
}
}
result
}
pub fn block_bootstrap_resample_joint_into<R: Rng>(
interleaved: &[TimingSample],
block_size: usize,
rng: &mut R,
out: &mut [TimingSample],
) {
assert_eq!(
out.len(),
interleaved.len(),
"Output buffer must have same length as input data"
);
if interleaved.is_empty() {
return;
}
let n = interleaved.len();
let block_size = block_size.max(1).min(n);
let num_block_starts = n.saturating_sub(block_size) + 1;
if num_block_starts == 0 {
out.copy_from_slice(interleaved);
return;
}
let mut pos = 0;
while pos < n {
let start = rng.random_range(0..num_block_starts);
let block_end = (start + block_size).min(n);
let block_len = block_end - start;
let copy_len = block_len.min(n - pos);
out[pos..pos + copy_len].copy_from_slice(&interleaved[start..start + copy_len]);
pos += copy_len;
}
}
#[cfg(test)]
mod tests {
use super::*;
use rand::SeedableRng;
use rand_xoshiro::Xoshiro256PlusPlus;
#[test]
fn test_block_size_computation() {
assert_eq!(compute_block_size(100), 7);
assert_eq!(compute_block_size(10000), 29);
assert_eq!(compute_block_size(30000), 41);
assert_eq!(compute_block_size(1), 2); assert_eq!(compute_block_size(0), 1); }
#[test]
fn test_bootstrap_preserves_length() {
let data: Vec<f64> = (0..100).map(|x| x as f64).collect();
let mut rng = Xoshiro256PlusPlus::seed_from_u64(42);
let resampled = block_bootstrap_resample(&data, 10, &mut rng);
assert_eq!(resampled.len(), data.len());
}
#[test]
fn test_bootstrap_samples_from_data() {
let data: Vec<f64> = (0..100).map(|x| x as f64).collect();
let mut rng = Xoshiro256PlusPlus::seed_from_u64(42);
let resampled = block_bootstrap_resample(&data, 10, &mut rng);
for val in &resampled {
assert!(data.contains(val));
}
}
#[test]
fn test_empty_data() {
let data: Vec<f64> = vec![];
let mut rng = Xoshiro256PlusPlus::seed_from_u64(42);
let resampled = block_bootstrap_resample(&data, 10, &mut rng);
assert!(resampled.is_empty());
}
#[test]
fn test_stratified_bootstrap() {
let fixed: Vec<f64> = (0..50).map(|x| x as f64).collect();
let random: Vec<f64> = (50..100).map(|x| x as f64).collect();
let mut rng = Xoshiro256PlusPlus::seed_from_u64(42);
let (resampled_fixed, resampled_random) =
stratified_block_bootstrap(&fixed, &random, 7, &mut rng);
assert_eq!(resampled_fixed.len(), fixed.len());
assert_eq!(resampled_random.len(), random.len());
for val in &resampled_fixed {
assert!(fixed.contains(val));
}
for val in &resampled_random {
assert!(random.contains(val));
}
}
}