From 1084b743554b215aa3c6ccb8976b4068d4cfeeaf Mon Sep 17 00:00:00 2001 From: L <457124+liborty@users.noreply.github.com> Date: Thu, 7 Mar 2024 00:23:45 +1000 Subject: [PATCH] 3.0.8 --- Cargo.toml | 2 +- README.md | 20 +--- src/algos.rs | 24 ++++- src/error.rs | 40 ------- src/lib.rs | 284 +++++-------------------------------------------- tests/tests.rs | 130 +++++++++++++--------- 6 files changed, 128 insertions(+), 372 deletions(-) delete mode 100644 src/error.rs diff --git a/Cargo.toml b/Cargo.toml index eb6e7ce..495b51e 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "medians" -version = "3.0.7" +version = "3.0.8" authors = ["Libor Spacek"] edition = "2021" description = "Median, Statistical Measures, Mathematics, Statistics" diff --git a/README.md b/README.md index 729cd57..a9ec6d0 100644 --- a/README.md +++ b/README.md @@ -5,7 +5,7 @@ Fast algorithm for finding medians of one dimensional data, implemented in 100% safe Rust. ```rust -use medians::{medianu8,Medians,Medianf64,Median}; +use medians::{*,algos::*}; ``` ## Introduction @@ -111,6 +111,8 @@ pub trait Median<'a, T> { ## Release Notes +**Version 3.0.8** - Added `implementation.rs` module and reorganized the source. + **Version 3.0.7** - Added `medf_weighted`, applying `&[f64]` weights. **Version 3.0.6** - Moved `part`, `ref_vec` and `deref_vec` into crate `Indxvec`, to allow their wider use. @@ -126,19 +128,3 @@ pub trait Median<'a, T> { **Version 3.0.1** - Renamed `correlation` to `med_correlation` to avoid name clashes elsewhere. **Version 3.0.0** - Numerous improvements to speed and generality and renaming. - -**Version 2.3.1** - Further speed optimisation of `partf64`. - -**Version 2.3.0** - Some minor changes to `algosf64.rs`. Improvements to this manual. - -**Version 2.2.6** - Improved `README.md`. No changes to the code. - -**Version 2.2.5** - Upped dependency on `indxvec` to version 1.8. - -**Version 2.2.3** - Slight further improvement to efficiency of `part`. - -**Version 2.2.2** - Corrected some comment and readme typos. No change in functionality. - -**Version 2.2.1** - Some code pruning and streamlining. No change in functionality. - -**Version 2.2.0** - Major new version with much improved speed and generality and some breaking changes (renaming). diff --git a/src/algos.rs b/src/algos.rs index ce6509d..640323c 100644 --- a/src/algos.rs +++ b/src/algos.rs @@ -1,6 +1,7 @@ use core::cmp::{Ordering, Ordering::*}; use std::ops::Range; use indxvec::Mutops; +use crate::{Me,merror}; /// Scan a slice of f64s for NANs pub fn nans(v: &[f64]) -> bool { @@ -111,7 +112,7 @@ pub fn qbalance(s: &[T], centre: &f64, q: impl Fn(&T) -> f64) -> i64 { } /// Odd median of `&[u8]` -pub fn oddmedianu8(s: &[u8]) -> f64 { +fn oddmedianu8(s: &[u8]) -> f64 { let need = s.len() / 2; // median target position let mut histogram = [0_usize; 256]; let mut cummulator = 0_usize; @@ -133,7 +134,7 @@ pub fn oddmedianu8(s: &[u8]) -> f64 { } /// Even median of `&[u8]` -pub fn evenmedianu8(s: &[u8]) -> f64 { +fn evenmedianu8(s: &[u8]) -> f64 { let need = s.len() / 2; // first median target position let mut histogram = [0_usize; 256]; let mut cummulator = 0_usize; @@ -163,9 +164,24 @@ pub fn evenmedianu8(s: &[u8]) -> f64 { res } +/// Median of primitive type u8 by fast radix search +pub fn medianu8(s:&[u8]) -> Result { + let n = s.len(); + match n { + 0 => return merror("size", "median: zero length data")?, + 1 => return Ok(s[0] as f64), + 2 => return Ok((s[0] as f64 + s[1] as f64) / 2.0), + _ => (), + }; + if (n & 1) == 1 { + Ok(oddmedianu8(s)) + } else { + Ok(evenmedianu8(s)) + } +} /// Median of odd sized generic data with Odering comparisons by custom closure -pub fn oddmedian_by<'a, T>(s: &mut [&'a T], c: &mut impl FnMut(&T, &T) -> Ordering) -> &'a T { +pub(super) fn oddmedian_by<'a, T>(s: &mut [&'a T], c: &mut impl FnMut(&T, &T) -> Ordering) -> &'a T { let mut rng = 0..s.len(); let need = s.len() / 2; // median target position in fully partitioned set loop { @@ -214,7 +230,7 @@ pub fn oddmedian_by<'a, T>(s: &mut [&'a T], c: &mut impl FnMut(&T, &T) -> Orderi } /// Median of even sized generic data with Odering comparisons by custom closure -pub fn evenmedian_by<'a, T>( +pub(super) fn evenmedian_by<'a, T>( s: &mut [&'a T], c: &mut impl FnMut(&T, &T) -> Ordering, ) -> (&'a T, &'a T) { diff --git a/src/error.rs b/src/error.rs deleted file mode 100644 index c91843e..0000000 --- a/src/error.rs +++ /dev/null @@ -1,40 +0,0 @@ -use std::error::Error; -use std::fmt; -use std::fmt::{Debug, Display}; - -#[derive(Debug)] -/// custom error -pub enum MedError { - /// Non positive data dimension - Size(T), - /// NaN float NaN encountered - Nan(T), - /// Other error converted to RanError - Other(T), -} - -impl Error for MedError where T: Sized + Debug + Display {} - -impl Display for MedError -where - T: Sized + Debug + Display, -{ - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - match self { - MedError::Size(s) => write!(f, "Size of data must be positive: {s}"), - MedError::Nan(s) => write!(f, "Floats must not include NaNs: {s}"), - MedError::Other(s) => write!(f, "Converted from: {s}"), - } - } -} - -/// Convenience function for building MedError -/// from error kind name and payload message, which can be either &str or String -pub fn merror(kind: &str, msg: impl Into) -> Result> { - match kind { - "size" => Err(MedError::Size(msg.into())), - "nan" => Err(MedError::Nan(msg.into())), - "other" => Err(MedError::Other(msg.into())), - _ => Err(MedError::Other("Wrong error kind given to merror".into())) - } -} diff --git a/src/lib.rs b/src/lib.rs index eb32a7f..d048ef1 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -7,19 +7,38 @@ /// Functions for finding medians pub mod algos; +/// Methods that implement Display and traits +pub mod implementations; -/// Custom errors -pub mod error; - -use crate::algos::*; -pub use crate::error::{merror, MedError}; - -use core::{cmp::Ordering, fmt::Display}; -use indxvec::{Vecops,printing::{GR, UN, YL}}; +use core::cmp::Ordering; +use core::fmt::Debug; /// Shorthand type for medians errors with message payload specialized to String pub type Me = MedError; +#[derive(Debug)] +/// custom error +pub enum MedError { + /// Non positive data dimension + Size(T), + /// NaN float NaN encountered + Nan(T), + /// Other error converted to RanError + Other(T), +} + +/// Convenience function for building MedError +/// from error kind name and payload message, which can be either &str or String +pub fn merror(kind: &str, msg: impl Into) -> Result> { + match kind { + "size" => Err(MedError::Size(msg.into())), + "nan" => Err(MedError::Nan(msg.into())), + "other" => Err(MedError::Other(msg.into())), + _ => Err(MedError::Other("Wrong error kind given to merror".into())) + } +} + + /// Enum for results of odd/even medians pub enum Medians<'a, T> { /// Odd sized data results in a single median @@ -27,37 +46,6 @@ pub enum Medians<'a, T> { /// Even sized data results in a pair of (centered) medians Even((&'a T, &'a T)), } -impl std::fmt::Display for Medians<'_, T> -where - T: Display, -{ - fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { - match self { - Medians::Odd(m) => { - write!(f, "{YL}odd median: {GR}{}{UN}", *m) - } - Medians::Even((m1, m2)) => { - write!(f, "{YL}even medians: {GR}{} {}{UN}", *m1, *m2) - } - } - } -} - -/// Median of primitive type u8 by fast radix search -pub fn medianu8(s:&[u8]) -> Result { - let n = s.len(); - match n { - 0 => return merror("size", "median: zero length data")?, - 1 => return Ok(s[0] as f64), - 2 => return Ok((s[0] as f64 + s[1] as f64) / 2.0), - _ => (), - }; - if (n & 1) == 1 { - Ok(oddmedianu8(s)) - } else { - Ok(evenmedianu8(s)) - } -} /// Fast 1D medians of floating point data, plus related methods pub trait Medianf64 { @@ -97,221 +85,3 @@ pub trait Median<'a, T> { /// Median of absolute differences (MAD). fn mad(self, centre: f64, quantify: impl Fn(&T) -> f64) -> f64; } - -/// Medians of &mut [&f64]. -impl Medianf64 for &[f64] { - /// Returns `nan` error when any data item is a NaN, otherwise the median - fn medf_checked(self) -> Result { - let n = self.len(); - match n { - 0 => return merror("size", "medf_checked: zero length data"), - 1 => return Ok(self[0]), - 2 => return Ok((self[0] + self[1]) / 2.0), - _ => (), - }; - let mut s = self - .iter() - .map(|x| { - if x.is_nan() { - merror("Nan", "medf_checked: Nan in input!") - } else { - Ok(x) - } - }) - .collect::, Me>>()?; - if (n & 1) == 1 { - let oddm = oddmedian_by(&mut s, &mut ::total_cmp); - Ok(*oddm) - } else { - let (&med1, &med2) = evenmedian_by(&mut s, &mut ::total_cmp); - Ok((med1+med2) / 2.0) - } - } - /// Use this when your data does not contain any NaNs. - /// NaNs will not raise an error. However, they will affect the result - /// because of their order positions beyond infinity. - fn medf_unchecked(self) -> f64 { - let n = self.len(); - match n { - 0 => return 0_f64, - 1 => return self[0], - 2 => return (self[0] + self[1]) / 2.0, - _ => (), - }; - let mut s = self.ref_vec(0..self.len()); - if (n & 1) == 1 { - let oddm = oddmedian_by(&mut s, &mut ::total_cmp); - *oddm - } else { - let (&med1, &med2) = evenmedian_by(&mut s, &mut ::total_cmp); - (med1 + med2) / 2.0 - } - } - /// Iterative weighted median with accuracy eps - fn medf_weighted(self, ws: Self, eps: f64) -> Result { - if self.len() != ws.len() { - return merror("size","medf_weighted - data and weights lengths mismatch"); }; - if nans(self) { - return merror("Nan","medf_weighted - detected Nan in input"); }; - let weights_sum: f64 = ws.iter().sum(); - let mut last_median = 0_f64; - for (g,w) in self.iter().zip(ws) { last_median += w*g; }; - last_median /= weights_sum; // start iterating from the weighted centre - let mut last_recsum = 0f64; - loop { // iteration till accuracy eps is exceeded - let mut median = 0_f64; - let mut recsum = 0_f64; - for (x,w) in self.iter().zip(ws) { - let mag = (x-last_median).abs(); - if mag.is_normal() { // only use this point if its distance from median is > 0.0 - let rec = w/(mag.sqrt()); // weight/distance - median += rec*x; - recsum += rec // add separately the reciprocals for final scaling - } - } - if recsum-last_recsum < eps { return Ok(median/recsum); }; // termination test - last_median = median/recsum; - last_recsum = recsum; - } - } - /// Zero mean/median data produced by subtracting the centre, - /// typically the mean or the median. - fn medf_zeroed(self, centre: f64) -> Vec { - self.iter().map(|&s| s - centre).collect() - } - /// Median correlation = cosine of an angle between two zero median vectors, - /// (where the two data samples are interpreted as n-dimensional vectors). - fn medf_correlation(self, v: Self) -> Result { - let mut sx2 = 0_f64; - let mut sy2 = 0_f64; - let smedian = self.medf_checked()?; - let vmedian = v.medf_checked()?; - let sxy: f64 = self - .iter() - .zip(v) - .map(|(&xt, &yt)| { - let x = xt - smedian; - let y = yt - vmedian; - sx2 += x * x; - sy2 += y * y; - x * y - }) - .sum(); - let res = sxy / (sx2 * sy2).sqrt(); - if res.is_nan() { - merror("Nan", "medf_correlation: Nan result!") - } else { - Ok(res) - } - } - /// Data dispersion estimator MAD (Median of Absolute Differences). - /// MAD is more stable than standard deviation and more general than quartiles. - /// When argument `centre` is the median, it is the most stable measure of data dispersion. - fn madf(self, centre: f64) -> f64 { - self.iter() - .map(|&s| (s - centre).abs()) - .collect::>() - .medf_unchecked() - } -} - -/// Medians of &[T] -impl<'a, T> Median<'a, T> for &'a [T] { - /// Median of `&[T]` by comparison `c`, quantified to a single f64 by `q`. - /// When T is a primitive type directly convertible to f64, pass in `as f64` for `q`. - /// When f64:From is implemented, pass in `|x| x.into()` for `q`. - /// When T is Ord, use `|a,b| a.cmp(b)` as the comparator closure. - /// In all other cases, use custom closures `c` and `q`. - /// When T is not quantifiable at all, use `median_by` method. - fn qmedian_by( - self, - c: &mut impl FnMut(&T, &T) -> Ordering, - q: impl Fn(&T) -> f64, - ) -> Result { - let n = self.len(); - match n { - 0 => return merror("size", "qmedian_by: zero length data"), - 1 => return Ok(q(&self[0])), - 2 => return Ok((q(&self[0]) + q(&self[1])) / 2.0), - _ => (), - }; - let mut s = self.ref_vec(0..self.len()); - if (n & 1) == 1 { - Ok(q(oddmedian_by(&mut s, c))) - } else { - let (med1, med2) = evenmedian_by(&mut s, c); - Ok((q(med1) + q(med2)) / 2.0) - } - } - - /// Median(s) of unquantifiable type by general comparison closure - fn median_by(self, c: &mut impl FnMut(&T, &T) -> Ordering) -> Result, Me> { - let n = self.len(); - match n { - 0 => return merror("size", "median_ord: zero length data"), - 1 => return Ok(Medians::Odd(&self[0])), - 2 => return Ok(Medians::Even((&self[0], &self[1]))), - _ => (), - }; - let mut s = self.ref_vec(0..self.len()); - if (n & 1) == 1 { - Ok(Medians::Odd(oddmedian_by(&mut s, c))) - } else { - Ok(Medians::Even(evenmedian_by(&mut s, c))) - } - } - - /// Zero mean/median data produced by subtracting the centre - fn zeroed(self, centre: f64, q: impl Fn(&T) -> f64) -> Result, Me> { - Ok(self.iter().map(|s| q(s) - centre).collect()) - } - /// We define median based correlation as cosine of an angle between two - /// zero median vectors (analogously to Pearson's zero mean vectors) - /// # Example - /// ``` - /// use medians::{Medianf64,Median}; - /// use core::convert::identity; - /// use core::cmp::Ordering::*; - /// let v1 = vec![1_f64,2.,3.,4.,5.,6.,7.,8.,9.,10.,11.,12.,13.,14.]; - /// let v2 = vec![14_f64,1.,13.,2.,12.,3.,11.,4.,10.,5.,9.,6.,8.,7.]; - /// assert_eq!(v1.medf_correlation(&v2).unwrap(),-0.1076923076923077); - /// assert_eq!(v1.med_correlation(&v2,&mut |a,b| a.total_cmp(b),|&a| identity(a)).unwrap(),-0.1076923076923077); - /// ``` - fn med_correlation( - self, - v: Self, - c: &mut impl FnMut(&T, &T) -> Ordering, - q: impl Fn(&T) -> f64, - ) -> Result { - let mut sx2 = 0_f64; - let mut sy2 = 0_f64; - let smedian = self.qmedian_by(c, &q)?; - let vmedian = v.qmedian_by(c, &q)?; - let sxy: f64 = self - .iter() - .zip(v) - .map(|(xt, yt)| { - let x = q(xt) - smedian; - let y = q(yt) - vmedian; - sx2 += x * x; - sy2 += y * y; - x * y - }) - .sum(); - let res = sxy / (sx2 * sy2).sqrt(); - if res.is_nan() { - merror("Nan", "correlation: Nan result!") - } else { - Ok(res) - } - } - /// Data dispersion estimator MAD (Median of Absolute Differences). - /// MAD is more stable than standard deviation and more general than quartiles. - /// When argument `centre` is the median, it is the most stable measure of data dispersion. - fn mad(self, centre: f64, q: impl Fn(&T) -> f64) -> f64 { - self.iter() - .map(|s| (q(s) - centre).abs()) - .collect::>() - .medf_unchecked() - } -} diff --git a/tests/tests.rs b/tests/tests.rs index 7d8ec84..5955d07 100644 --- a/tests/tests.rs +++ b/tests/tests.rs @@ -2,20 +2,37 @@ #![allow(dead_code)] #[cfg(test)] use indxvec::{here, printing::*, Indices, Mutops, Printing, Vecops}; -use medians::algos::{qbalance, min, min2,best1_k}; -// use medians::algosf64::partord; -use medians::{Me, merror, medianu8, Median, Medianf64}; -use ran:: *; -// use std::io::{stdout,Write}; +use medians::{*,algos::*}; +use ran::*; +use core::cmp::{Ordering, Ordering::*}; use std::convert::From; use std::error::Error; -use core::cmp::{Ordering,Ordering::*}; use times::{benchf64, benchu64, benchu8, mutbenchf64}; #[test] fn parting() -> Result<(), Me> { let data = [ - 5., 8., 7., 6., 5., 4., 3., 2., -f64::NAN, 1., 0., 1., -2., 3., 4., -5., f64::NAN, f64::NAN, 6., 7., 7., + 5., + 8., + 7., + 6., + 5., + 4., + 3., + 2., + -f64::NAN, + 1., + 0., + 1., + -2., + 3., + 4., + -5., + f64::NAN, + f64::NAN, + 6., + 7., + 7., ]; // println!("To u64s: {}",to_u64s(&data).gr()); // println!("To f64s: {}",to_f64s(&to_u64s(&data)).gr()); @@ -23,26 +40,22 @@ fn parting() -> Result<(), Me> { let len = data.len(); println!("Pivot {}: {}", data[0].yl(), data.gr()); let mut refdata = data.ref_vec(0..len); - let (eqsub, gtsub) = <&mut [f64]>::part( - &mut refdata, - &(0..len), - &mut ::total_cmp, - ); + let (eqsub, gtsub) = <&mut [f64]>::part(&mut refdata, &(0..len), &mut ::total_cmp); println!( "Result: {}\nCommas show the subranges:\n\ {GR}[{}, {}, {}]{UN}\n{} items equal to pivot {}", - (eqsub,gtsub).yl(), + (eqsub, gtsub).yl(), refdata[0..eqsub].to_plainstr(), refdata[eqsub..gtsub].to_plainstr(), refdata[gtsub..len].to_plainstr(), (gtsub - eqsub).yl(), data[0].yl() ); - let refindex = data.isort_refs(0..len, |a,b| a.total_cmp(b)); - println!("isort_refs ascending sorted:\n{}",&refindex.gr()); - let indx = data.isort_indexed(0..len, |a,b| b.total_cmp(a)); - println!("isort_index (descending):\n{}",indx.gr()); - println!("Unindexed:\n{}",indx.unindex(&data,true).gr()); + let refindex = data.isort_refs(0..len, |a, b| a.total_cmp(b)); + println!("isort_refs ascending sorted:\n{}", &refindex.gr()); + let indx = data.isort_indexed(0..len, |a, b| b.total_cmp(a)); + println!("isort_index (descending):\n{}", indx.gr()); + println!("Unindexed:\n{}", indx.unindex(&data, true).gr()); Ok(()) } @@ -59,36 +72,48 @@ fn text() { "Hash sorted by word lengths: {}", v.sorth(|s| s.len() as f64, true).gr() ); - println!("Median word(s) by length: {GR}{}{UN}", - (&v[..]).median_by(&mut |a,b| a.len().cmp(&b.len())) - .expect("text(): median_by length failed\n") ); + println!( + "Median word(s) by length: {GR}{}{UN}", + (&v[..]) + .median_by(&mut |a, b| a.len().cmp(&b.len())) + .expect("text(): median_by length failed\n") + ); println!("Sorted by lexicon: {}", v.sortm(true).gr()); - println!("Median word(s) by lexicon: {GR}{}{UN}", - (&v[..]).median_by(&mut <&str>::cmp) - .expect("text(): median_by lexicon failed\n") ); + println!( + "Median word(s) by lexicon: {GR}{}{UN}", + (&v[..]) + .median_by(&mut <&str>::cmp) + .expect("text(): median_by lexicon failed\n") + ); } #[test] fn medf64() -> Result<(), Me> { let v = [ - 9., 10., 18., 17., 16., 15., 14., 1., 2., 3., 4., 5., 6., 7., 8., 17., 10., 11., 12., 13., 14., 15., 16., 18., 9. + 9., 10., 18., 17., 16., 15., 14., 1., 2., 3., 4., 5., 6., 7., 8., 17., 10., 11., 12., 13., + 14., 15., 16., 18., 9., + ]; + let weights = [ + 1., 2., 3., 4., 5., 6., 7., 8., 9., 10., 11., 12., 13., 14., 15., 16., 17., 18., 19., 20., + 21., 22., 23., 24., 25., ]; - let weights = [ 1.,2.,3.,4.,5.,6.,7.,8.,9.,10.,11.,12.,13.,14.,15.,16.,17.,18.,19.,20.,21.,22.,23.,24.,25.]; - println!("Data: {}",v.gr()); - println!("Weights: {}",weights.gr()); + println!("Data: {}", v.gr()); + println!("Weights: {}", weights.gr()); let len = v.len(); let mut vr = v.ref_vec(0..len); - println!("max: {}",min(&vr,0..len,&mut |a:&f64,b:&f64| b.total_cmp(a)).gr()); - println!("max2: {}",min2(&vr,0..len,&mut |a:&f64,b:&f64| b.total_cmp(a)).gr()); - let (eqsub,gtsub) = <&mut [f64]>::part( - &mut vr, - &(0..v.len()), - &mut ::total_cmp, + println!( + "max: {}", + min(&vr, 0..len, &mut |a: &f64, b: &f64| b.total_cmp(a)).gr() + ); + println!( + "max2: {}", + min2(&vr, 0..len, &mut |a: &f64, b: &f64| b.total_cmp(a)).gr() ); + let (eqsub, gtsub) = <&mut [f64]>::part(&mut vr, &(0..v.len()), &mut ::total_cmp); println!( "Result: {}\nCommas separate the subranges:\n\ {GR}[{}, {}, {}]{UN}\n{} items equal to the pivot {}", - (eqsub,gtsub).yl(), + (eqsub, gtsub).yl(), vr[0..eqsub].to_plainstr(), vr[eqsub..gtsub].to_plainstr(), vr[gtsub..len].to_plainstr(), @@ -98,7 +123,10 @@ fn medf64() -> Result<(), Me> { let median = v.medf_checked()?; let mad = v.madf(median); println!("Median±mad: {GR}{}±{}{UN}", median, mad); - println!("Weighted median: {GR}{}{UN} ", v.medf_weighted(&weights,0.0001)?); + println!( + "Weighted median: {GR}{}{UN} ", + v.medf_weighted(&weights, 0.0001)? + ); Ok(()) } @@ -106,30 +134,32 @@ fn medf64() -> Result<(), Me> { fn correlation() -> Result<(), Me> { let v1 = ranv_f64(100).expect("Random vec1 generation failed"); // random vector let v2 = ranv_f64(100).expect("Random vec2 generation failed"); // random vector - println!("medf_correlation: {}",v1.medf_correlation(&v2)?.gr()); + println!("medf_correlation: {}", v1.medf_correlation(&v2)?.gr()); Ok(()) } #[test] fn errors() -> Result<(), Me> { let n = 10_usize; // number of vectors to test for each magnitude - // set_seeds(33333); + // set_seeds(33333); for d in [10, 50, 100, 1000, 10000, 100000] { let mut error = 0_i64; trait Eq: PartialEq {} impl Eq for f64 {} for _ in 0..n { let Ok(v) = ranv_u8(d) else { - return merror("other","Random vec genertion failed"); }; + return merror("other", "Random vec genertion failed"); + }; let med = medianu8(&v)?; // random vector - // v.as_slice().medf_unchecked(); - error += qbalance(&v, &med, |&f| f as f64); + // v.as_slice().medf_unchecked(); + error += qbalance(&v, &med, |&f| f as f64); } println!("Even length {GR}{d}{UN}, repeats: {GR}{n}{UN}, errors: {GR}{error}{UN}"); error = 0_i64; for _ in 0..n { let Ok(v) = ranv_u8(d + 1) else { - return merror("other","Random vec genertion failed"); }; // random vector + return merror("other", "Random vec genertion failed"); + }; // random vector let med = medianu8(&v)?; // v // .as_slice() @@ -144,12 +174,7 @@ fn errors() -> Result<(), Me> { Ok(()) } -const NAMES: [&str; 3] = [ - "median_by", - "best_k", - "medf_unchecked", - -]; +const NAMES: [&str; 3] = ["median_by", "best_k", "medf_unchecked"]; const CLOSURESF64: [fn(&[f64]); 3] = [ |v: &[_]| { @@ -157,15 +182,14 @@ const CLOSURESF64: [fn(&[f64]); 3] = [ .expect("even median closure failed"); }, |v: &[_]| { - let mut sorted:Vec<&f64> = v.iter().collect(); - sorted.sort_unstable_by(|&a,&b| a.total_cmp(b)); - // sorted[sorted.len()/2]; + let mut sorted: Vec<&f64> = v.iter().collect(); + sorted.sort_unstable_by(|&a, &b| a.total_cmp(b)); + // sorted[sorted.len()/2]; }, |v: &[_]| { v.medf_unchecked(); }, - - /* + /* |v: &[_]| { v.qmedian_by(&mut ::total_cmp,|&x| x) .expect("even median closure failed");