diff --git a/base/src/functions/mod.rs b/base/src/functions/mod.rs index 9f3a0aa..7009f05 100644 --- a/base/src/functions/mod.rs +++ b/base/src/functions/mod.rs @@ -140,6 +140,7 @@ pub enum Function { Countifs, Maxifs, Minifs, + Geomean, // Date and time Date, @@ -248,7 +249,7 @@ pub enum Function { } impl Function { - pub fn into_iter() -> IntoIter { + pub fn into_iter() -> IntoIter { [ Function::And, Function::False, @@ -348,6 +349,7 @@ impl Function { Function::Countifs, Function::Maxifs, Function::Minifs, + Function::Geomean, Function::Year, Function::Day, Function::Month, @@ -611,6 +613,7 @@ impl Function { "COUNTIFS" => Some(Function::Countifs), "MAXIFS" | "_XLFN.MAXIFS" => Some(Function::Maxifs), "MINIFS" | "_XLFN.MINIFS" => Some(Function::Minifs), + "GEOMEAN" => Some(Function::Geomean), // Date and Time "YEAR" => Some(Function::Year), "DAY" => Some(Function::Day), @@ -818,6 +821,7 @@ impl fmt::Display for Function { Function::Countifs => write!(f, "COUNTIFS"), Function::Maxifs => write!(f, "MAXIFS"), Function::Minifs => write!(f, "MINIFS"), + Function::Geomean => write!(f, "GEOMEAN"), Function::Year => write!(f, "YEAR"), Function::Day => write!(f, "DAY"), Function::Month => write!(f, "MONTH"), @@ -1054,6 +1058,7 @@ impl Model { Function::Countifs => self.fn_countifs(args, cell), Function::Maxifs => self.fn_maxifs(args, cell), Function::Minifs => self.fn_minifs(args, cell), + Function::Geomean => self.fn_geomean(args, cell), // Date and Time Function::Year => self.fn_year(args, cell), Function::Day => self.fn_day(args, cell), diff --git a/base/src/functions/statistical.rs b/base/src/functions/statistical.rs index 2c1523c..a7d0825 100644 --- a/base/src/functions/statistical.rs +++ b/base/src/functions/statistical.rs @@ -635,4 +635,85 @@ impl Model { } CalcResult::Number(max) } + + pub(crate) fn fn_geomean(&mut self, args: &[Node], cell: CellReferenceIndex) -> CalcResult{ + if args.is_empty() { + return CalcResult::new_args_number_error(cell); + } + let mut count = 0.0; + let mut product = 1.0; + for arg in args { + match self.evaluate_node_in_context(arg, cell) { + CalcResult::Number(value) => { + count += 1.0; + product *= value; + } + CalcResult::Boolean(b) => { + if let Node::ReferenceKind { .. } = arg { + } else { + product *= if b { 1.0 } else { 0.0 }; + count += 1.0; + } + } + CalcResult::Range { left, right } => { + if left.sheet != right.sheet { + return CalcResult::new_error( + Error::VALUE, + cell, + "Ranges are in different sheets".to_string(), + ); + } + for row in left.row..(right.row + 1) { + for column in left.column..(right.column + 1) { + match self.evaluate_cell(CellReferenceIndex { + sheet: left.sheet, + row, + column, + }) { + CalcResult::Number(value) => { + count += 1.0; + product *= value; + } + error @ CalcResult::Error { .. } => return error, + CalcResult::Range { .. } => { + return CalcResult::new_error( + Error::ERROR, + cell, + "Unexpected Range".to_string(), + ); + } + _ => {} + } + } + } + } + error @ CalcResult::Error { .. } => return error, + CalcResult::String(s) => { + if let Node::ReferenceKind { .. } = arg { + // Do nothing + } else if let Ok(t) = s.parse::() { + product *= t; + count += 1.0; + } else { + return CalcResult::Error { + error: Error::VALUE, + origin: cell, + message: "Argument cannot be cast into number".to_string(), + }; + } + } + _ => { + // Ignore everything else + } + }; + } + if count == 0.0 { + return CalcResult::Error { + error: Error::DIV, + origin: cell, + message: "Division by Zero".to_string(), + }; + } + CalcResult::Number(product.powf(1.0/count)) + } }