use statrs::distribution::{Discrete, DiscreteCDF, Poisson}; use crate::expressions::types::CellReferenceIndex; use crate::{ calc_result::CalcResult, expressions::parser::Node, expressions::token::Error, model::Model, }; impl Model { // =POISSON.DIST(x, mean, cumulative) pub(crate) fn fn_poisson_dist( &mut self, args: &[Node], cell: CellReferenceIndex, ) -> CalcResult { if args.len() != 3 { return CalcResult::new_args_number_error(cell); } // x let x = match self.get_number_no_bools(&args[0], cell) { Ok(f) => f.trunc(), Err(e) => return e, }; // mean (lambda) let lambda = match self.get_number_no_bools(&args[1], cell) { Ok(f) => f, Err(e) => return e, }; let cumulative = match self.get_boolean(&args[2], cell) { Ok(b) => b, Err(e) => return e, }; if x < 0.0 || lambda < 0.0 { return CalcResult::Error { error: Error::NUM, origin: cell, message: "Invalid parameters for POISSON.DIST".to_string(), }; } // Guard against insane k for u64 if x < 0.0 || x > (u64::MAX as f64) { return CalcResult::Error { error: Error::NUM, origin: cell, message: "Invalid parameters for POISSON.DIST".to_string(), }; } let k = x as u64; // Special-case lambda = 0: degenerate distribution at 0 if lambda == 0.0 { let result = if cumulative { // For x >= 0, P(X <= x) = 1 1.0 } else { // P(X = 0) = 1, P(X = k>0) = 0 if k == 0 { 1.0 } else { 0.0 } }; return CalcResult::Number(result); } let dist = match Poisson::new(lambda) { Ok(d) => d, Err(_) => { return CalcResult::Error { error: Error::NUM, origin: cell, message: "Invalid parameters for POISSON.DIST".to_string(), } } }; let prob = if cumulative { dist.cdf(k) } else { dist.pmf(k) }; if !prob.is_finite() { return CalcResult::Error { error: Error::NUM, origin: cell, message: "Invalid result for POISSON.DIST".to_string(), }; } CalcResult::Number(prob) } }