use ordered_float::OrderedFloat;
use num_traits::float::FloatCore;
pub fn xicorf_norm<F: FloatCore>(x: &[F], y: &[F]) -> f64 {
let n = x.len() as f64;
let lim = (n-2.)/(n+1.);
xicorf(x, y)/lim
}
pub fn xicor_norm<T: Ord + Copy>(x: &[T], y: &[T]) -> f64 {
let n = x.len() as f64;
let lim = (n-2.)/(n+1.);
xicor(x, y)/lim
}
pub fn xicorf<F: FloatCore>(x: &[F], y: &[F]) -> f64 {
let x: &[OrderedFloat<F>] = unsafe { std::mem::transmute(x) };
let y: &[OrderedFloat<F>] = unsafe { std::mem::transmute(y) };
xicor(x, y)
}
pub fn xicor<T: Ord + Copy>(x: &[T], y: &[T]) -> f64 {
assert!(x.len() == y.len(), "x and y must have the same length");
let idcs = argsort(x);
let y_ord = permute(y, &idcs);
let idcs = argsort(&y_ord);
let y_ascending = permute(&y_ord, &idcs);
let r_ascending = cumulative_lte(&y_ascending);
let l_ascending = cumulative_gte(&y_ascending);
let mut rs = vec![0.; x.len()];
let mut ls = vec![0.; x.len()];
for ((i, r), l) in idcs.into_iter().zip(r_ascending).zip(l_ascending) {
rs[i] = r as f64;
ls[i] = l as f64;
}
let rsum = rs.windows(2)
.map(|win| (win[0]-win[1]).abs())
.sum::<f64>();
let n = x.len() as f64;
let lsum = ls.into_iter()
.map(|l| l*(n-l))
.sum::<f64>();
1.-n*rsum/(2.*lsum)
}
pub(super) fn argsort<T: Ord>(arr: &[T]) -> Vec<usize> {
let mut idcs: Vec<usize> = (0..arr.len()).collect();
idcs.sort_unstable_by_key(|&i| &arr[i]);
idcs
}
pub(super) fn permute<T: Copy>(arr: &[T], idcs: &[usize]) -> Vec<T> {
idcs.iter()
.map(|&i| arr[i])
.collect()
}
pub(super) fn cumulative_lte<T: PartialEq<T> + Copy>(arr: &[T]) -> Vec<usize> {
let mut counts: Vec<usize> = (1..=arr.len()).collect();
for i in (0..arr.len()-1).rev() {
if arr[i] == arr[i+1] { counts[i] = counts[i+1]; }
}
counts
}
pub(super) fn cumulative_gte<T: PartialEq<T> + Copy>(arr: &[T]) -> Vec<usize> {
let mut counts: Vec<usize> = (1..=arr.len()).rev().collect();
for i in 0..arr.len()-1 {
if arr[i+1] == arr[i] { counts[i+1] = counts[i]; }
}
counts
}