use super::*;
#[test]
fn indices_error_when_n_below_two() {
assert_eq!(
jackknife_indices(1),
Err(Error::InsufficientData),
"n < 2 must be rejected as insufficient data"
);
}
#[test]
fn indices_leave_one_out_for_three() -> Result<()> {
assert_eq!(
jackknife_indices(3)?,
vec![vec![1, 2], vec![0, 2], vec![0, 1]],
"each set must omit exactly its own index, in order"
);
Ok(())
}
const DATA: [f64; 8] = [2.0, 4.0, 4.0, 4.0, 5.0, 5.0, 7.0, 9.0];
fn mean(s: &[f64]) -> f64 {
s.iter().sum::<f64>() / count_to_f64(s.len())
}
fn plugin_var(s: &[f64]) -> f64 {
let m = mean(s);
s.iter()
.map(|&v| {
let deviation = v - m;
deviation * deviation
})
.sum::<f64>()
/ count_to_f64(s.len())
}
#[test]
fn mean_std_error_matches_classic_identity() -> Result<()> {
let expected = 0.755_928_946_018_454_4;
let est = jackknife_statistic(&DATA, mean)?;
assert!(
(est.std_error() - expected).abs() < 1e-12,
"jackknife SE of the mean was {}, expected {expected}",
est.std_error()
);
Ok(())
}
#[test]
fn mean_bias_is_zero() -> Result<()> {
let est = jackknife_statistic(&DATA, mean)?;
assert!(
est.bias().abs() < 1e-12,
"jackknife bias of the mean was {}, expected 0",
est.bias()
);
Ok(())
}
#[test]
fn variance_bias_correction_recovers_unbiased() -> Result<()> {
let est = jackknife_statistic(&DATA, plugin_var)?;
assert!(
(est.bias() - (-0.571_428_571_428_569)).abs() < 1e-9,
"jackknife bias of the plug-in variance was {}",
est.bias()
);
let corrected = est.estimate() - est.bias();
let unbiased = 4.571_428_571_428_571;
assert!(
(corrected - unbiased).abs() < 1e-9,
"bias-corrected variance was {corrected}, expected {unbiased}"
);
Ok(())
}
#[test]
fn accessors_expose_estimate_and_replicates() -> Result<()> {
let est = jackknife_statistic(&DATA, mean)?;
assert!(
(est.estimate() - 5.0).abs() < 1e-12,
"estimate accessor was {}",
est.estimate()
);
assert!(est.bias().abs() < 1e-12, "bias accessor was {}", est.bias());
assert!(
est.std_error() > 0.0,
"std_error accessor was {}",
est.std_error()
);
assert_eq!(
est.replicates().len(),
DATA.len(),
"replicates accessor must return one value per observation"
);
Ok(())
}
#[test]
fn inherent_estimate_delegates_to_free_function() -> Result<()> {
let scheme = JackknifeResampling::default();
let via_scheme = scheme.estimate(&DATA, mean)?;
let direct = jackknife_statistic(&DATA, mean)?;
assert_eq!(
via_scheme, direct,
"the inherent estimate must delegate to jackknife_statistic"
);
Ok(())
}
#[test]
fn n_two_mean_se_equals_sd_over_sqrt_two_and_bias_is_zero() -> Result<()> {
let data = [1.0_f64, 4.0];
let est = jackknife_statistic(&data, mean)?;
let dev = 1.5_f64;
let var_ddof1 = dev.mul_add(dev, dev * dev);
let expected_se = var_ddof1.sqrt() / 2.0_f64.sqrt();
assert!(
(est.std_error() - expected_se).abs() < 1e-12,
"n=2 SE was {}, expected sd(ddof=1)/sqrt(2) = {expected_se}",
est.std_error()
);
assert!(
(est.std_error() - 1.5).abs() < 1e-12,
"n=2 SE was {}, expected closed-form 1.5",
est.std_error()
);
assert!(
est.bias().abs() < 1e-12,
"n=2 jackknife bias of the mean was {}, expected 0",
est.bias()
);
Ok(())
}
#[test]
fn indices_leave_one_out_for_two() -> Result<()> {
assert_eq!(
jackknife_indices(2)?,
vec![vec![1], vec![0]],
"n=2 leave-one-out sets must be [[1], [0]]"
);
Ok(())
}