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
2 changes: 2 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,7 @@ 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::Skew | Function::SkewP => vec![Signature::Vector; arg_count],
}
}

Expand Down Expand Up @@ -990,5 +991,6 @@ 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::Skew | Function::SkewP => not_implemented(args),
}
}
12 changes: 11 additions & 1 deletion base/src/functions/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -145,6 +145,8 @@ pub enum Function {
Maxifs,
Minifs,
Geomean,
Skew,
SkewP,

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

impl Function {
pub fn into_iter() -> IntoIter<Function, 198> {
pub fn into_iter() -> IntoIter<Function, 200> {
[
Function::And,
Function::False,
Expand Down Expand Up @@ -357,6 +359,8 @@ impl Function {
Function::Maxifs,
Function::Minifs,
Function::Geomean,
Function::Skew,
Function::SkewP,
Function::Year,
Function::Day,
Function::Month,
Expand Down Expand Up @@ -625,6 +629,8 @@ impl Function {
"MAXIFS" | "_XLFN.MAXIFS" => Some(Function::Maxifs),
"MINIFS" | "_XLFN.MINIFS" => Some(Function::Minifs),
"GEOMEAN" => Some(Function::Geomean),
"SKEW" => Some(Function::Skew),
"SKEW.P" | "_XLFN.SKEW.P" => Some(Function::SkewP),
// Date and Time
"YEAR" => Some(Function::Year),
"DAY" => Some(Function::Day),
Expand Down Expand Up @@ -836,6 +842,8 @@ impl fmt::Display for Function {
Function::Maxifs => write!(f, "MAXIFS"),
Function::Minifs => write!(f, "MINIFS"),
Function::Geomean => write!(f, "GEOMEAN"),
Function::Skew => write!(f, "SKEW"),
Function::SkewP => write!(f, "SKEW.P"),
Function::Year => write!(f, "YEAR"),
Function::Day => write!(f, "DAY"),
Function::Month => write!(f, "MONTH"),
Expand Down Expand Up @@ -1076,6 +1084,8 @@ impl Model {
Function::Maxifs => self.fn_maxifs(args, cell),
Function::Minifs => self.fn_minifs(args, cell),
Function::Geomean => self.fn_geomean(args, cell),
Function::Skew => self.fn_skew(args, cell),
Function::SkewP => self.fn_skew_p(args, cell),
// Date and Time
Function::Year => self.fn_year(args, cell),
Function::Day => self.fn_day(args, cell),
Expand Down
170 changes: 170 additions & 0 deletions base/src/functions/statistical.rs
Original file line number Diff line number Diff line change
Expand Up @@ -730,4 +730,174 @@ impl Model {
}
CalcResult::Number(product.powf(1.0 / count))
}

pub(crate) fn fn_skew(&mut self, args: &[Node], cell: CellReferenceIndex) -> CalcResult {
if args.is_empty() {
return CalcResult::new_args_number_error(cell);
}
let mut values = Vec::new();
for arg in args {
match self.evaluate_node_in_context(arg, cell) {
CalcResult::Number(value) => values.push(value),
CalcResult::Boolean(b) => {
if !matches!(arg, Node::ReferenceKind { .. }) {
values.push(if b { 1.0 } else { 0.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 {
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(_)
| CalcResult::EmptyCell
| CalcResult::EmptyArg => {}
CalcResult::Range { .. } => {
return CalcResult::new_error(
Error::ERROR,
cell,
"Unexpected Range".to_string(),
);
}
error @ CalcResult::Error { .. } => return error,
_ => {}
}
}
}
}
error @ CalcResult::Error { .. } => return error,
CalcResult::String(s) => {
if !matches!(arg, Node::ReferenceKind { .. }) {
if let Ok(t) = s.parse::<f64>() {
values.push(t);
} else {
return CalcResult::new_error(
Error::VALUE,
cell,
"Argument cannot be cast into number".to_string(),
);
}
}
}
_ => {}
}
}

let n = values.len();
if n < 3 {
return CalcResult::new_error(Error::DIV, cell, "Division by Zero".to_string());
}

let mean = values.iter().sum::<f64>() / n as f64;
let mut var = 0.0;
for &v in &values {
var += (v - mean).powi(2);
}
let std = (var / (n as f64 - 1.0)).sqrt();
if std == 0.0 {
return CalcResult::new_error(Error::DIV, cell, "division by 0".to_string());
}
let mut sum3 = 0.0;
for &v in &values {
sum3 += ((v - mean) / std).powi(3);
}
let result = n as f64 / ((n as f64 - 1.0) * (n as f64 - 2.0)) * sum3;
CalcResult::Number(result)
}

pub(crate) fn fn_skew_p(&mut self, args: &[Node], cell: CellReferenceIndex) -> CalcResult {
if args.is_empty() {
return CalcResult::new_args_number_error(cell);
}
let mut values = Vec::new();
for arg in args {
match self.evaluate_node_in_context(arg, cell) {
CalcResult::Number(value) => values.push(value),
CalcResult::Boolean(b) => {
if !matches!(arg, Node::ReferenceKind { .. }) {
values.push(if b { 1.0 } else { 0.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 {
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(_)
| CalcResult::EmptyCell
| CalcResult::EmptyArg => {}
CalcResult::Range { .. } => {
return CalcResult::new_error(
Error::ERROR,
cell,
"Unexpected Range".to_string(),
);
}
error @ CalcResult::Error { .. } => return error,
_ => {}
}
}
}
}
error @ CalcResult::Error { .. } => return error,
CalcResult::String(s) => {
if !matches!(arg, Node::ReferenceKind { .. }) {
if let Ok(t) = s.parse::<f64>() {
values.push(t);
} else {
return CalcResult::new_error(
Error::VALUE,
cell,
"Argument cannot be cast into number".to_string(),
);
}
}
}
_ => {}
}
}

let n = values.len();
if n == 0 {
return CalcResult::new_error(Error::DIV, cell, "Division by Zero".to_string());
}

let mean = values.iter().sum::<f64>() / n as f64;
let mut var = 0.0;
for &v in &values {
var += (v - mean).powi(2);
}
let std = (var / n as f64).sqrt();
if std == 0.0 {
return CalcResult::new_error(Error::DIV, cell, "division by 0".to_string());
}
let mut sum3 = 0.0;
for &v in &values {
sum3 += ((v - mean) / std).powi(3);
}
let result = sum3 / n as f64;
CalcResult::Number(result)
}
}
1 change: 1 addition & 0 deletions base/src/test/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,7 @@ mod test_log;
mod test_log10;
mod test_percentage;
mod test_set_functions_error_handling;
mod test_skew;
mod test_today;
mod test_types;
mod user_model;
Loading