Uses statrs for numerical functions REFACTOR: Put statistical functions on its own module This might seem counter-intuitive but the wasm build after this refactor is 1528 bytes smaller :)
125 lines
3.6 KiB
Rust
125 lines
3.6 KiB
Rust
use statrs::distribution::{Continuous, ContinuousCDF, LogNormal};
|
|
|
|
use crate::expressions::types::CellReferenceIndex;
|
|
use crate::{
|
|
calc_result::CalcResult, expressions::parser::Node, expressions::token::Error, model::Model,
|
|
};
|
|
|
|
impl Model {
|
|
pub(crate) fn fn_log_norm_dist(
|
|
&mut self,
|
|
args: &[Node],
|
|
cell: CellReferenceIndex,
|
|
) -> CalcResult {
|
|
if args.len() != 4 {
|
|
return CalcResult::new_args_number_error(cell);
|
|
}
|
|
|
|
let x = match self.get_number_no_bools(&args[0], cell) {
|
|
Ok(f) => f,
|
|
Err(e) => return e,
|
|
};
|
|
let mean = match self.get_number_no_bools(&args[1], cell) {
|
|
Ok(f) => f,
|
|
Err(e) => return e,
|
|
};
|
|
let std_dev = match self.get_number_no_bools(&args[2], cell) {
|
|
Ok(f) => f,
|
|
Err(e) => return e,
|
|
};
|
|
let cumulative = match self.get_boolean(&args[3], cell) {
|
|
Ok(b) => b,
|
|
Err(e) => return e,
|
|
};
|
|
|
|
// Excel domain checks
|
|
if x <= 0.0 || std_dev <= 0.0 {
|
|
return CalcResult::Error {
|
|
error: Error::NUM,
|
|
origin: cell,
|
|
message: "Invalid parameter for LOGNORM.DIST".to_string(),
|
|
};
|
|
}
|
|
|
|
let dist = match LogNormal::new(mean, std_dev) {
|
|
Ok(d) => d,
|
|
Err(_) => {
|
|
return CalcResult::Error {
|
|
error: Error::NUM,
|
|
origin: cell,
|
|
message: "Invalid parameter for LOGNORM.DIST".to_string(),
|
|
}
|
|
}
|
|
};
|
|
|
|
let result = if cumulative { dist.cdf(x) } else { dist.pdf(x) };
|
|
|
|
if !result.is_finite() {
|
|
return CalcResult::Error {
|
|
error: Error::NUM,
|
|
origin: cell,
|
|
message: "Invalid parameter for LOGNORM.DIST".to_string(),
|
|
};
|
|
}
|
|
|
|
CalcResult::Number(result)
|
|
}
|
|
|
|
pub(crate) fn fn_log_norm_inv(
|
|
&mut self,
|
|
args: &[Node],
|
|
cell: CellReferenceIndex,
|
|
) -> CalcResult {
|
|
use statrs::distribution::{ContinuousCDF, LogNormal};
|
|
|
|
if args.len() != 3 {
|
|
return CalcResult::new_args_number_error(cell);
|
|
}
|
|
|
|
let p = match self.get_number_no_bools(&args[0], cell) {
|
|
Ok(f) => f,
|
|
Err(e) => return e,
|
|
};
|
|
let mean = match self.get_number_no_bools(&args[1], cell) {
|
|
Ok(f) => f,
|
|
Err(e) => return e,
|
|
};
|
|
let std_dev = match self.get_number_no_bools(&args[2], cell) {
|
|
Ok(f) => f,
|
|
Err(e) => return e,
|
|
};
|
|
|
|
// Excel domain checks
|
|
if p <= 0.0 || p >= 1.0 || std_dev <= 0.0 {
|
|
return CalcResult::Error {
|
|
error: Error::NUM,
|
|
origin: cell,
|
|
message: "Invalid parameter for LOGNORM.INV".to_string(),
|
|
};
|
|
}
|
|
|
|
let dist = match LogNormal::new(mean, std_dev) {
|
|
Ok(d) => d,
|
|
Err(_) => {
|
|
return CalcResult::Error {
|
|
error: Error::NUM,
|
|
origin: cell,
|
|
message: "Invalid parameter for LOGNORM.INV".to_string(),
|
|
}
|
|
}
|
|
};
|
|
|
|
let result = dist.inverse_cdf(p);
|
|
|
|
if !result.is_finite() {
|
|
return CalcResult::Error {
|
|
error: Error::NUM,
|
|
origin: cell,
|
|
message: "Invalid parameter for LOGNORM.INV".to_string(),
|
|
};
|
|
}
|
|
|
|
CalcResult::Number(result)
|
|
}
|
|
}
|