1use crate::error::{check_option, check_series, TimeSeriesError};
26use std::ops::RangeInclusive;
27use u_numflow::matrix::Matrix;
28use u_numflow::special;
29use u_numflow::stats;
30
31#[derive(Debug, Clone, Copy)]
33pub struct CorrelationResult {
34 pub r: f64,
36 pub p_value: f64,
38 pub n: usize,
40}
41
42#[derive(Debug, Clone, Copy)]
44pub struct CorrelationCI {
45 pub lower: f64,
47 pub upper: f64,
49 pub confidence: f64,
51}
52
53pub fn pearson(x: &[f64], y: &[f64]) -> Option<CorrelationResult> {
86 let n = x.len();
87 if n < 3 || n != y.len() {
88 return None;
89 }
90
91 if x.iter().any(|v| !v.is_finite()) || y.iter().any(|v| !v.is_finite()) {
92 return None;
93 }
94
95 let cov = stats::covariance(x, y)?;
96 let sx = stats::std_dev(x)?;
97 let sy = stats::std_dev(y)?;
98
99 if sx < 1e-300 || sy < 1e-300 {
100 return None; }
102
103 let r = (cov / (sx * sy)).clamp(-1.0, 1.0);
104 let p_value = correlation_p_value(r, n);
105
106 Some(CorrelationResult { r, p_value, n })
107}
108
109pub fn spearman(x: &[f64], y: &[f64]) -> Option<CorrelationResult> {
142 let n = x.len();
143 if n < 3 || n != y.len() {
144 return None;
145 }
146
147 if x.iter().any(|v| !v.is_finite()) || y.iter().any(|v| !v.is_finite()) {
148 return None;
149 }
150
151 let rx = rank_data(x);
152 let ry = rank_data(y);
153
154 let cov = stats::covariance(&rx, &ry)?;
156 let srx = stats::std_dev(&rx)?;
157 let sry = stats::std_dev(&ry)?;
158
159 if srx < 1e-300 || sry < 1e-300 {
160 return None;
161 }
162
163 let r = (cov / (srx * sry)).clamp(-1.0, 1.0);
164 let p_value = correlation_p_value(r, n);
165
166 Some(CorrelationResult { r, p_value, n })
167}
168
169pub fn kendall_tau_b(x: &[f64], y: &[f64]) -> Option<CorrelationResult> {
220 let n = x.len();
221 if n < 3 || n != y.len() {
222 return None;
223 }
224
225 if x.iter().any(|v| !v.is_finite()) || y.iter().any(|v| !v.is_finite()) {
226 return None;
227 }
228
229 let mut concordant: i64 = 0;
230 let mut discordant: i64 = 0;
231 let mut ties_x: i64 = 0;
232 let mut ties_y: i64 = 0;
233 let mut _ties_xy: i64 = 0;
234
235 for i in 0..n {
236 for j in (i + 1)..n {
237 let dx = x[i] - x[j];
238 let dy = y[i] - y[j];
239 let product = dx * dy;
240
241 if dx == 0.0 && dy == 0.0 {
242 _ties_xy += 1;
243 ties_x += 1;
244 ties_y += 1;
245 } else if dx == 0.0 {
246 ties_x += 1;
247 } else if dy == 0.0 {
248 ties_y += 1;
249 } else if product > 0.0 {
250 concordant += 1;
251 } else {
252 discordant += 1;
253 }
254 }
255 }
256
257 let n0 = (n as i64) * (n as i64 - 1) / 2;
258 let denom_sq = (n0 - ties_x) as f64 * (n0 - ties_y) as f64;
259
260 if denom_sq <= 0.0 {
261 return None; }
263
264 let tau = (concordant - discordant) as f64 / denom_sq.sqrt();
265 let tau = tau.clamp(-1.0, 1.0);
266
267 let p_value = if ties_x == 0
268 && ties_y == 0
269 && (n <= KENDALL_EXACT_MAX_N || concordant.min(discordant) <= 1)
270 {
271 kendall_exact_p(n, discordant)
274 } else {
275 let m = n as f64 * (n as f64 - 1.0);
279 let (pairs_x, triples_x, weighted_x) = tie_sums(x);
280 let (pairs_y, triples_y, weighted_y) = tie_sums(y);
281 let var_s = (m * (2.0 * n as f64 + 5.0) - weighted_x - weighted_y) / 18.0
282 + 2.0 * pairs_x * pairs_y / m
283 + triples_x * triples_y / (9.0 * m * (n as f64 - 2.0));
284
285 if var_s > 0.0 {
286 let s = (concordant - discordant) as f64;
287 let z = s / var_s.sqrt();
288 2.0 * special::standard_normal_sf(z.abs())
289 } else {
290 1.0
291 }
292 };
293
294 Some(CorrelationResult { r: tau, p_value, n })
295}
296
297const KENDALL_EXACT_MAX_N: usize = 33;
301
302fn kendall_exact_p(n: usize, discordant: i64) -> f64 {
313 let total = (n * (n - 1) / 2) as i64;
314 let tail = discordant.min(total - discordant);
315 if 4 * tail == (n * (n - 1)) as i64 {
316 return 1.0; }
318 let two_over_factorial = |m: usize| (2..=m).fold(2.0_f64, |p, k| p / k as f64);
320 match tail {
321 0 => return two_over_factorial(n),
322 1 => return two_over_factorial(n - 1),
323 _ => {}
324 }
325 debug_assert!(n <= KENDALL_EXACT_MAX_N, "counts would overflow u128");
326 let c = tail as usize;
327 let mut counts = vec![0u128; c + 1];
329 counts[0] = 1;
330 for j in 2..=n {
331 let mut next = vec![0u128; c + 1];
333 let mut window = 0u128;
334 for k in 0..=c {
335 window += counts[k];
336 if k >= j {
337 window -= counts[k - j];
338 }
339 next[k] = window;
340 }
341 counts = next;
342 }
343 let at_most: u128 = counts.iter().sum();
344 let permutations: u128 = (2..=n as u128).product();
345 (2.0 * at_most as f64 / permutations as f64).min(1.0)
346}
347
348pub fn fisher_z(r: f64) -> Option<f64> {
366 if r <= -1.0 || r >= 1.0 || !r.is_finite() {
367 return None;
368 }
369 Some(r.atanh())
370}
371
372pub fn fisher_z_inv(z: f64) -> f64 {
374 z.tanh()
375}
376
377pub fn correlation_ci(r: f64, n: usize, confidence: f64) -> Option<CorrelationCI> {
402 if n < 4 || r <= -1.0 || r >= 1.0 || !r.is_finite() {
403 return None;
404 }
405 if confidence <= 0.0 || confidence >= 1.0 {
406 return None;
407 }
408
409 let z = r.atanh();
410 let se = 1.0 / ((n as f64 - 3.0).sqrt());
411 let alpha = 1.0 - confidence;
412 let z_crit = special::inverse_normal_cdf(1.0 - alpha / 2.0);
413
414 let z_lower = z - z_crit * se;
415 let z_upper = z + z_crit * se;
416
417 Some(CorrelationCI {
418 lower: z_lower.tanh(),
419 upper: z_upper.tanh(),
420 confidence,
421 })
422}
423
424pub fn correlation_matrix(variables: &[&[f64]]) -> Option<Matrix> {
454 let p = variables.len();
455 if p < 2 {
456 return None;
457 }
458 let n = variables[0].len();
459 if n < 3 {
460 return None;
461 }
462 for v in variables {
463 if v.len() != n {
464 return None;
465 }
466 }
467
468 let mut sds = Vec::with_capacity(p);
470 for v in variables {
471 let m = stats::mean(v)?;
472 if !m.is_finite() {
473 return None;
474 }
475 let s = stats::std_dev(v)?;
476 if !s.is_finite() || s < 1e-300 {
477 return None;
478 }
479 sds.push(s);
480 }
481
482 let mut data = vec![0.0; p * p];
483
484 for i in 0..p {
485 data[i * p + i] = 1.0; for j in (i + 1)..p {
487 let cov = stats::covariance(variables[i], variables[j])?;
488 let r = (cov / (sds[i] * sds[j])).clamp(-1.0, 1.0);
489 data[i * p + j] = r;
490 data[j * p + i] = r;
491 }
492 }
493
494 Matrix::new(p, p, data).ok()
495}
496
497pub fn spearman_matrix(variables: &[&[f64]]) -> Option<Matrix> {
501 let p = variables.len();
502 if p < 2 {
503 return None;
504 }
505 let n = variables[0].len();
506 if n < 3 {
507 return None;
508 }
509 for v in variables {
510 if v.len() != n || v.iter().any(|x| !x.is_finite()) {
511 return None;
512 }
513 }
514
515 let ranked: Vec<Vec<f64>> = variables.iter().map(|v| rank_data(v)).collect();
517 let ranked_refs: Vec<&[f64]> = ranked.iter().map(|v| v.as_slice()).collect();
518
519 correlation_matrix(&ranked_refs)
520}
521
522pub fn kendall_matrix(variables: &[&[f64]]) -> Option<Matrix> {
552 let p = variables.len();
553 if p < 2 {
554 return None;
555 }
556 let n = variables[0].len();
557 if n < 3 {
558 return None;
559 }
560 for v in variables {
561 if v.len() != n || v.iter().any(|x| !x.is_finite()) {
562 return None;
563 }
564 }
565
566 let mut data = vec![0.0_f64; p * p];
567 for i in 0..p {
568 data[i * p + i] = 1.0;
569 for j in (i + 1)..p {
570 let r = kendall_tau_b(variables[i], variables[j])?.r;
571 data[i * p + j] = r;
572 data[j * p + i] = r;
573 }
574 }
575 Matrix::new(p, p, data).ok()
576}
577
578fn correlation_p_value(r: f64, n: usize) -> f64 {
586 if n < 3 {
587 return 1.0;
588 }
589 let df = (n - 2) as f64;
590 let r2 = r * r;
591
592 if r2 >= 1.0 - 1e-15 {
594 return 0.0; }
596
597 let t = r * (df / (1.0 - r2)).sqrt();
598 2.0 * special::t_distribution_sf(t.abs(), df)
599}
600
601fn rank_data(data: &[f64]) -> Vec<f64> {
605 let n = data.len();
606 let mut indexed: Vec<(usize, f64)> = data.iter().copied().enumerate().collect();
607 indexed.sort_by(|a, b| a.1.partial_cmp(&b.1).unwrap_or(std::cmp::Ordering::Equal));
608
609 let mut ranks = vec![0.0; n];
610 let mut i = 0;
611 while i < n {
612 let mut j = i;
613 while j < n && (indexed[j].1 - indexed[i].1).abs() < 1e-300 {
615 j += 1;
616 }
617 let avg_rank = (i + j) as f64 / 2.0 + 0.5;
619 for item in indexed.iter().take(j).skip(i) {
620 ranks[item.0] = avg_rank;
621 }
622 i = j;
623 }
624
625 ranks
626}
627
628fn tie_sums(data: &[f64]) -> (f64, f64, f64) {
631 let mut sorted: Vec<f64> = data.to_vec();
632 sorted.sort_by(f64::total_cmp);
633
634 let (mut pairs, mut triples, mut weighted) = (0.0, 0.0, 0.0);
635 let mut i = 0;
636 while i < sorted.len() {
637 let mut j = i;
638 while j < sorted.len() && sorted[j] == sorted[i] {
639 j += 1;
640 }
641 let t = (j - i) as f64;
642 pairs += t * (t - 1.0) / 2.0;
643 triples += t * (t - 1.0) * (t - 2.0);
644 weighted += t * (t - 1.0) * (2.0 * t + 5.0);
645 i = j;
646 }
647 (pairs, triples, weighted)
648}
649
650#[derive(Debug, Clone)]
656pub struct AcfResult {
657 pub acf: Vec<f64>,
660 pub confidence_threshold: f64,
663 pub bartlett_limits: Vec<f64>,
668}
669
670#[derive(Debug, Clone)]
672pub struct PacfResult {
673 pub pacf: Vec<f64>,
675 pub confidence_threshold: f64,
678}
679
680const Z_975: f64 = 1.959_963_984_540_054;
682
683pub const ACF_MIN_OBSERVATIONS: usize = 2;
685
686pub const PACF_MIN_OBSERVATIONS: usize = 4;
688
689fn lag_choice(
693 max_lag: Option<usize>,
694 n: usize,
695 domain: RangeInclusive<usize>,
696) -> Result<usize, TimeSeriesError> {
697 match max_lag {
698 None => Ok((((n as f64).log10() * 10.0).floor() as usize).clamp(1, *domain.end())),
699 Some(k) => check_option("max_lag", k, &domain),
700 }
701}
702
703pub fn acf_max_lag_domain(data: &[f64]) -> Result<RangeInclusive<usize>, TimeSeriesError> {
718 check_series(data, ACF_MIN_OBSERVATIONS)?;
719 Ok(1..=data.len() - 1)
720}
721
722pub fn pacf_max_lag_domain(data: &[f64]) -> Result<RangeInclusive<usize>, TimeSeriesError> {
729 check_series(data, PACF_MIN_OBSERVATIONS)?;
730 Ok(1..=data.len() / 2 - 1)
731}
732
733pub fn acf(data: &[f64], max_lag: Option<usize>) -> Result<AcfResult, TimeSeriesError> {
764 let domain = acf_max_lag_domain(data)?;
765 let n = data.len();
766 let max_lag = lag_choice(max_lag, n, domain)?;
767 let nf = n as f64;
768
769 let mean = data.iter().sum::<f64>() / nf;
770
771 let c0: f64 = data.iter().map(|&x| (x - mean).powi(2)).sum::<f64>() / nf;
773
774 let mean_square: f64 = data.iter().map(|&x| x * x).sum::<f64>() / nf;
777 if c0 <= 1e-20 * mean_square {
778 return Err(TimeSeriesError::NoVariation);
779 }
780
781 let mut acf_vals = Vec::with_capacity(max_lag + 1);
782 acf_vals.push(1.0); for lag in 1..=max_lag {
785 let ck: f64 = data[..n - lag]
786 .iter()
787 .zip(&data[lag..])
788 .map(|(&xt, &xt_h)| (xt - mean) * (xt_h - mean))
789 .sum::<f64>()
790 / nf;
791 acf_vals.push(ck / c0);
792 }
793
794 let threshold = Z_975 / nf.sqrt();
795 let mut cumulative = 0.0;
796 let bartlett_limits = acf_vals[1..]
797 .iter()
798 .map(|r| {
799 let limit = Z_975 * ((1.0 + 2.0 * cumulative) / nf).sqrt();
800 cumulative += r * r;
801 limit
802 })
803 .collect();
804
805 Ok(AcfResult {
806 acf: acf_vals,
807 confidence_threshold: threshold,
808 bartlett_limits,
809 })
810}
811
812pub fn pacf(data: &[f64], max_lag: Option<usize>) -> Result<PacfResult, TimeSeriesError> {
847 let domain = pacf_max_lag_domain(data)?;
848 let n = data.len();
849 let max_lag = lag_choice(max_lag, n, domain)?;
850
851 let acf_result = acf(data, Some(max_lag))?;
854 let rho = &acf_result.acf;
855
856 let mut pacf_vals = Vec::with_capacity(max_lag);
857
858 pacf_vals.push(rho[1]);
860
861 if max_lag == 1 {
862 return Ok(PacfResult {
863 pacf: pacf_vals,
864 confidence_threshold: Z_975 / (n as f64).sqrt(),
865 });
866 }
867
868 let mut phi_prev = vec![rho[1]];
870
871 for h in 2..=max_lag {
872 let mut num = rho[h];
874 for j in 0..h - 1 {
875 num -= phi_prev[j] * rho[h - 1 - j];
876 }
877
878 let mut den = 1.0;
880 for j in 0..h - 1 {
881 den -= phi_prev[j] * rho[j + 1];
882 }
883
884 let phi_hh = if den.abs() > 1e-14 { num / den } else { 0.0 };
885
886 let mut phi_new = Vec::with_capacity(h);
888 for j in 0..h - 1 {
889 phi_new.push(phi_prev[j] - phi_hh * phi_prev[h - 2 - j]);
890 }
891 phi_new.push(phi_hh);
892
893 pacf_vals.push(phi_hh);
894 phi_prev = phi_new;
895 }
896
897 Ok(PacfResult {
898 pacf: pacf_vals,
899 confidence_threshold: Z_975 / (n as f64).sqrt(),
900 })
901}
902
903#[cfg(test)]
906mod statsmodels_reference {
907 use super::*;
908
909 const X: [f64; 24] = [
910 2.3, 3.1, 4.0, 3.6, 2.9, 2.2, 2.8, 3.9, 4.6, 4.1, 3.3, 2.5, 2.7, 3.8, 4.9, 4.4, 3.5, 2.6,
911 3.0, 4.2, 5.1, 4.5, 3.4, 2.9,
912 ];
913
914 fn close(got: &[f64], want: &[f64], what: &str) {
915 assert_eq!(got.len(), want.len(), "{what}");
916 for (k, (g, w)) in got.iter().zip(want).enumerate() {
917 assert!((g - w).abs() < 1e-12, "{what}[{k}]: {g} vs {w}");
918 }
919 }
920
921 #[test]
922 fn acf_and_bartlett_limits_reproduce_statsmodels() {
923 let r = acf(&X, Some(8)).unwrap();
924 close(
925 &r.acf,
926 &[
927 1.0,
928 0.486403576681892,
929 -0.36186808082372074,
930 -0.6951594797553611,
931 -0.2634125570953007,
932 0.42164395757528844,
933 0.6593055663079663,
934 0.26106100487729356,
935 -0.3171014941549895,
936 ],
937 "acf",
938 );
939 close(
940 &r.bartlett_limits,
941 &[
942 0.40007597302960923,
943 0.48559020426940086,
944 0.5269888881332575,
945 0.6575826795727397,
946 0.6742603293351266,
947 0.7152198020271394,
948 0.80665422242141,
949 0.8200659919957791,
950 ],
951 "bartlett",
952 );
953 assert!((r.confidence_threshold - 0.40007597302960923).abs() < 1e-12);
954 }
955
956 #[test]
957 fn pacf_reproduces_statsmodels_ldb() {
958 let r = pacf(&X, Some(8)).unwrap();
959 close(
960 &r.pacf,
961 &[
962 0.486403576681892,
963 -0.7839238375815395,
964 0.00047345408884359627,
965 0.1902495235598503,
966 0.19973005383776718,
967 0.1225767860916582,
968 0.05567632845800997,
969 0.03539504608148146,
970 ],
971 "pacf",
972 );
973 }
974
975 #[test]
976 fn a_small_unit_is_not_a_constant_series() {
977 let small: Vec<f64> = X.iter().map(|v| v * 1e-150).collect();
978 let r = acf(&small, Some(8)).unwrap();
979 assert!((r.acf[1] - 0.486403576681892).abs() < 1e-12);
980 assert_eq!(
981 acf(&[3.0; 10], Some(3)).unwrap_err(),
982 TimeSeriesError::NoVariation
983 );
984 }
985}
986
987#[cfg(test)]
988mod tests {
989 use super::*;
990
991 #[test]
996 fn pearson_perfect_positive() {
997 let x = [1.0, 2.0, 3.0, 4.0, 5.0];
998 let y = [2.0, 4.0, 6.0, 8.0, 10.0];
999 let result = pearson(&x, &y).expect("should compute");
1000 assert!((result.r - 1.0).abs() < 1e-10);
1001 assert!(result.p_value < 1e-10);
1002 }
1003
1004 #[test]
1005 fn pearson_perfect_negative() {
1006 let x = [1.0, 2.0, 3.0, 4.0, 5.0];
1007 let y = [10.0, 8.0, 6.0, 4.0, 2.0];
1008 let result = pearson(&x, &y).expect("should compute");
1009 assert!((result.r + 1.0).abs() < 1e-10);
1010 assert!(result.p_value < 1e-10);
1011 }
1012
1013 #[test]
1014 fn pearson_uncorrelated() {
1015 let x = [1.0, 2.0, 3.0, 4.0, 5.0];
1017 let y = [3.0, 1.0, 5.0, 1.0, 5.0];
1018 let result = pearson(&x, &y).expect("should compute");
1019 assert!(result.r.abs() < 0.5);
1020 }
1021
1022 #[test]
1023 fn pearson_known_value() {
1024 let x = [68.0, 71.0, 62.0, 75.0, 58.0, 60.0, 67.0, 68.0, 71.0, 69.0];
1026 let y = [4.1, 4.6, 3.8, 4.4, 3.2, 3.1, 3.8, 4.1, 4.3, 3.7];
1027 let result = pearson(&x, &y).expect("should compute");
1028 assert!((result.r - 0.8816).abs() < 0.01, "r = {}", result.r);
1030 }
1031
1032 #[test]
1041 fn pearson_numeric_reference() {
1042 let x = [1.0, 2.0, 3.0, 4.0, 5.0];
1043 let y = [2.0, 4.0, 5.0, 4.0, 5.0];
1044 let result = pearson(&x, &y).expect("should compute");
1045
1046 let expected_r = 6.0_f64 / (60.0_f64).sqrt(); assert!(
1048 (result.r - expected_r).abs() < 1e-3,
1049 "r expected {:.6}, got {}",
1050 expected_r,
1051 result.r
1052 );
1053 assert!(
1054 (result.r - 0.7746).abs() < 1e-3,
1055 "r expected ≈0.7746, got {}",
1056 result.r
1057 );
1058 }
1059
1060 #[test]
1061 fn pearson_insufficient_data() {
1062 assert!(pearson(&[1.0, 2.0], &[3.0, 4.0]).is_none());
1063 assert!(pearson(&[1.0], &[2.0]).is_none());
1064 }
1065
1066 #[test]
1067 fn pearson_length_mismatch() {
1068 assert!(pearson(&[1.0, 2.0, 3.0], &[4.0, 5.0]).is_none());
1069 }
1070
1071 #[test]
1072 fn pearson_zero_variance() {
1073 assert!(pearson(&[5.0, 5.0, 5.0], &[1.0, 2.0, 3.0]).is_none());
1074 }
1075
1076 #[test]
1077 fn pearson_nan_input() {
1078 assert!(pearson(&[1.0, f64::NAN, 3.0], &[4.0, 5.0, 6.0]).is_none());
1079 }
1080
1081 #[test]
1086 fn spearman_perfect_monotone() {
1087 let x = [1.0, 2.0, 3.0, 4.0, 5.0];
1088 let y = [1.0, 4.0, 9.0, 16.0, 25.0]; let result = spearman(&x, &y).expect("should compute");
1090 assert!((result.r - 1.0).abs() < 1e-10);
1091 }
1092
1093 #[test]
1094 fn spearman_perfect_negative() {
1095 let x = [1.0, 2.0, 3.0, 4.0, 5.0];
1096 let y = [5.0, 4.0, 3.0, 2.0, 1.0];
1097 let result = spearman(&x, &y).expect("should compute");
1098 assert!((result.r + 1.0).abs() < 1e-10);
1099 }
1100
1101 #[test]
1102 fn spearman_with_ties() {
1103 let x = [1.0, 2.0, 2.0, 4.0, 5.0];
1104 let y = [1.0, 3.0, 3.0, 4.0, 5.0];
1105 let result = spearman(&x, &y).expect("should compute");
1106 assert!(result.r > 0.9); }
1108
1109 #[test]
1110 fn spearman_nonlinear_monotone() {
1111 let x = [1.0, 2.0, 3.0, 4.0, 5.0];
1113 let y: Vec<f64> = x.iter().map(|&v: &f64| v.powi(3)).collect();
1114 let result = spearman(&x, &y).expect("should compute");
1115 assert!((result.r - 1.0).abs() < 1e-10);
1116 }
1117
1118 #[test]
1123 fn kendall_perfect_concordance() {
1124 let x = [1.0, 2.0, 3.0, 4.0, 5.0];
1125 let y = [1.0, 2.0, 3.0, 4.0, 5.0];
1126 let result = kendall_tau_b(&x, &y).expect("should compute");
1127 assert!((result.r - 1.0).abs() < 1e-10);
1128 }
1129
1130 #[test]
1131 fn kendall_perfect_discordance() {
1132 let x = [1.0, 2.0, 3.0, 4.0, 5.0];
1133 let y = [5.0, 4.0, 3.0, 2.0, 1.0];
1134 let result = kendall_tau_b(&x, &y).expect("should compute");
1135 assert!((result.r + 1.0).abs() < 1e-10);
1136 }
1137
1138 #[test]
1139 fn kendall_known_value() {
1140 let x = [1.0, 2.0, 3.0, 4.0, 5.0];
1142 let y = [3.0, 4.0, 1.0, 2.0, 5.0];
1143 let result = kendall_tau_b(&x, &y).expect("should compute");
1144 assert!((result.r - 0.2).abs() < 0.01);
1145 }
1146
1147 #[test]
1148 fn kendall_with_ties() {
1149 let x = [1.0, 2.0, 2.0, 4.0, 5.0];
1150 let y = [1.0, 3.0, 3.0, 4.0, 5.0];
1151 let result = kendall_tau_b(&x, &y).expect("should compute");
1152 assert!(result.r > 0.8);
1153 }
1154
1155 #[test]
1156 fn kendall_all_ties() {
1157 let x = [1.0, 1.0, 1.0];
1158 let y = [2.0, 2.0, 2.0];
1159 assert!(kendall_tau_b(&x, &y).is_none()); }
1161
1162 #[test]
1167 fn fisher_z_zero() {
1168 let z = fisher_z(0.0).expect("should compute");
1169 assert!(z.abs() < 1e-15);
1170 }
1171
1172 #[test]
1173 fn fisher_z_roundtrip() {
1174 for &r in &[0.0, 0.3, 0.5, 0.8, 0.95, -0.5, -0.95] {
1175 let z = fisher_z(r).expect("should compute");
1176 let r_back = fisher_z_inv(z);
1177 assert!((r - r_back).abs() < 1e-10, "Roundtrip failed for r={r}");
1178 }
1179 }
1180
1181 #[test]
1182 fn fisher_z_boundary() {
1183 assert!(fisher_z(1.0).is_none());
1184 assert!(fisher_z(-1.0).is_none());
1185 assert!(fisher_z(1.5).is_none());
1186 assert!(fisher_z(f64::NAN).is_none());
1187 }
1188
1189 #[test]
1194 fn ci_contains_r() {
1195 let ci = correlation_ci(0.6, 50, 0.95).expect("should compute");
1196 assert!(ci.lower < 0.6);
1197 assert!(ci.upper > 0.6);
1198 assert!(ci.lower > -1.0);
1199 assert!(ci.upper < 1.0);
1200 }
1201
1202 #[test]
1203 fn ci_wider_at_higher_confidence() {
1204 let ci_95 = correlation_ci(0.5, 30, 0.95).expect("should compute");
1205 let ci_99 = correlation_ci(0.5, 30, 0.99).expect("should compute");
1206 assert!(ci_99.upper - ci_99.lower > ci_95.upper - ci_95.lower);
1207 }
1208
1209 #[test]
1210 fn ci_narrower_with_more_data() {
1211 let ci_30 = correlation_ci(0.5, 30, 0.95).expect("should compute");
1212 let ci_100 = correlation_ci(0.5, 100, 0.95).expect("should compute");
1213 assert!(ci_100.upper - ci_100.lower < ci_30.upper - ci_30.lower);
1214 }
1215
1216 #[test]
1217 fn ci_edge_cases() {
1218 assert!(correlation_ci(0.5, 3, 0.95).is_none()); assert!(correlation_ci(1.0, 30, 0.95).is_none()); assert!(correlation_ci(0.5, 30, 0.0).is_none()); assert!(correlation_ci(0.5, 30, 1.0).is_none());
1222 }
1223
1224 #[test]
1229 fn corr_matrix_identity() {
1230 let x = [1.0, 2.0, 3.0, 4.0, 5.0];
1231 let y = [2.0, 4.0, 6.0, 8.0, 10.0];
1232 let z = [5.0, 4.0, 3.0, 2.0, 1.0];
1233 let mat = correlation_matrix(&[&x, &y, &z]).expect("should compute");
1234
1235 assert!((mat.get(0, 0) - 1.0).abs() < 1e-10);
1237 assert!((mat.get(1, 1) - 1.0).abs() < 1e-10);
1238 assert!((mat.get(2, 2) - 1.0).abs() < 1e-10);
1239
1240 assert!((mat.get(0, 1) - 1.0).abs() < 1e-10);
1242
1243 assert!((mat.get(0, 2) + 1.0).abs() < 1e-10);
1245
1246 assert!((mat.get(0, 1) - mat.get(1, 0)).abs() < 1e-15);
1248 assert!((mat.get(0, 2) - mat.get(2, 0)).abs() < 1e-15);
1249 }
1250
1251 #[test]
1252 fn corr_matrix_insufficient_variables() {
1253 let x = [1.0, 2.0, 3.0];
1254 assert!(correlation_matrix(&[&x]).is_none());
1255 }
1256
1257 #[test]
1258 fn corr_matrix_length_mismatch() {
1259 let x = [1.0, 2.0, 3.0];
1260 let y = [4.0, 5.0];
1261 assert!(correlation_matrix(&[&x, &y]).is_none());
1262 }
1263
1264 #[test]
1265 fn spearman_matrix_basic() {
1266 let x = [1.0, 2.0, 3.0, 4.0, 5.0];
1267 let y = [1.0, 4.0, 9.0, 16.0, 25.0]; let z = [5.0, 4.0, 3.0, 2.0, 1.0];
1269 let mat = spearman_matrix(&[&x, &y, &z]).expect("should compute");
1270
1271 assert!((mat.get(0, 1) - 1.0).abs() < 1e-10);
1273 assert!((mat.get(0, 2) + 1.0).abs() < 1e-10);
1275 }
1276
1277 #[test]
1278 fn kendall_matrix_basic() {
1279 let x = [1.0, 2.0, 3.0, 4.0, 5.0];
1280 let y = [2.0, 4.0, 6.0, 8.0, 10.0]; let z = [5.0, 4.0, 3.0, 2.0, 1.0]; let mat = kendall_matrix(&[&x, &y, &z]).expect("should compute");
1283
1284 assert_eq!(mat.rows(), 3);
1285 assert!((mat.get(0, 0) - 1.0).abs() < 1e-15);
1286 assert!((mat.get(1, 1) - 1.0).abs() < 1e-15);
1287 assert!((mat.get(2, 2) - 1.0).abs() < 1e-15);
1288
1289 assert!((mat.get(0, 1) - 1.0).abs() < 1e-10);
1290 assert!((mat.get(0, 2) + 1.0).abs() < 1e-10);
1291
1292 assert!((mat.get(0, 1) - mat.get(1, 0)).abs() < 1e-15);
1294 assert!((mat.get(0, 2) - mat.get(2, 0)).abs() < 1e-15);
1295 }
1296
1297 #[test]
1298 fn kendall_matrix_insufficient_variables() {
1299 let x = [1.0, 2.0, 3.0];
1300 assert!(kendall_matrix(&[&x]).is_none());
1301 }
1302
1303 #[test]
1304 fn kendall_matrix_length_mismatch() {
1305 let x = [1.0, 2.0, 3.0, 4.0];
1306 let y = [4.0, 5.0];
1307 assert!(kendall_matrix(&[&x, &y]).is_none());
1308 }
1309
1310 #[test]
1311 fn kendall_matrix_with_ties() {
1312 let x = [1.0, 2.0, 2.0, 3.0, 4.0];
1313 let y = [1.0, 2.0, 3.0, 4.0, 5.0];
1314 let mat = kendall_matrix(&[&x, &y]).expect("should compute");
1315 assert!(mat.get(0, 1) > 0.7);
1316 }
1317
1318 #[test]
1323 fn rank_no_ties() {
1324 let ranks = rank_data(&[3.0, 1.0, 2.0]);
1325 assert!((ranks[0] - 3.0).abs() < 1e-10); assert!((ranks[1] - 1.0).abs() < 1e-10); assert!((ranks[2] - 2.0).abs() < 1e-10); }
1329
1330 #[test]
1331 fn rank_with_ties() {
1332 let ranks = rank_data(&[1.0, 2.0, 2.0, 4.0]);
1333 assert!((ranks[0] - 1.0).abs() < 1e-10);
1334 assert!((ranks[1] - 2.5).abs() < 1e-10); assert!((ranks[2] - 2.5).abs() < 1e-10);
1336 assert!((ranks[3] - 4.0).abs() < 1e-10);
1337 }
1338
1339 #[test]
1340 fn rank_all_same() {
1341 let ranks = rank_data(&[5.0, 5.0, 5.0]);
1342 assert!((ranks[0] - 2.0).abs() < 1e-10); assert!((ranks[1] - 2.0).abs() < 1e-10);
1344 assert!((ranks[2] - 2.0).abs() < 1e-10);
1345 }
1346
1347 #[test]
1352 fn pvalue_significant() {
1353 let x: Vec<f64> = (0..100).map(|i| i as f64).collect();
1355 let y: Vec<f64> = x.iter().map(|&v| v * 2.0 + 1.0).collect();
1356 let result = pearson(&x, &y).expect("should compute");
1357 assert!(result.p_value < 1e-10);
1358 }
1359
1360 #[test]
1361 fn pvalue_not_significant() {
1362 let x = [1.0, 2.0, 3.0, 4.0, 5.0];
1364 let y = [5.0, 1.0, 3.0, 5.0, 1.0];
1365 let result = pearson(&x, &y).expect("should compute");
1366 assert!(result.p_value > 0.3); }
1368
1369 #[test]
1374 fn acf_lag_zero_is_one() {
1375 let data = [1.0, 3.0, 2.0, 5.0, 4.0, 7.0, 6.0, 8.0];
1376 let r = acf(&data, Some(3)).expect("should compute");
1377 assert!((r.acf[0] - 1.0).abs() < 1e-10);
1378 }
1379
1380 #[test]
1381 fn acf_linear_trend() {
1382 let data: Vec<f64> = (0..20).map(|i| i as f64).collect();
1384 let r = acf(&data, Some(5)).expect("should compute");
1385 assert!(r.acf[1] > 0.8, "lag-1 ACF for linear trend should be high");
1386 assert!(r.acf.len() == 6);
1387 }
1388
1389 #[test]
1390 fn acf_alternating() {
1391 let data: Vec<f64> = (0..30)
1393 .map(|i| if i % 2 == 0 { 1.0 } else { -1.0 })
1394 .collect();
1395 let r = acf(&data, Some(5)).expect("should compute");
1396 assert!(r.acf[1] < -0.8, "alternating → negative lag-1 ACF");
1397 assert!(r.acf[2] > 0.8, "alternating → positive lag-2 ACF");
1398 }
1399
1400 #[test]
1401 fn acf_white_noise_threshold() {
1402 let data: Vec<f64> = (0..100).map(|i| ((i * 7 + 3) % 13) as f64).collect();
1403 let r = acf(&data, Some(10)).expect("should compute");
1404 assert!((r.confidence_threshold - 1.96 / 10.0).abs() < 0.01);
1406 }
1407
1408 #[test]
1409 fn acf_edge_cases() {
1410 assert_eq!(
1411 acf(&[1.0], Some(3)).unwrap_err(),
1412 TimeSeriesError::TooFewObservations { needed: 2, got: 1 }
1413 );
1414 assert_eq!(
1415 acf(&[1.0, 2.0], Some(0)).unwrap_err(),
1416 TimeSeriesError::OptionOutOfRange {
1417 option: "max_lag",
1418 got: 0.0,
1419 min: 1.0,
1420 max: 1.0
1421 }
1422 );
1423 assert_eq!(
1424 acf(&[5.0, 5.0, 5.0, 5.0], Some(2)).unwrap_err(),
1425 TimeSeriesError::NoVariation
1426 );
1427 assert_eq!(
1429 acf(&[f64::NAN], Some(1)).unwrap_err(),
1430 TimeSeriesError::ValueNotFinite { index: 0 }
1431 );
1432 }
1433
1434 #[test]
1435 fn acf_max_lag_is_refused_past_n_minus_1_and_defaults_to_ten_log_n() {
1436 let data = [1.0, 2.0, 3.0, 4.0, 5.0];
1437 assert_eq!(
1438 acf(&data, Some(100)).unwrap_err(),
1439 TimeSeriesError::OptionOutOfRange {
1440 option: "max_lag",
1441 got: 100.0,
1442 min: 1.0,
1443 max: 4.0
1444 }
1445 );
1446 assert_eq!(acf(&data, None).expect("defined").acf.len(), 5);
1448 let long: Vec<f64> = (0..100).map(|i| ((i * 37) % 11) as f64).collect();
1449 assert_eq!(acf(&long, None).expect("defined").acf.len(), 21);
1450 }
1451
1452 #[test]
1457 fn pacf_lag1_equals_acf_lag1() {
1458 let data = [1.0, 3.0, 2.0, 5.0, 4.0, 7.0, 6.0, 8.0, 5.0, 3.0];
1459 let acf_r = acf(&data, Some(4)).expect("should compute ACF");
1460 let pacf_r = pacf(&data, Some(4)).expect("should compute PACF");
1461 assert!(
1462 (pacf_r.pacf[0] - acf_r.acf[1]).abs() < 1e-10,
1463 "PACF[1] should equal ACF[1]: {} vs {}",
1464 pacf_r.pacf[0],
1465 acf_r.acf[1]
1466 );
1467 }
1468
1469 #[test]
1470 fn pacf_linear_trend() {
1471 let data: Vec<f64> = (0..20).map(|i| i as f64).collect();
1472 let r = pacf(&data, Some(5)).expect("should compute");
1473 assert!(r.pacf[0].abs() > 0.5, "lag-1 PACF should be strong");
1475 }
1476
1477 #[test]
1478 fn pacf_bounded() {
1479 let data: Vec<f64> = (0..30).map(|i| (i as f64 * 0.5).sin()).collect();
1480 let r = pacf(&data, Some(10)).expect("should compute");
1481 for (i, &p) in r.pacf.iter().enumerate() {
1482 assert!(
1483 (-1.0..=1.0).contains(&p),
1484 "PACF[{}] = {} out of [-1, 1]",
1485 i + 1,
1486 p
1487 );
1488 }
1489 }
1490
1491 #[test]
1492 fn pacf_edge_cases() {
1493 assert_eq!(
1494 pacf(&[1.0, 2.0, 3.0], Some(1)).unwrap_err(),
1495 TimeSeriesError::TooFewObservations { needed: 4, got: 3 }
1496 );
1497 let data = [1.0, 3.0, 2.0, 5.0, 4.0, 7.0, 6.0, 8.0, 5.0, 3.0];
1499 for k in [0, 5] {
1500 assert_eq!(
1501 pacf(&data, Some(k)).unwrap_err(),
1502 TimeSeriesError::OptionOutOfRange {
1503 option: "max_lag",
1504 got: k as f64,
1505 min: 1.0,
1506 max: 4.0
1507 }
1508 );
1509 }
1510 assert_eq!(pacf(&data, None).expect("defined").pacf.len(), 4);
1511 }
1512}
1513
1514#[cfg(test)]
1515mod proptests {
1516 use super::*;
1517 use proptest::prelude::*;
1518
1519 fn bounded_vec(min_len: usize, max_len: usize) -> BoxedStrategy<Vec<f64>> {
1520 proptest::collection::vec(-1e6_f64..1e6, min_len..=max_len).boxed()
1521 }
1522
1523 proptest! {
1524 #[test]
1525 fn pearson_bounded(
1526 data in bounded_vec(5, 50).prop_flat_map(|x| {
1527 let n = x.len();
1528 (Just(x), proptest::collection::vec(-1e6_f64..1e6, n..=n))
1529 })
1530 ) {
1531 let (x, y) = data;
1532 if let Some(result) = pearson(&x, &y) {
1533 prop_assert!(result.r >= -1.0 && result.r <= 1.0, "r out of bounds: {}", result.r);
1534 prop_assert!(result.p_value >= 0.0 && result.p_value <= 1.0, "p out of bounds: {}", result.p_value);
1535 }
1536 }
1537
1538 #[test]
1539 fn spearman_bounded(
1540 data in bounded_vec(5, 50).prop_flat_map(|x| {
1541 let n = x.len();
1542 (Just(x), proptest::collection::vec(-1e6_f64..1e6, n..=n))
1543 })
1544 ) {
1545 let (x, y) = data;
1546 if let Some(result) = spearman(&x, &y) {
1547 prop_assert!(result.r >= -1.0 && result.r <= 1.0, "r out of bounds: {}", result.r);
1548 prop_assert!(result.p_value >= 0.0 && result.p_value <= 1.0, "p out of bounds: {}", result.p_value);
1549 }
1550 }
1551
1552 #[test]
1553 fn kendall_bounded(
1554 data in bounded_vec(5, 30).prop_flat_map(|x| {
1555 let n = x.len();
1556 (Just(x), proptest::collection::vec(-1e6_f64..1e6, n..=n))
1557 })
1558 ) {
1559 let (x, y) = data;
1560 if let Some(result) = kendall_tau_b(&x, &y) {
1561 prop_assert!(result.r >= -1.0 && result.r <= 1.0, "tau out of bounds: {}", result.r);
1562 prop_assert!(result.p_value >= 0.0 && result.p_value <= 1.0, "p out of bounds: {}", result.p_value);
1563 }
1564 }
1565
1566 #[test]
1567 fn pearson_symmetric(
1568 data in bounded_vec(5, 50).prop_flat_map(|x| {
1569 let n = x.len();
1570 (Just(x), proptest::collection::vec(-1e6_f64..1e6, n..=n))
1571 })
1572 ) {
1573 let (x, y) = data;
1574 let r_xy = pearson(&x, &y);
1575 let r_yx = pearson(&y, &x);
1576 match (r_xy, r_yx) {
1577 (Some(a), Some(b)) => {
1578 prop_assert!((a.r - b.r).abs() < 1e-10, "not symmetric: {} vs {}", a.r, b.r);
1579 }
1580 (None, None) => {}
1581 _ => prop_assert!(false, "one is None but not the other"),
1582 }
1583 }
1584
1585 #[test]
1586 fn fisher_z_roundtrip_prop(r in -0.99_f64..0.99) {
1587 let z = fisher_z(r).expect("should compute");
1588 let r_back = fisher_z_inv(z);
1589 prop_assert!((r - r_back).abs() < 1e-10);
1590 }
1591
1592 #[test]
1593 fn ci_contains_true_r(
1594 r in -0.99_f64..0.99,
1595 n in 10_usize..200
1596 ) {
1597 let ci = correlation_ci(r, n, 0.95).expect("should compute");
1598 prop_assert!(ci.lower < ci.upper, "CI inverted");
1599 prop_assert!(ci.lower >= -1.0 && ci.upper <= 1.0, "CI out of bounds");
1600 }
1601
1602 #[test]
1603 fn acf_bounded_prop(
1604 data in proptest::collection::vec(-1e3_f64..1e3, 5..=50),
1605 ) {
1606 if let Ok(r) = acf(&data, Some(10)) {
1607 prop_assert!((r.acf[0] - 1.0).abs() < 1e-10, "ACF[0] must be 1.0");
1608 for (i, &v) in r.acf.iter().enumerate() {
1609 prop_assert!((-1.0..=1.0).contains(&v), "ACF[{i}] = {v} out of [-1,1]");
1610 }
1611 }
1612 }
1613
1614 #[test]
1615 fn pacf_bounded_prop(
1616 data in proptest::collection::vec(-1e3_f64..1e3, 5..=50),
1617 ) {
1618 if let Ok(r) = pacf(&data, Some(5)) {
1619 for (i, &v) in r.pacf.iter().enumerate() {
1620 prop_assert!((-1.0 - 1e-10..=1.0 + 1e-10).contains(&v), "PACF[{i}] = {v} out of bounds");
1621 }
1622 }
1623 }
1624 }
1625}
1626
1627#[cfg(test)]
1629mod scipy_tail_reference {
1630 use super::*;
1631
1632 #[test]
1633 fn pearson_p_value_keeps_its_tail() {
1634 let x: Vec<f64> = (1..=20).map(f64::from).collect();
1635 let y = [
1636 5.3999999999999995,
1637 7.0,
1638 9.4,
1639 11.8,
1640 13.2,
1641 15.8,
1642 17.6,
1643 19.8,
1644 22.250000000000004,
1645 23.75,
1646 26.25,
1647 28.150000000000002,
1648 30.55,
1649 32.050000000000004,
1650 34.55,
1651 36.7,
1652 38.550000000000004,
1653 41.1,
1654 42.699999999999996,
1655 45.0,
1656 ];
1657 let got = pearson(&x, &y).expect("valid").p_value;
1658 let want = 8.20335692044105e-33;
1659 assert!(
1660 (got - want).abs() <= 1e-9 * want,
1661 "p: got {got:e}, SciPy {want:e}"
1662 );
1663 }
1664}
1665
1666#[cfg(test)]
1669mod kendall_exact_tests {
1670 use super::*;
1671
1672 const EXACT_CASES: &[(&[u8], f64, f64)] = &[
1674 (&[2, 1, 0], -1.0, 0.3333333333333333),
1675 (&[2, 1, 0], -1.0, 0.3333333333333333),
1676 (&[2, 1, 0, 3], 0.0, 1.0),
1677 (&[2, 0, 3, 1], 0.0, 1.0),
1678 (&[4, 0, 3, 1, 2], -0.19999999999999998, 0.8166666666666667),
1679 (&[2, 0, 3, 4, 1], 0.19999999999999998, 0.8166666666666667),
1680 (&[4, 1, 3, 2, 5, 0], -0.2, 0.7194444444444444),
1681 (&[3, 2, 1, 5, 0, 4], -0.06666666666666665, 1.0),
1682 (
1683 &[0, 5, 3, 1, 4, 2, 6],
1684 0.33333333333333337,
1685 0.3813492063492063,
1686 ),
1687 (
1688 &[5, 2, 4, 1, 3, 0, 6],
1689 -0.14285714285714288,
1690 0.7726190476190476,
1691 ),
1692 (
1693 &[1, 4, 3, 0, 7, 5, 6, 2],
1694 0.21428571428571427,
1695 0.5484126984126985,
1696 ),
1697 (
1698 &[5, 2, 3, 6, 1, 0, 4, 7],
1699 0.07142857142857142,
1700 0.9048611111111111,
1701 ),
1702 (
1703 &[2, 3, 6, 7, 1, 4, 8, 5, 0],
1704 0.05555555555555555,
1705 0.919455467372134,
1706 ),
1707 (
1708 &[7, 8, 1, 6, 0, 3, 4, 5, 2],
1709 -0.2777777777777778,
1710 0.35848765432098767,
1711 ),
1712 (
1713 &[7, 4, 2, 5, 9, 0, 8, 3, 6, 1],
1714 -0.15555555555555553,
1715 0.6006536596119929,
1716 ),
1717 (
1718 &[3, 4, 6, 0, 1, 9, 8, 5, 2, 7],
1719 0.19999999999999998,
1720 0.4843127204585538,
1721 ),
1722 (
1723 &[10, 7, 6, 4, 5, 3, 1, 2, 11, 0, 9, 8],
1724 -0.2121212121212121,
1725 0.38070480349126185,
1726 ),
1727 (
1728 &[7, 4, 3, 9, 10, 8, 2, 1, 6, 5, 0, 11],
1729 -0.1212121212121212,
1730 0.6383612539081289,
1731 ),
1732 (
1733 &[6, 10, 0, 13, 1, 4, 5, 2, 9, 3, 12, 7, 14, 8, 11],
1734 0.29523809523809524,
1735 0.13946285911906778,
1736 ),
1737 (
1738 &[9, 11, 12, 1, 4, 3, 6, 8, 7, 14, 13, 0, 2, 5, 10],
1739 -0.028571428571428574,
1740 0.9225700904951897,
1741 ),
1742 (
1743 &[11, 17, 13, 1, 6, 4, 2, 5, 10, 7, 12, 9, 14, 16, 15, 3, 0, 8],
1744 0.006535947712418302,
1745 1.0,
1746 ),
1747 (
1748 &[8, 16, 12, 4, 13, 6, 10, 17, 2, 5, 1, 15, 9, 14, 7, 11, 0, 3],
1749 -0.21568627450980396,
1750 0.22906043853787195,
1751 ),
1752 (
1753 &[
1754 15, 3, 0, 13, 11, 8, 1, 7, 17, 19, 10, 14, 12, 2, 5, 4, 6, 18, 9, 16,
1755 ],
1756 0.09473684210526316,
1757 0.5858584459782121,
1758 ),
1759 (
1760 &[
1761 7, 13, 17, 12, 11, 19, 2, 16, 8, 3, 10, 9, 6, 15, 18, 14, 0, 4, 5, 1,
1762 ],
1763 -0.2736842105263158,
1764 0.0983302187347563,
1765 ),
1766 (
1767 &[
1768 9, 21, 11, 3, 17, 12, 8, 1, 5, 13, 15, 22, 6, 4, 18, 2, 0, 14, 16, 23, 19, 7, 10,
1769 20,
1770 ],
1771 0.11594202898550725,
1772 0.44607885611402504,
1773 ),
1774 (
1775 &[
1776 17, 2, 18, 7, 10, 21, 15, 14, 13, 23, 1, 4, 6, 9, 3, 8, 22, 0, 20, 5, 11, 19, 12,
1777 16,
1778 ],
1779 -0.007246376811594203,
1780 0.9804334461613707,
1781 ),
1782 (
1783 &[
1784 20, 13, 24, 18, 14, 2, 9, 5, 10, 15, 11, 26, 12, 21, 25, 19, 16, 22, 27, 3, 1, 17,
1785 8, 7, 23, 4, 6, 0,
1786 ],
1787 -0.15873015873015872,
1788 0.24626955123962418,
1789 ),
1790 (
1791 &[
1792 3, 11, 14, 0, 6, 7, 21, 24, 19, 10, 8, 2, 27, 16, 26, 20, 23, 15, 9, 5, 13, 1, 25,
1793 4, 17, 12, 22, 18,
1794 ],
1795 0.13227513227513227,
1796 0.3362383470773132,
1797 ),
1798 (
1799 &[
1800 29, 16, 28, 19, 18, 12, 13, 27, 1, 25, 4, 17, 22, 15, 3, 2, 10, 8, 9, 23, 11, 7,
1801 21, 0, 26, 6, 5, 14, 24, 20,
1802 ],
1803 -0.18160919540229886,
1804 0.1653935231257671,
1805 ),
1806 (
1807 &[
1808 17, 10, 9, 22, 19, 18, 8, 5, 21, 29, 13, 28, 14, 20, 27, 25, 0, 23, 24, 7, 16, 12,
1809 11, 4, 2, 26, 6, 1, 3, 15,
1810 ],
1811 -0.20459770114942533,
1812 0.11698808645983594,
1813 ),
1814 (
1815 &[
1816 31, 24, 14, 11, 29, 12, 28, 16, 10, 7, 6, 3, 8, 17, 25, 20, 26, 0, 13, 21, 5, 9,
1817 18, 23, 27, 1, 22, 19, 2, 30, 15, 4, 32,
1818 ],
1819 -0.041666666666666664,
1820 0.7468360016830153,
1821 ),
1822 (
1823 &[
1824 0, 27, 13, 32, 19, 21, 24, 25, 30, 16, 23, 8, 26, 7, 5, 14, 29, 20, 31, 22, 28, 6,
1825 15, 2, 4, 18, 9, 10, 1, 17, 11, 12, 3,
1826 ],
1827 -0.27272727272727276,
1828 0.025883100486831147,
1829 ),
1830 (
1831 &[
1832 0, 1, 2, 4, 3, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19,
1833 ],
1834 0.9894736842105263,
1835 1.644127049324866e-17,
1836 ),
1837 (
1838 &[
1839 0, 1, 2, 4, 3, 5, 6, 7, 8, 9, 11, 10, 12, 13, 14, 15, 16, 17, 18, 19,
1840 ],
1841 0.9789473684210527,
1842 1.7181127665444849e-16,
1843 ),
1844 (
1845 &[
1846 19, 18, 17, 16, 15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0,
1847 ],
1848 -1.0,
1849 8.22063524662433e-19,
1850 ),
1851 (
1852 &[
1853 19, 18, 17, 16, 15, 13, 14, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0,
1854 ],
1855 -0.9894736842105263,
1856 1.644127049324866e-17,
1857 ),
1858 (
1859 &[
1860 0, 1, 2, 4, 3, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22,
1861 23, 24, 25, 26, 27, 28, 29, 30, 31, 32,
1862 ],
1863 0.9962121212121212,
1864 7.600781509709488e-36,
1865 ),
1866 (
1867 &[
1868 0, 1, 2, 4, 3, 5, 6, 7, 8, 9, 11, 10, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22,
1869 23, 24, 25, 26, 27, 28, 29, 30, 31, 32,
1870 ],
1871 0.9924242424242423,
1872 1.2898295895264586e-34,
1873 ),
1874 (
1875 &[
1876 32, 31, 30, 29, 28, 27, 26, 25, 24, 23, 22, 21, 20, 19, 18, 17, 16, 15, 14, 13, 12,
1877 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0,
1878 ],
1879 -1.0,
1880 2.30326712415439e-37,
1881 ),
1882 (
1883 &[
1884 32, 31, 30, 29, 28, 26, 27, 25, 24, 23, 22, 21, 20, 19, 18, 17, 16, 15, 14, 13, 12,
1885 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0,
1886 ],
1887 -0.9962121212121212,
1888 7.600781509709488e-36,
1889 ),
1890 (
1891 &[
1892 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22,
1893 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33,
1894 ],
1895 1.0,
1896 6.774315071042324e-39,
1897 ),
1898 (
1899 &[
1900 0, 1, 2, 3, 4, 5, 6, 8, 7, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22,
1901 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33,
1902 ],
1903 0.9964349376114083,
1904 2.30326712415439e-37,
1905 ),
1906 (
1907 &[
1908 33, 32, 31, 30, 29, 28, 27, 26, 25, 24, 23, 22, 21, 20, 19, 18, 17, 16, 15, 14, 13,
1909 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0,
1910 ],
1911 -1.0,
1912 6.774315071042324e-39,
1913 ),
1914 (
1915 &[
1916 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22,
1917 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43,
1918 44, 45, 46, 47, 48, 49,
1919 ],
1920 1.0,
1921 6.575898833266317e-65,
1922 ),
1923 (
1924 &[
1925 0, 1, 2, 3, 4, 5, 6, 8, 7, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22,
1926 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43,
1927 44, 45, 46, 47, 48, 49,
1928 ],
1929 0.9983673469387755,
1930 3.287949416633158e-63,
1931 ),
1932 (
1933 &[
1934 49, 48, 47, 46, 45, 44, 43, 42, 41, 40, 39, 38, 37, 36, 35, 34, 33, 32, 31, 30, 29,
1935 28, 27, 26, 25, 24, 23, 22, 21, 20, 19, 18, 17, 16, 15, 14, 13, 12, 11, 10, 9, 8,
1936 7, 6, 5, 4, 3, 2, 1, 0,
1937 ],
1938 -1.0,
1939 6.575898833266317e-65,
1940 ),
1941 (
1942 &[
1943 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22,
1944 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43,
1945 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64,
1946 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85,
1947 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99,
1948 ],
1949 1.0,
1950 2.143020576250934e-158,
1951 ),
1952 (
1953 &[
1954 0, 1, 2, 3, 4, 5, 6, 8, 7, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22,
1955 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43,
1956 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64,
1957 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85,
1958 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99,
1959 ],
1960 0.9995959595959597,
1961 2.1430205762509337e-156,
1962 ),
1963 (
1964 &[
1965 99, 98, 97, 96, 95, 94, 93, 92, 91, 90, 89, 88, 87, 86, 85, 84, 83, 82, 81, 80, 79,
1966 78, 77, 76, 75, 74, 73, 72, 71, 70, 69, 68, 67, 66, 65, 64, 63, 62, 61, 60, 59, 58,
1967 57, 56, 55, 54, 53, 52, 51, 50, 49, 48, 47, 46, 45, 44, 43, 42, 41, 40, 39, 38, 37,
1968 36, 35, 34, 33, 32, 31, 30, 29, 28, 27, 26, 25, 24, 23, 22, 21, 20, 19, 18, 17, 16,
1969 15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0,
1970 ],
1971 -1.0,
1972 2.143020576250934e-158,
1973 ),
1974 (
1975 &[
1976 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22,
1977 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43,
1978 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64,
1979 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85,
1980 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, 100, 101, 102, 103, 104,
1981 105, 106, 107, 108, 109, 110, 111, 112, 113, 114, 115, 116, 117, 118, 119, 120,
1982 121, 122, 123, 124, 125, 126, 127, 128, 129, 130, 131, 132, 133, 134, 135, 136,
1983 137, 138, 139, 140, 141, 142, 143, 144, 145, 146, 147, 148, 149, 150, 151, 152,
1984 153, 154, 155, 156, 157, 158, 159, 160, 161, 162, 163, 164, 165, 166, 167, 168,
1985 169,
1986 ],
1987 1.0,
1988 2.755801935583541e-307,
1989 ),
1990 (
1991 &[
1992 0, 1, 2, 3, 4, 5, 6, 8, 7, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22,
1993 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43,
1994 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64,
1995 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85,
1996 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, 100, 101, 102, 103, 104,
1997 105, 106, 107, 108, 109, 110, 111, 112, 113, 114, 115, 116, 117, 118, 119, 120,
1998 121, 122, 123, 124, 125, 126, 127, 128, 129, 130, 131, 132, 133, 134, 135, 136,
1999 137, 138, 139, 140, 141, 142, 143, 144, 145, 146, 147, 148, 149, 150, 151, 152,
2000 153, 154, 155, 156, 157, 158, 159, 160, 161, 162, 163, 164, 165, 166, 167, 168,
2001 169,
2002 ],
2003 0.9998607727114516,
2004 4.68486329049202e-305,
2005 ),
2006 (
2007 &[
2008 169, 168, 167, 166, 165, 164, 163, 162, 161, 160, 159, 158, 157, 156, 155, 154,
2009 153, 152, 151, 150, 149, 148, 147, 146, 145, 144, 143, 142, 141, 140, 139, 138,
2010 137, 136, 135, 134, 133, 132, 131, 130, 129, 128, 127, 126, 125, 124, 123, 122,
2011 121, 120, 119, 118, 117, 116, 115, 114, 113, 112, 111, 110, 109, 108, 107, 106,
2012 105, 104, 103, 102, 101, 100, 99, 98, 97, 96, 95, 94, 93, 92, 91, 90, 89, 88, 87,
2013 86, 85, 84, 83, 82, 81, 80, 79, 78, 77, 76, 75, 74, 73, 72, 71, 70, 69, 68, 67, 66,
2014 65, 64, 63, 62, 61, 60, 59, 58, 57, 56, 55, 54, 53, 52, 51, 50, 49, 48, 47, 46, 45,
2015 44, 43, 42, 41, 40, 39, 38, 37, 36, 35, 34, 33, 32, 31, 30, 29, 28, 27, 26, 25, 24,
2016 23, 22, 21, 20, 19, 18, 17, 16, 15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1,
2017 0,
2018 ],
2019 -1.0,
2020 2.755801935583541e-307,
2021 ),
2022 (
2023 &[
2024 7, 11, 20, 1, 33, 32, 29, 18, 3, 5, 27, 10, 8, 0, 31, 23, 9, 13, 19, 17, 28, 24, 6,
2025 4, 2, 26, 15, 16, 12, 14, 25, 21, 22, 30,
2026 ],
2027 0.07664884135472372,
2028 0.5238310765052258,
2029 ),
2030 (
2031 &[
2032 29, 22, 18, 16, 38, 31, 2, 24, 11, 23, 30, 5, 4, 34, 15, 33, 36, 6, 32, 27, 12, 14,
2033 19, 0, 37, 8, 35, 20, 26, 13, 21, 28, 7, 3, 17, 1, 9, 25, 39, 10,
2034 ],
2035 -0.10512820512820513,
2036 0.33938312065834597,
2037 ),
2038 (
2039 &[
2040 6, 51, 13, 41, 17, 52, 24, 35, 53, 2, 49, 22, 43, 39, 30, 58, 10, 59, 8, 21, 5, 20,
2041 50, 4, 44, 47, 38, 9, 36, 32, 42, 15, 26, 18, 37, 45, 23, 55, 25, 40, 28, 54, 11,
2042 7, 48, 16, 3, 27, 0, 34, 14, 46, 33, 56, 12, 1, 31, 57, 19, 29,
2043 ],
2044 -0.05536723163841807,
2045 0.5319468085548157,
2046 ),
2047 ];
2048
2049 const TIE_CASES: &[(&[f64], &[f64], f64, f64)] = &[
2051 (
2052 &[0.0, 2.0, 1.0, 1.0, 2.0, 0.0, 2.0, 1.0],
2053 &[3.0, 2.2, 1.6, 1.6, 2.2, 3.0, 1.2, 0.6],
2054 -0.30550504633038933,
2055 0.3441320967322945,
2056 ),
2057 (
2058 &[3.0, 1.0, 0.0, 3.0, 2.0, 3.0, 2.0, 2.0, 3.0, 0.0, 3.0, 1.0],
2059 &[
2060 9.8,
2061 10.6,
2062 5.0,
2063 12.8,
2064 3.2,
2065 9.8,
2066 2.2,
2067 2.2,
2068 1.7999999999999998,
2069 10.0,
2070 3.8,
2071 1.6,
2072 ],
2073 0.05251050315105037,
2074 0.827163859987499,
2075 ),
2076 (
2077 &[5.0, 4.0, 3.0, 4.0, 3.0, 4.0, 7.0, 1.0, 6.0, 2.0, 6.0, 9.0],
2078 &[
2079 4.0,
2080 2.4,
2081 3.8,
2082 3.4,
2083 2.8,
2084 2.4,
2085 6.2,
2086 1.6,
2087 5.6,
2088 1.2,
2089 4.6,
2090 5.3999999999999995,
2091 ],
2092 0.7146459189048197,
2093 0.001720169176312472,
2094 ),
2095 (
2096 &[
2097 3.0, 4.0, 1.0, 1.0, 0.0, 4.0, 4.0, 1.0, 2.0, 4.0, 2.0, 2.0, 4.0, 3.0, 3.0, 4.0,
2098 4.0, 1.0, 3.0, 3.0, 0.0, 0.0, 3.0, 2.0, 4.0,
2099 ],
2100 &[
2101 1.7999999999999998,
2102 5.4,
2103 4.6,
2104 0.6,
2105 0.0,
2106 3.4,
2107 4.4,
2108 1.6,
2109 1.2,
2110 4.4,
2111 3.2,
2112 2.2,
2113 7.4,
2114 6.8,
2115 5.8,
2116 2.4,
2117 4.4,
2118 4.6,
2119 6.8,
2120 2.8,
2121 2.0,
2122 0.0,
2123 2.8,
2124 2.2,
2125 2.4,
2126 ],
2127 0.4371231544800122,
2128 0.005038720516121261,
2129 ),
2130 (
2131 &[
2132 4.0, 6.0, 4.0, 1.0, 6.0, 5.0, 7.0, 6.0, 7.0, 2.0, 4.0, 6.0, 4.0, 0.0, 5.0, 0.0,
2133 7.0, 5.0, 2.0, 3.0, 7.0, 7.0, 4.0, 7.0, 0.0, 6.0, 6.0, 6.0, 1.0, 5.0, 1.0, 6.0,
2134 6.0, 2.0, 7.0, 7.0, 2.0, 6.0, 6.0, 6.0,
2135 ],
2136 &[
2137 9.4,
2138 3.5999999999999996,
2139 3.4,
2140 1.6,
2141 9.6,
2142 5.0,
2143 10.2,
2144 10.6,
2145 8.2,
2146 5.2,
2147 5.4,
2148 8.6,
2149 2.4,
2150 4.0,
2151 7.0,
2152 3.0,
2153 10.2,
2154 6.0,
2155 3.2,
2156 5.8,
2157 5.2,
2158 10.2,
2159 3.4,
2160 10.2,
2161 1.0,
2162 10.6,
2163 9.6,
2164 9.6,
2165 4.6,
2166 3.0,
2167 5.6,
2168 5.6,
2169 8.6,
2170 3.2,
2171 10.2,
2172 4.2,
2173 3.2,
2174 10.6,
2175 9.6,
2176 9.6,
2177 ],
2178 0.5598074769339775,
2179 2.6671536744057452e-06,
2180 ),
2181 (
2182 &[
2183 1.0, 4.0, 2.0, 0.0, 7.0, 3.0, 9.0, 2.0, 3.0, 3.0, 7.0, 4.0, 8.0, 9.0, 2.0, 4.0,
2184 2.0, 0.0, 2.0, 3.0, 6.0, 4.0, 2.0, 3.0, 0.0, 0.0, 3.0, 9.0, 6.0, 0.0, 7.0, 7.0,
2185 4.0, 5.0, 4.0, 8.0, 4.0, 6.0, 0.0, 0.0, 1.0, 0.0, 4.0, 0.0, 6.0, 6.0, 9.0, 1.0,
2186 7.0, 1.0, 5.0, 3.0, 1.0, 0.0, 1.0, 9.0, 4.0, 7.0, 3.0, 6.0, 3.0, 7.0, 5.0, 9.0,
2187 1.0, 1.0, 1.0, 7.0, 0.0, 8.0, 6.0, 5.0, 0.0, 2.0, 9.0, 7.0, 9.0, 1.0, 0.0, 9.0,
2188 8.0, 2.0, 7.0, 8.0, 7.0, 6.0, 8.0, 6.0, 0.0, 6.0, 0.0, 0.0, 0.0, 0.0, 6.0, 6.0,
2189 0.0, 6.0, 6.0, 2.0,
2190 ],
2191 &[
2192 2.6,
2193 8.4,
2194 3.2,
2195 6.0,
2196 8.2,
2197 6.8,
2198 6.3999999999999995,
2199 1.2,
2200 5.8,
2201 7.8,
2202 7.2,
2203 4.4,
2204 6.8,
2205 10.399999999999999,
2206 4.2,
2207 7.4,
2208 3.2,
2209 4.0,
2210 7.2,
2211 4.8,
2212 4.6,
2213 5.4,
2214 4.2,
2215 4.8,
2216 5.0,
2217 3.0,
2218 4.8,
2219 11.399999999999999,
2220 5.6,
2221 0.0,
2222 10.2,
2223 9.2,
2224 6.4,
2225 9.0,
2226 2.4,
2227 9.8,
2228 2.4,
2229 9.6,
2230 2.0,
2231 3.0,
2232 2.6,
2233 4.0,
2234 7.4,
2235 3.0,
2236 3.5999999999999996,
2237 5.6,
2238 10.399999999999999,
2239 1.6,
2240 5.2,
2241 6.6,
2242 5.0,
2243 1.7999999999999998,
2244 4.6,
2245 5.0,
2246 2.6,
2247 11.399999999999999,
2248 6.4,
2249 7.2,
2250 5.8,
2251 4.6,
2252 4.8,
2253 8.2,
2254 8.0,
2255 9.399999999999999,
2256 1.6,
2257 2.6,
2258 2.6,
2259 9.2,
2260 6.0,
2261 9.8,
2262 9.6,
2263 8.0,
2264 1.0,
2265 1.2,
2266 10.399999999999999,
2267 10.2,
2268 8.399999999999999,
2269 2.6,
2270 2.0,
2271 11.399999999999999,
2272 8.8,
2273 3.2,
2274 10.2,
2275 8.8,
2276 8.2,
2277 5.6,
2278 10.8,
2279 7.6,
2280 3.0,
2281 8.6,
2282 3.0,
2283 5.0,
2284 0.0,
2285 4.0,
2286 7.6,
2287 7.6,
2288 1.0,
2289 6.6,
2290 7.6,
2291 5.2,
2292 ],
2293 0.6273681127435055,
2294 2.3395873885354134e-18,
2295 ),
2296 (
2297 &[
2298 2.0, 81.0, 93.0, 5.0, 23.0, 26.0, 30.0, 18.0, 96.0, 39.0, 2.0, 30.0, 60.0, 40.0,
2299 90.0, 32.0, 63.0, 92.0, 6.0, 28.0, 85.0, 70.0, 35.0, 39.0, 58.0, 21.0, 23.0, 50.0,
2300 79.0, 79.0, 35.0, 15.0, 24.0, 94.0, 52.0, 3.0, 55.0, 72.0, 45.0, 76.0, 16.0, 93.0,
2301 42.0, 61.0, 75.0, 57.0, 87.0, 25.0, 54.0, 88.0, 46.0, 71.0, 57.0, 11.0, 74.0, 56.0,
2302 68.0, 56.0, 29.0, 88.0, 4.0, 19.0, 75.0, 38.0, 85.0, 86.0, 25.0, 96.0, 42.0, 10.0,
2303 34.0, 82.0, 27.0, 9.0, 26.0, 32.0, 24.0, 82.0, 7.0, 62.0, 90.0, 10.0, 30.0, 52.0,
2304 21.0, 32.0, 37.0, 0.0, 76.0, 26.0, 24.0, 50.0, 23.0, 41.0, 58.0, 32.0, 51.0, 0.0,
2305 19.0, 49.0,
2306 ],
2307 &[
2308 1.2,
2309 51.6,
2310 58.8,
2311 6.0,
2312 14.799999999999999,
2313 15.6,
2314 21.0,
2315 10.799999999999999,
2316 59.599999999999994,
2317 24.4,
2318 4.2,
2319 19.0,
2320 36.0,
2321 25.0,
2322 56.0,
2323 20.2,
2324 40.8,
2325 57.199999999999996,
2326 3.5999999999999996,
2327 19.8,
2328 51.0,
2329 44.0,
2330 22.0,
2331 24.4,
2332 35.8,
2333 14.6,
2334 16.799999999999997,
2335 32.0,
2336 47.4,
2337 47.4,
2338 24.0,
2339 9.0,
2340 17.4,
2341 59.4,
2342 34.2,
2343 1.7999999999999998,
2344 33.0,
2345 43.199999999999996,
2346 29.0,
2347 47.6,
2348 10.6,
2349 58.8,
2350 26.2,
2351 36.6,
2352 47.0,
2353 37.199999999999996,
2354 53.199999999999996,
2355 15.0,
2356 32.4,
2357 52.8,
2358 27.599999999999998,
2359 43.6,
2360 36.199999999999996,
2361 8.6,
2362 47.4,
2363 36.6,
2364 43.8,
2365 33.6,
2366 19.4,
2367 53.8,
2368 4.4,
2369 12.4,
2370 47.0,
2371 23.8,
2372 54.0,
2373 51.6,
2374 18.0,
2375 60.599999999999994,
2376 27.2,
2377 9.0,
2378 22.4,
2379 49.199999999999996,
2380 17.2,
2381 6.3999999999999995,
2382 18.6,
2383 22.2,
2384 17.4,
2385 49.199999999999996,
2386 6.2,
2387 40.199999999999996,
2388 55.0,
2389 7.0,
2390 18.0,
2391 32.2,
2392 12.6,
2393 20.2,
2394 23.2,
2395 0.0,
2396 45.6,
2397 16.6,
2398 15.399999999999999,
2399 31.0,
2400 16.799999999999997,
2401 26.599999999999998,
2402 34.8,
2403 19.2,
2404 33.599999999999994,
2405 0.0,
2406 14.4,
2407 32.4,
2408 ],
2409 0.9619049618044789,
2410 3.673981239914972e-45,
2411 ),
2412 ];
2413
2414 fn assert_close(got: f64, want: f64, rel: f64, what: &str) {
2415 let scale = want.abs().max(1e-300);
2416 assert!(
2417 (got - want).abs() <= rel * scale,
2418 "{what}: got {got:e}, want {want:e}"
2419 );
2420 }
2421
2422 #[test]
2427 fn kendall_p_values_reproduce_scipy_without_ties() {
2428 for (perm, tau, p) in EXACT_CASES {
2429 let x: Vec<f64> = (0..perm.len()).map(|i| i as f64).collect();
2430 let y: Vec<f64> = perm.iter().map(|&v| f64::from(v)).collect();
2431 let r = kendall_tau_b(&x, &y).expect("computes");
2432 let what = format!("n = {}", perm.len());
2433 assert!((r.r - tau).abs() < 1e-12, "{what}: tau {} vs {tau}", r.r);
2434 assert_close(r.p_value, *p, 1e-12, &what);
2435 }
2436 }
2437
2438 #[test]
2441 fn kendall_p_values_reproduce_scipy_with_ties() {
2442 assert!(!TIE_CASES.is_empty());
2443 for (x, y, tau, p) in TIE_CASES {
2444 let r = kendall_tau_b(x, y).expect("computes");
2445 let what = format!("n = {} with ties", x.len());
2446 assert!((r.r - tau).abs() < 1e-12, "{what}: tau {} vs {tau}", r.r);
2447 assert_close(r.p_value, *p, 1e-10, &what);
2448 }
2449 }
2450
2451 #[test]
2455 fn exact_p_is_the_share_of_permutations_with_fewer_inversions() {
2456 fn permutations(
2457 n: usize,
2458 out: &mut Vec<Vec<usize>>,
2459 cur: &mut Vec<usize>,
2460 used: &mut [bool],
2461 ) {
2462 if cur.len() == n {
2463 out.push(cur.clone());
2464 return;
2465 }
2466 for v in 0..n {
2467 if !used[v] {
2468 used[v] = true;
2469 cur.push(v);
2470 permutations(n, out, cur, used);
2471 cur.pop();
2472 used[v] = false;
2473 }
2474 }
2475 }
2476 for n in 3..=8usize {
2477 let mut all = Vec::new();
2478 permutations(n, &mut all, &mut Vec::new(), &mut vec![false; n]);
2479 let inversions: Vec<i64> = all
2480 .iter()
2481 .map(|p| {
2482 (0..n)
2483 .flat_map(|i| (i + 1..n).map(move |j| (i, j)))
2484 .filter(|&(i, j)| p[i] > p[j])
2485 .count() as i64
2486 })
2487 .collect();
2488 let total = (n * (n - 1) / 2) as i64;
2489 for d in 0..=total {
2490 let tail = d.min(total - d);
2491 let share =
2492 inversions.iter().filter(|&&k| k <= tail).count() as f64 / all.len() as f64;
2493 let want = if 4 * tail == (n * (n - 1)) as i64 {
2494 1.0
2495 } else {
2496 (2.0 * share).min(1.0)
2497 };
2498 assert_close(
2499 kendall_exact_p(n, d),
2500 want,
2501 1e-14,
2502 &format!("n = {n}, D = {d}"),
2503 );
2504 }
2505 }
2506 }
2507
2508 #[test]
2510 fn exact_p_is_symmetric_and_a_probability() {
2511 for n in [3usize, 10, 33] {
2512 let total = (n * (n - 1) / 2) as i64;
2513 for d in 0..=total {
2514 let p = kendall_exact_p(n, d);
2515 assert!((0.0..=1.0).contains(&p), "n = {n}, D = {d}: {p}");
2516 assert_eq!(p, kendall_exact_p(n, total - d), "n = {n}, D = {d}");
2517 }
2518 }
2519 }
2520
2521 #[test]
2524 fn beyond_33_only_the_smallest_tails_are_exact() {
2525 let n = 40;
2526 let x: Vec<f64> = (0..n).map(f64::from).collect();
2527 let mut y = x.clone();
2529 y.swap(7, 8);
2530 let r = kendall_tau_b(&x, &y).expect("computes");
2531 let factorial_39: f64 = (2..=39).map(f64::from).product();
2532 assert_close(r.p_value, 2.0 / factorial_39, 1e-12, "tail 1");
2533 y.swap(20, 21);
2536 let r2 = kendall_tau_b(&x, &y).expect("computes");
2537 assert!(r2.p_value > r.p_value && r2.p_value < 1e-9);
2538 }
2539}