use std::ops::{Add,Sub};
use std::cmp::Ordering;
use anyhow::{Result,bail};
fn tofvec<T>(set:&[T]) -> Vec<f64> where T:Copy, f64:From<T> {
set.iter().map(|s| f64::from(*s)).collect()
}
pub fn mad<T>(s: &[T], m:f64) -> f64
where T: Copy,f64:From<T> {
s.iter().map(|&si| (f64::from(si) - m).abs()).sum()
}
pub fn naive_median<T>(set:&[T]) -> Result<f64>
where T: Copy,f64:From<T> {
let n = set.len();
if n == 0 { bail!("empty vector!"); };
let mut s = tofvec(set); Ok( if (n & 1) == 0 { even_naive_median( &mut s) }
else { odd_naive_median(&mut s) })
}
fn even_naive_median(s:&mut [f64]) -> f64 {
let mid = s.len()/2;
if mid == 1 { return (s[0]+s[1])/2.0; }; s.sort_unstable_by(|a, b| a.partial_cmp(b).unwrap());
(s[mid-1] + s[mid]) / 2.0
}
fn odd_naive_median(s:&mut [f64]) -> f64 {
let mid = s.len()/2;
if mid == 0 { return s[0]; }; s.sort_unstable_by(|a, b| a.partial_cmp(b).unwrap());
s[mid]
}
pub fn w_median<T>(set:&[T]) -> Result<f64>
where T: Copy,f64:From<T> {
let n = set.len();
if n == 0 { bail!("empty vector!"); };
let s = tofvec(set); let sumx:f64 = s.iter().sum();
let mean = sumx/(n as f64);
Ok( if (n & 1) == 0 { even_median(&s,mean) }
else { odd_median(&s,mean) })
}
fn next(s:&[f64],x:f64) -> (i64,f64) {
let mut recipsum = 0_f64;
let mut sigsum = 0_i64;
for &si in s {
let d = si-x;
if d.is_normal() {
if d > 0_f64 { recipsum += 1./d; sigsum += 1; }
else if d < 0_f64 { recipsum += 1./-d; sigsum -= 1; };
}
}
(sigsum,recipsum)
}
fn odd_median(s:&[f64],mean:f64) -> f64 {
let n = s.len();
if n == 1 { return s[0] };
let mut gm = mean;
loop {
let (sigs,recs) = next(s,gm);
if sigs.abs() < 3 {
break match sigs.cmp(&0_i64) {
Ordering::Greater => nearestgt(s, gm),
Ordering::Less => nearestlt(s, gm),
Ordering::Equal => gm
}
}
gm += (sigs as f64)/recs;
}
}
fn even_median(s:&[f64],mean:f64) -> f64 {
let n = s.len();
if n == 2 { return (s[0]+s[1])/2.0 };
let mut gm = mean;
loop {
let (sigs,recs) = next(s,gm);
gm += (sigs as f64)/recs;
if sigs.abs() < 2 {
let (lt,gt) = bracket(s, gm);
break (lt+gt)/2.0 };
}
}
pub fn i_median<T>(set:&[T]) -> Result<f64>
where T: PartialOrd+Copy+Sub<Output=T>+Add<Output=T>,f64:From<T> {
let n = set.len();
match n {
0 => bail!("empty vector!"),
1 => return Ok(f64::from(set[0])),
2 => return Ok(f64::from(set[0]+set[1])/2.0),
_ => {}
}
let mut x1 = set[0];
let mut x2 = x1;
set.iter().skip(1).for_each(|&s| {
if s < x1 { x1 = s }
else if s > x2 { x2 = s };
});
let hashf = (n-1) as f64 / f64::from(x2-x1);
let mut freqvec = vec![Vec::new();n];
for &si in set { freqvec[(f64::from(si-x1)*hashf).floor()as usize].push(si) }
let mut freqsum = 0_usize;
let mut res = 0_f64;
for v in freqvec {
freqsum += v.len();
if 2*freqsum > n {
let vlen = v.len();
let needed = ((n/2)as f64 - freqsum as f64 + vlen as f64).floor()as usize;
if vlen == 1 { res = f64::from(v[0]); break };
let mut midset = tofvec(&v);
midset.sort_unstable_by(|a, b| a.partial_cmp(b).unwrap());
if (n & 1) == 0 && needed > 0 { res = (midset[(needed as i64 -1)as usize]+midset[needed])/2.0; break };
res = midset[needed];
break }
};
Ok(res)
}
fn nearestlt(set:&[f64],x:f64) -> f64 {
let mut best = f64::MIN;
for &s in set {
if s >= x { continue };
if s > best { best = s };
}
best
}
fn nearestgt(set:&[f64],x:f64) -> f64 {
let mut best = f64::MAX;
for &s in set {
if s <= x { continue };
if s < best { best = s };
}
best
}
fn bracket(set:&[f64],x:f64) -> (f64,f64) {
let mut bestlt = f64::MIN;
let mut bestgt = f64::MAX;
for &s in set {
if s > x {
if s < bestgt { bestgt = s };
continue;
};
if s > bestlt && s<x { bestlt = s };
}
(bestlt,bestgt)
}