use crate::eval::functions::check_arity;
use crate::types::{ErrorKind, Value};
pub fn percentile_inc_fn(args: &[Value]) -> Value {
if let Some(err) = check_arity(args, 2, 2) {
return err;
}
let k = match &args[1] {
Value::Number(n) => *n,
_ => return Value::Error(ErrorKind::Num),
};
if !(0.0..=1.0).contains(&k) {
return Value::Error(ErrorKind::Num);
}
let mut nums = match collect_numbers_checked(&args[0]) {
Ok(v) => v,
Err(e) => return e,
};
if nums.is_empty() {
return Value::Error(ErrorKind::Num);
}
nums.sort_by(|a, b| a.partial_cmp(b).unwrap());
Value::Number(percentile_inc_calc(&nums, k))
}
pub fn percentile_inc_calc(sorted: &[f64], k: f64) -> f64 {
let n = sorted.len();
if n == 1 {
return sorted[0];
}
let idx = k * (n - 1) as f64;
let lo = idx.floor() as usize;
let hi = idx.ceil() as usize;
if lo == hi {
sorted[lo]
} else {
let frac = idx - lo as f64;
sorted[lo] * (1.0 - frac) + sorted[hi] * frac
}
}
pub fn collect_numbers(v: &Value) -> Vec<f64> {
match v {
Value::Array(arr) => {
let mut nums = Vec::new();
collect_numbers_into(arr, &mut nums);
nums
}
Value::Number(n) => vec![*n],
_ => vec![],
}
}
fn collect_numbers_into(arr: &[Value], out: &mut Vec<f64>) {
for x in arr {
match x {
Value::Number(n) => out.push(*n),
Value::Array(inner) => collect_numbers_into(inner, out),
_ => {}
}
}
}
pub fn collect_numbers_checked(v: &Value) -> Result<Vec<f64>, Value> {
match v {
Value::Array(arr) => {
let mut nums = Vec::new();
collect_numbers_checked_into(arr, &mut nums)?;
Ok(nums)
}
Value::Number(n) => Ok(vec![*n]),
Value::Error(e) => Err(Value::Error(e.clone())),
Value::ErrorMsg(e, m) => Err(Value::ErrorMsg(e.clone(), m.clone())),
_ => Ok(vec![]),
}
}
fn collect_numbers_checked_into(arr: &[Value], out: &mut Vec<f64>) -> Result<(), Value> {
for x in arr {
match x {
Value::Number(n) => out.push(*n),
Value::Array(inner) => collect_numbers_checked_into(inner, out)?,
Value::Error(e) => return Err(Value::Error(e.clone())),
Value::ErrorMsg(e, m) => return Err(Value::ErrorMsg(e.clone(), m.clone())),
_ => {}
}
}
Ok(())
}
#[cfg(test)]
mod tests;