#![cfg_attr(not(test), no_std)]
#![forbid(unsafe_code)]
#![deny(missing_docs)]
use core::cmp::Ordering;
use micromath::F32Ext;
type Word = u8;
type Rational = f32;
pub fn mean(slice: &[Word]) -> Option<Rational> {
if slice.is_empty() {
return None;
}
let avg = slice
.iter()
.fold(0.0 as Rational, |acc, &elm| acc + (elm as Rational))
/ (slice.len() as Rational);
Some(avg)
}
pub fn next_min(slice: &[Word], prev: Option<&Word>) -> Option<(usize, usize)> {
if slice.is_empty() {
return None;
}
let mut v_in = Word::max_value();
let mut v_ind = 0usize;
let mut c = 0usize;
if let Some(l_ex) = prev {
let mut v_found = false;
for (i, e) in slice.iter().enumerate() {
match (e.cmp(l_ex), e.cmp(&v_in)) {
(Ordering::Greater, Ordering::Less) => {
v_in = *e;
v_ind = i;
c = 1usize;
v_found = true;
}
(_, Ordering::Equal) => {
c += 1usize;
if !v_found {
v_in = *e;
v_ind = i;
v_found = true;
}
}
_ => {}
}
}
if l_ex == &v_in {
c = 0usize;
}
} else {
for (i, e) in slice.iter().enumerate() {
match e.cmp(&v_in) {
Ordering::Less => {
v_in = *e;
v_ind = i;
c = 1usize;
}
Ordering::Equal => {
c += 1usize;
}
_ => {}
}
}
}
if c > 0 {
Some((v_ind, c))
} else {
None
}
}
pub fn kth_ind(slice: &[Word], k: usize) -> Option<usize> {
if k >= slice.len() {
return None;
}
let mut items = 0usize;
let mut v_ind = Option::<usize>::None;
while items <= k {
let prev = {
if let Some(v_i) = v_ind {
Some(&slice[v_i])
} else {
None
}
};
if let Some((ind, count)) = next_min(slice, prev) {
items += count;
v_ind = Some(ind);
}
}
v_ind
}
pub fn median(slice: &[Word]) -> Option<Rational> {
if slice.is_empty() {
return None;
}
let k = slice.len() / 2;
let mut items = 0usize;
let mut v_items = 0usize;
let mut v_ind = Option::<usize>::None;
let mut p_ind = Option::<usize>::None;
while items <= k {
p_ind = v_ind;
let prev = {
if let Some(v_i) = v_ind {
Some(&slice[v_i])
} else {
None
}
};
if let Some((ind, count)) = next_min(slice, prev) {
v_items = count;
items += count;
v_ind = Some(ind);
}
}
let mut med = slice[v_ind.unwrap()] as Rational;
if (slice.len() % 2) == 0 {
let p_total_items = items - v_items;
let k_l = k - 1;
if p_total_items > k_l {
let k_l_v = slice[p_ind.unwrap()];
med += k_l_v as Rational;
med /= 2_f32;
} else {
}
}
Some(med)
}
pub fn std_dev_pop(avg: &Rational, slice: &[Word]) -> Option<Rational> {
if slice.is_empty() {
return None;
}
let sq_sum = slice.iter().fold(0.0 as Rational, |acc, &elm| {
let e = elm as Rational;
let delta = e - avg;
acc + (delta * delta)
});
let norm_sq_sum = sq_sum / (slice.len() as Rational);
Some(F32Ext::sqrt(norm_sq_sum))
}
pub fn pearson_skew_median(slice: &[Word]) -> Option<Rational> {
let avg = mean(slice)?;
let med = median(slice)?;
let std = std_dev_pop(&avg, slice)?;
Some((3.0 * (avg - med)) / std)
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn next_min_check() {
let test_vec = [
[0, 0, 0, 0, 0, 0, 0, 0],
[0, 1, 0, 1, 0, 0, 1, 1],
[8, 1, 4, 5, 6, 3, 2, 7],
[7, 4, 6, 7, 2, 3, 2, 2],
];
{
assert_eq!(next_min(&test_vec[0], None), Some((0, 8)));
assert_eq!(next_min(&test_vec[0], Some(&0)), None);
assert_eq!(next_min(&test_vec[0], Some(&1)), None);
}
{
assert_eq!(next_min(&test_vec[1], None), Some((0, 4)));
assert_eq!(next_min(&test_vec[1], Some(&0)), Some((1, 4)));
assert_eq!(next_min(&test_vec[1], Some(&1)), None);
assert_eq!(next_min(&test_vec[1], Some(&2)), None);
}
{
assert_eq!(next_min(&test_vec[2], None), Some((1, 1)));
assert_eq!(next_min(&test_vec[2], Some(&0)), Some((1, 1)));
assert_eq!(next_min(&test_vec[2], Some(&1)), Some((6, 1)));
assert_eq!(next_min(&test_vec[2], Some(&2)), Some((5, 1)));
assert_eq!(next_min(&test_vec[2], Some(&3)), Some((2, 1)));
assert_eq!(next_min(&test_vec[2], Some(&4)), Some((3, 1)));
assert_eq!(next_min(&test_vec[2], Some(&5)), Some((4, 1)));
assert_eq!(next_min(&test_vec[2], Some(&6)), Some((7, 1)));
assert_eq!(next_min(&test_vec[2], Some(&7)), Some((0, 1)));
assert_eq!(next_min(&test_vec[2], Some(&8)), None);
assert_eq!(next_min(&test_vec[2], Some(&9)), None);
}
{
assert_eq!(next_min(&test_vec[3], None), Some((4, 3)));
assert_eq!(next_min(&test_vec[3], Some(&0)), Some((4, 3)));
assert_eq!(next_min(&test_vec[3], Some(&1)), Some((4, 3)));
assert_eq!(next_min(&test_vec[3], Some(&2)), Some((5, 1)));
assert_eq!(next_min(&test_vec[3], Some(&3)), Some((1, 1)));
assert_eq!(next_min(&test_vec[3], Some(&4)), Some((2, 1)));
assert_eq!(next_min(&test_vec[3], Some(&5)), Some((2, 1)));
assert_eq!(next_min(&test_vec[3], Some(&6)), Some((0, 2)));
assert_eq!(next_min(&test_vec[3], Some(&7)), None);
assert_eq!(next_min(&test_vec[3], Some(&8)), None);
}
}
#[test]
fn kth_ind_check() {
let test_vec = [
[0, 0, 0, 0, 0, 0, 0, 0],
[0, 1, 0, 1, 0, 0, 1, 1],
[8, 1, 4, 5, 6, 3, 2, 7],
[7, 4, 6, 7, 2, 3, 2, 2],
];
for k in 0..test_vec[0].len() {
let ind = kth_ind(&test_vec[0], k).unwrap();
assert_eq!(ind, 0);
}
for k in 0..test_vec[1].len() {
let ind = kth_ind(&test_vec[1], k).unwrap();
dbg!(k, ind);
if k < 4 {
assert_eq!(ind, 0);
} else {
assert_eq!(ind, 1);
}
}
{
assert_eq!(kth_ind(&test_vec[2], 0), Some(1));
assert_eq!(kth_ind(&test_vec[2], 1), Some(6));
assert_eq!(kth_ind(&test_vec[2], 2), Some(5));
assert_eq!(kth_ind(&test_vec[2], 3), Some(2));
assert_eq!(kth_ind(&test_vec[2], 4), Some(3));
assert_eq!(kth_ind(&test_vec[2], 5), Some(4));
assert_eq!(kth_ind(&test_vec[2], 6), Some(7));
assert_eq!(kth_ind(&test_vec[2], 7), Some(0));
assert_eq!(kth_ind(&test_vec[2], 8), None);
assert_eq!(kth_ind(&test_vec[2], 9), None);
}
{
assert_eq!(kth_ind(&test_vec[3], 0), Some(4));
assert_eq!(kth_ind(&test_vec[3], 1), Some(4));
assert_eq!(kth_ind(&test_vec[3], 2), Some(4));
assert_eq!(kth_ind(&test_vec[3], 3), Some(5));
assert_eq!(kth_ind(&test_vec[3], 4), Some(1));
assert_eq!(kth_ind(&test_vec[3], 5), Some(2));
assert_eq!(kth_ind(&test_vec[3], 6), Some(0));
assert_eq!(kth_ind(&test_vec[3], 7), Some(0));
assert_eq!(kth_ind(&test_vec[3], 8), None);
assert_eq!(kth_ind(&test_vec[3], 9), None);
}
}
fn median_mut(slice: &mut [Word]) -> Option<Rational> {
if slice.is_empty() {
return None;
}
slice.sort();
let i = slice.len() / 2;
let mut med = slice[i] as Rational;
if (slice.len() % 2) == 0 {
med = (med + (slice[i - 1] as Rational)) / 2.0;
}
Some(med)
}
#[test]
fn median_odd_check() {
let mut test_vec = [
[0, 0, 0, 0, 0, 0, 0, 0, 1],
[1, 1, 0, 1, 1, 0, 0, 0, 1],
[9, 1, 8, 2, 7, 3, 6, 3, 5],
[1, 1, 1, 1, 7, 3, 6, 3, 5],
[9, 1, 8, 2, 7, 0, 0, 0, 0],
];
for i in 0..test_vec.len() {
let v = &mut test_vec[i];
assert_eq!(v.len() % 2, 1);
dbg!(i, &v);
let a = median(v);
let b = median_mut(v);
assert_eq!(a, b);
}
}
#[test]
fn median_even_check() {
let mut test_vec = [
[0, 0, 0, 0, 0, 0, 0, 1],
[1, 1, 0, 1, 0, 0, 0, 1],
[9, 1, 8, 2, 3, 6, 3, 5],
[1, 1, 1, 1, 3, 6, 3, 5],
[9, 1, 8, 2, 0, 0, 0, 0],
];
for i in 0..test_vec.len() {
let v = &mut test_vec[i];
assert_eq!(v.len() % 2, 0);
dbg!(i, &v);
let a = median(v);
let b = median_mut(v);
assert_eq!(a, b);
}
}
#[test]
fn skew_check() {
let arr = [0, 0, 0, 5, 10];
let skew = pearson_skew_median(&arr).unwrap();
assert_eq!(skew, 2.25)
}
}