use indxvec::{here,Vecops};
pub fn naive_median<T>(s:&mut [T]) -> f64
where T: Copy+PartialOrd,f64:From<T> {
let n = s.len();
if n == 0 { panic!("{} empty vector!",here!()); };
if n == 1 { return f64::from(s[0]); };
if n == 2 { return (f64::from(s[0])+f64::from(s[1]))/2.0; };
s.sort_unstable_by(|a, b| a.partial_cmp(b).unwrap()); let mid = s.len()/2; if (n & 1) == 0 { (f64::from(s[mid-1]) + f64::from(s[mid])) / 2.0 } else { f64::from(s[mid]) } }
fn next(s:&[f64],x:f64) -> (i64,i64,f64) {
let mut recipsum = 0_f64;
let (mut left,mut right) = (0_i64,0_i64);
for &si in s {
if si < x { left += 1; recipsum += 1./(x-si); continue; };
if si > x { right += 1; recipsum += 1./(si-x);
}
}
let balance = right-left;
( balance.abs(),s.len() as i64-left-right,(balance as f64)/recipsum )
}
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
}
pub fn w_median<T>(set:&[T]) -> f64
where T: Copy,f64:From<T> {
let n = set.len();
match n {
1 => f64::from(set[0]),
2 => f64::from(set[0])+f64::from(set[1])/2.0,
_ => {
let s = set.tof64(); let sumx:f64 = s.iter().sum();
let mean = sumx/(n as f64);
if (n & 1) == 0 { even_w_median(&s,mean) }
else { odd_w_median(&s,mean) }}
}
}
fn odd_w_median(s:&[f64],m:f64) -> f64 {
let mut gm = m;
let mut lastsig = 0_i64;
loop {
let (sigs,eqs,dx) = next(s,gm);
if sigs < eqs { return gm };
gm += dx; if (sigs < lastsig) && (sigs >= 3) { lastsig = sigs;
continue;
};
if dx > 0. { gm = nearestgt(s, gm); }
else if dx < 0. { gm = nearestlt(s, gm); };
if sigs < 3 { return gm; }; lastsig = sigs; }
}
fn even_w_median(s:&[f64],m:f64) -> f64 {
let mut gm = m;
let mut lastsig = 0_i64;
loop {
let (sigs,eqs,dx) = next(s,gm);
if sigs < eqs { return gm };
gm += dx; if (sigs < lastsig) && (sigs >= 2) { lastsig = sigs;
continue;
};
if sigs < 2 { return (nearestgt(s, gm) + nearestlt(s, gm))/2.; }; lastsig = sigs; if dx > 0. { gm = nearestgt(s, gm); }
else if dx < 0. { gm = nearestlt(s, gm); };
}
}
fn part<T>(s:&[T],pivot:f64) -> (Vec<T>,Vec<T>) where T:Copy, f64:From<T> {
let mut ltset = Vec::new();
let mut gtset = Vec::new();
for &f in s {
if f64::from(f) < pivot { ltset.push(f); } else { gtset.push(f); };
};
(ltset,gtset)
}
pub fn r_median<T>(set:&[T]) -> f64
where T: Copy+PartialOrd,f64:From<T> {
let n = set.len();
let (min,max) = set.minmaxt();
let pivot = (f64::from(min)+f64::from(max))/2.;
if (n & 1) == 0 { r_med_even(set,n/2,pivot,f64::from(min),f64::from(max)) }
else { r_med_odd(set,n/2+1,pivot,f64::from(min),f64::from(max)) }
}
fn r_med_odd<T>(set:&[T],need:usize,pivot:f64,setmin:f64,setmax:f64) -> f64
where T:PartialOrd+Copy,f64:From<T> {
if need == 1 { return setmin };
let n = set.len();
if need == n { return setmax };
let (ltset,gtset) = part(set,pivot);
let ltlen = ltset.len();
let gtlen = gtset.len();
match need {
1 => f64::from(ltset.mint()),
x if x < ltlen => {
let max = f64::from(ltset.maxt());
if setmin == max { return f64::from(ltset[0]) }; let newpivot = setmin + (need as f64)*(max-setmin)/(ltlen as f64);
r_med_odd(<set, need, newpivot,setmin,max)
},
x if x == ltlen => f64::from(ltset.maxt()),
x if x == ltlen+1 => f64::from(gtset.mint()),
x if x == n => f64::from(gtset.maxt()),
_ => { let newneed = need - ltlen;
let min = f64::from(gtset.mint());
if min == setmax { return f64::from(gtset[0]) }; let newpivot = min + (setmax-min)*(newneed as f64)/(gtlen as f64);
r_med_odd(>set, newneed, newpivot,min,setmax)
}
}
}
fn r_med_even<T>(set:&[T],need:usize,pivot:f64,setmin:f64,setmax:f64) -> f64
where T:PartialOrd+Copy,f64:From<T> {
let n = set.len();
let (ltset,gtset) = part(set,pivot);
let ltlen = ltset.len();
let gtlen = gtset.len();
match need {
x if x < ltlen => {
let max = f64::from(ltset.maxt());
if setmin == max { return f64::from(ltset[0]) }; let newpivot = setmin + (need as f64)*(max-setmin)/(ltlen as f64);
r_med_even(<set, need, newpivot,setmin,max)
},
x if x == ltlen => (f64::from(ltset.maxt())+f64::from(gtset.mint()))/2., x if x == n => f64::from(gtset.maxt()),
_ => { let newneed = need - ltlen;
let min = f64::from(gtset.mint());
if min == setmax { return f64::from(gtset[0]) }; let newpivot = min + (newneed as f64)*(setmax-min)/(gtlen as f64);
r_med_even(>set, newneed, newpivot,min,setmax)
}
}
}