use crate::evaluator::RuntimeError;
use crate::value::Value;
fn get_number(val: &Value) -> Result<f64, RuntimeError> {
match val {
Value::Number(n) => Ok(*n),
_ => Err(RuntimeError::TypeErrorDetailed {
expected: "Number".to_string(),
got: format!("{:?}", val),
}),
}
}
pub fn calc_salary_average(args: &[Value]) -> Result<Value, RuntimeError> {
if args.is_empty() {
return Err(RuntimeError::WrongArity {
expected: 1,
got: 0,
});
}
let mut sum = 0.0;
for arg in args {
sum += get_number(arg)?;
}
Ok(Value::Number(sum / args.len() as f64))
}
pub fn calc_salary_median(args: &[Value]) -> Result<Value, RuntimeError> {
if args.is_empty() {
return Err(RuntimeError::WrongArity {
expected: 1,
got: 0,
});
}
let mut salaries: Vec<f64> = Vec::new();
for arg in args {
salaries.push(get_number(arg)?);
}
salaries.sort_by(|a, b| a.partial_cmp(b).unwrap());
let len = salaries.len();
let median = if len.is_multiple_of(2) {
(salaries[len / 2 - 1] + salaries[len / 2]) / 2.0
} else {
salaries[len / 2]
};
Ok(Value::Number(median))
}
pub fn calc_salary_range(args: &[Value]) -> Result<Value, RuntimeError> {
if args.is_empty() {
return Err(RuntimeError::WrongArity {
expected: 1,
got: 0,
});
}
let mut min = f64::MAX;
let mut max = f64::MIN;
for arg in args {
let val = get_number(arg)?;
if val < min {
min = val;
}
if val > max {
max = val;
}
}
Ok(Value::Number(max - min))
}
pub fn calc_percentile(args: &[Value]) -> Result<Value, RuntimeError> {
if args.len() < 2 {
return Err(RuntimeError::WrongArity {
expected: 2,
got: args.len(),
});
}
let percentile = get_number(&args[0])?;
if !(0.0..=100.0).contains(&percentile) {
return Err(RuntimeError::InvalidOperation(
"百分位必须在0-100之间".to_string(),
));
}
let mut salaries: Vec<f64> = Vec::new();
#[allow(clippy::needless_range_loop)]
for i in 1..args.len() {
salaries.push(get_number(&args[i])?);
}
salaries.sort_by(|a, b| a.partial_cmp(b).unwrap());
let index = (percentile / 100.0 * (salaries.len() - 1) as f64).round() as usize;
Ok(Value::Number(salaries[index]))
}
pub fn calc_salary_std_dev(args: &[Value]) -> Result<Value, RuntimeError> {
if args.is_empty() {
return Err(RuntimeError::WrongArity {
expected: 1,
got: 0,
});
}
let mut sum = 0.0;
let mut salaries: Vec<f64> = Vec::new();
for arg in args {
let val = get_number(arg)?;
salaries.push(val);
sum += val;
}
let mean = sum / salaries.len() as f64;
let mut variance_sum = 0.0;
for salary in &salaries {
let diff = salary - mean;
variance_sum += diff * diff;
}
let variance = variance_sum / salaries.len() as f64;
Ok(Value::Number(variance.sqrt()))
}
pub fn calc_salary_distribution(args: &[Value]) -> Result<Value, RuntimeError> {
if args.len() < 2 {
return Err(RuntimeError::WrongArity {
expected: 2,
got: args.len(),
});
}
let bin_size = get_number(&args[0])?;
if bin_size <= 0.0 {
return Err(RuntimeError::InvalidOperation(
"区间大小必须大于0".to_string(),
));
}
let mut salaries: Vec<f64> = Vec::new();
#[allow(clippy::needless_range_loop)]
for i in 1..args.len() {
salaries.push(get_number(&args[i])?);
}
if salaries.is_empty() {
return Ok(Value::Number(0.0));
}
let mut min = salaries[0];
let mut max = salaries[0];
for &salary in &salaries {
if salary < min {
min = salary;
}
if salary > max {
max = salary;
}
}
let num_bins = ((max - min) / bin_size).ceil() + 1.0;
Ok(Value::Number(num_bins))
}