Add GEOMEAN to Functions

This commit is contained in:
Andrew Fillmore
2024-12-10 13:47:50 -08:00
committed by Nicolás Hatcher Andrés
parent 5f3856350b
commit 4c3374c0de
2 changed files with 87 additions and 1 deletions

View File

@@ -140,6 +140,7 @@ pub enum Function {
Countifs, Countifs,
Maxifs, Maxifs,
Minifs, Minifs,
Geomean,
// Date and time // Date and time
Date, Date,
@@ -248,7 +249,7 @@ pub enum Function {
} }
impl Function { impl Function {
pub fn into_iter() -> IntoIter<Function, 194> { pub fn into_iter() -> IntoIter<Function, 195> {
[ [
Function::And, Function::And,
Function::False, Function::False,
@@ -348,6 +349,7 @@ impl Function {
Function::Countifs, Function::Countifs,
Function::Maxifs, Function::Maxifs,
Function::Minifs, Function::Minifs,
Function::Geomean,
Function::Year, Function::Year,
Function::Day, Function::Day,
Function::Month, Function::Month,
@@ -611,6 +613,7 @@ impl Function {
"COUNTIFS" => Some(Function::Countifs), "COUNTIFS" => Some(Function::Countifs),
"MAXIFS" | "_XLFN.MAXIFS" => Some(Function::Maxifs), "MAXIFS" | "_XLFN.MAXIFS" => Some(Function::Maxifs),
"MINIFS" | "_XLFN.MINIFS" => Some(Function::Minifs), "MINIFS" | "_XLFN.MINIFS" => Some(Function::Minifs),
"GEOMEAN" => Some(Function::Geomean),
// Date and Time // Date and Time
"YEAR" => Some(Function::Year), "YEAR" => Some(Function::Year),
"DAY" => Some(Function::Day), "DAY" => Some(Function::Day),
@@ -818,6 +821,7 @@ impl fmt::Display for Function {
Function::Countifs => write!(f, "COUNTIFS"), Function::Countifs => write!(f, "COUNTIFS"),
Function::Maxifs => write!(f, "MAXIFS"), Function::Maxifs => write!(f, "MAXIFS"),
Function::Minifs => write!(f, "MINIFS"), Function::Minifs => write!(f, "MINIFS"),
Function::Geomean => write!(f, "GEOMEAN"),
Function::Year => write!(f, "YEAR"), Function::Year => write!(f, "YEAR"),
Function::Day => write!(f, "DAY"), Function::Day => write!(f, "DAY"),
Function::Month => write!(f, "MONTH"), Function::Month => write!(f, "MONTH"),
@@ -1054,6 +1058,7 @@ impl Model {
Function::Countifs => self.fn_countifs(args, cell), Function::Countifs => self.fn_countifs(args, cell),
Function::Maxifs => self.fn_maxifs(args, cell), Function::Maxifs => self.fn_maxifs(args, cell),
Function::Minifs => self.fn_minifs(args, cell), Function::Minifs => self.fn_minifs(args, cell),
Function::Geomean => self.fn_geomean(args, cell),
// Date and Time // Date and Time
Function::Year => self.fn_year(args, cell), Function::Year => self.fn_year(args, cell),
Function::Day => self.fn_day(args, cell), Function::Day => self.fn_day(args, cell),

View File

@@ -635,4 +635,85 @@ impl Model {
} }
CalcResult::Number(max) 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::<f64>() {
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))
}
} }