Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions base/src/expressions/parser/static_analysis.rs
Original file line number Diff line number Diff line change
Expand Up @@ -785,6 +785,10 @@ fn get_function_args_signature(kind: &Function, arg_count: usize) -> Vec<Signatu
Function::Formulatext => args_signature_scalars(arg_count, 1, 0),
Function::Unicode => args_signature_scalars(arg_count, 1, 0),
Function::Geomean => vec![Signature::Vector; arg_count],
Function::Stdeva => vec![Signature::Vector; arg_count],
Function::Stdevpa => vec![Signature::Vector; arg_count],
Function::Vara => vec![Signature::Vector; arg_count],
Function::Varpa => vec![Signature::Vector; arg_count],
}
}

Expand Down Expand Up @@ -990,5 +994,9 @@ fn static_analysis_on_function(kind: &Function, args: &[Node]) -> StaticResult {
Function::Eomonth => scalar_arguments(args),
Function::Formulatext => not_implemented(args),
Function::Geomean => not_implemented(args),
Function::Stdeva => not_implemented(args),
Function::Stdevpa => not_implemented(args),
Function::Vara => not_implemented(args),
Function::Varpa => not_implemented(args),
}
}
22 changes: 21 additions & 1 deletion base/src/functions/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -145,6 +145,10 @@ pub enum Function {
Maxifs,
Minifs,
Geomean,
Stdeva,
Stdevpa,
Vara,
Varpa,

// Date and time
Date,
Expand Down Expand Up @@ -253,7 +257,7 @@ pub enum Function {
}

impl Function {
pub fn into_iter() -> IntoIter<Function, 198> {
pub fn into_iter() -> IntoIter<Function, 202> {
[
Function::And,
Function::False,
Expand Down Expand Up @@ -357,6 +361,10 @@ impl Function {
Function::Maxifs,
Function::Minifs,
Function::Geomean,
Function::Stdeva,
Function::Stdevpa,
Function::Vara,
Function::Varpa,
Function::Year,
Function::Day,
Function::Month,
Expand Down Expand Up @@ -625,6 +633,10 @@ impl Function {
"MAXIFS" | "_XLFN.MAXIFS" => Some(Function::Maxifs),
"MINIFS" | "_XLFN.MINIFS" => Some(Function::Minifs),
"GEOMEAN" => Some(Function::Geomean),
"STDEVA" => Some(Function::Stdeva),
"STDEVPA" => Some(Function::Stdevpa),
"VARA" => Some(Function::Vara),
"VARPA" => Some(Function::Varpa),
// Date and Time
"YEAR" => Some(Function::Year),
"DAY" => Some(Function::Day),
Expand Down Expand Up @@ -836,6 +848,10 @@ impl fmt::Display for Function {
Function::Maxifs => write!(f, "MAXIFS"),
Function::Minifs => write!(f, "MINIFS"),
Function::Geomean => write!(f, "GEOMEAN"),
Function::Stdeva => write!(f, "STDEVA"),
Function::Stdevpa => write!(f, "STDEVPA"),
Function::Vara => write!(f, "VARA"),
Function::Varpa => write!(f, "VARPA"),
Function::Year => write!(f, "YEAR"),
Function::Day => write!(f, "DAY"),
Function::Month => write!(f, "MONTH"),
Expand Down Expand Up @@ -1076,6 +1092,10 @@ impl Model {
Function::Maxifs => self.fn_maxifs(args, cell),
Function::Minifs => self.fn_minifs(args, cell),
Function::Geomean => self.fn_geomean(args, cell),
Function::Stdeva => self.fn_stdeva(args, cell),
Function::Stdevpa => self.fn_stdevpa(args, cell),
Function::Vara => self.fn_vara(args, cell),
Function::Varpa => self.fn_varpa(args, cell),
// Date and Time
Function::Year => self.fn_year(args, cell),
Function::Day => self.fn_day(args, cell),
Expand Down
181 changes: 181 additions & 0 deletions base/src/functions/statistical.rs
Original file line number Diff line number Diff line change
Expand Up @@ -730,4 +730,185 @@ impl Model {
}
CalcResult::Number(product.powf(1.0 / count))
}

fn get_a_values(
&mut self,
args: &[Node],
cell: CellReferenceIndex,
) -> Result<Vec<f64>, CalcResult> {
let mut values = Vec::new();
for arg in args {
match self.evaluate_node_in_context(arg, cell) {
CalcResult::Range { left, right } => {
if left.sheet != right.sheet {
return Err(CalcResult::new_error(
Error::VALUE,
cell,
"Ranges are in different sheets".to_string(),
));
}
for row in left.row..=right.row {
for column in left.column..=right.column {
match self.evaluate_cell(CellReferenceIndex {
sheet: left.sheet,
row,
column,
}) {
CalcResult::Number(v) => values.push(v),
CalcResult::Boolean(b) => {
values.push(if b { 1.0 } else { 0.0 });
}
CalcResult::String(_) => values.push(0.0),
error @ CalcResult::Error { .. } => return Err(error),
CalcResult::Range { .. } => {
return Err(CalcResult::new_error(
Error::ERROR,
cell,
"Unexpected Range".to_string(),
))
}
CalcResult::EmptyCell | CalcResult::EmptyArg => {}
CalcResult::Array(_) => {
return Err(CalcResult::Error {
error: Error::NIMPL,
origin: cell,
message: "Arrays not supported yet".to_string(),
})
}
}
}
}
}
CalcResult::Number(v) => values.push(v),
CalcResult::Boolean(b) => values.push(if b { 1.0 } else { 0.0 }),
CalcResult::String(s) => {
if let Node::ReferenceKind { .. } = arg {
values.push(0.0);
} else if let Ok(t) = s.parse::<f64>() {
values.push(t);
} else {
return Err(CalcResult::new_error(
Error::VALUE,
cell,
"Argument cannot be cast into number".to_string(),
));
}
}
error @ CalcResult::Error { .. } => return Err(error),
CalcResult::EmptyCell | CalcResult::EmptyArg => {}
CalcResult::Array(_) => {
return Err(CalcResult::Error {
error: Error::NIMPL,
origin: cell,
message: "Arrays not supported yet".to_string(),
})
}
}
}
Ok(values)
}

pub(crate) fn fn_stdeva(&mut self, args: &[Node], cell: CellReferenceIndex) -> CalcResult {
if args.is_empty() {
return CalcResult::new_args_number_error(cell);
}
let values = match self.get_a_values(args, cell) {
Ok(v) => v,
Err(e) => return e,
};
let l = values.len();
if l < 2 {
return CalcResult::Error {
error: Error::DIV,
origin: cell,
message: "Division by 0".to_string(),
};
}
let sum: f64 = values.iter().sum();
let mean = sum / l as f64;
let mut var = 0.0;
for v in &values {
var += (v - mean).powi(2);
}
var /= l as f64 - 1.0;
CalcResult::Number(var.sqrt())
}

pub(crate) fn fn_stdevpa(&mut self, args: &[Node], cell: CellReferenceIndex) -> CalcResult {
if args.is_empty() {
return CalcResult::new_args_number_error(cell);
}
let values = match self.get_a_values(args, cell) {
Ok(v) => v,
Err(e) => return e,
};
let l = values.len();
if l == 0 {
return CalcResult::Error {
error: Error::DIV,
origin: cell,
message: "Division by 0".to_string(),
};
}
let sum: f64 = values.iter().sum();
let mean = sum / l as f64;
let mut var = 0.0;
for v in &values {
var += (v - mean).powi(2);
}
var /= l as f64;
CalcResult::Number(var.sqrt())
}

pub(crate) fn fn_vara(&mut self, args: &[Node], cell: CellReferenceIndex) -> CalcResult {
if args.is_empty() {
return CalcResult::new_args_number_error(cell);
}
let values = match self.get_a_values(args, cell) {
Ok(v) => v,
Err(e) => return e,
};
let l = values.len();
if l < 2 {
return CalcResult::Error {
error: Error::DIV,
origin: cell,
message: "Division by 0".to_string(),
};
}
let sum: f64 = values.iter().sum();
let mean = sum / l as f64;
let mut var = 0.0;
for v in &values {
var += (v - mean).powi(2);
}
var /= l as f64 - 1.0;
CalcResult::Number(var)
}

pub(crate) fn fn_varpa(&mut self, args: &[Node], cell: CellReferenceIndex) -> CalcResult {
if args.is_empty() {
return CalcResult::new_args_number_error(cell);
}
let values = match self.get_a_values(args, cell) {
Ok(v) => v,
Err(e) => return e,
};
let l = values.len();
if l == 0 {
return CalcResult::Error {
error: Error::DIV,
origin: cell,
message: "Division by 0".to_string(),
};
}
let sum: f64 = values.iter().sum();
let mean = sum / l as f64;
let mut var = 0.0;
for v in &values {
var += (v - mean).powi(2);
}
var /= l as f64;
CalcResult::Number(var)
}
}
1 change: 1 addition & 0 deletions base/src/test/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,7 @@ mod test_arrays;
mod test_escape_quotes;
mod test_extend;
mod test_fn_fv;
mod test_fn_stdev_var;
mod test_fn_type;
mod test_frozen_rows_and_columns;
mod test_geomean;
Expand Down
Loading