UPDATE: Adds SUMX2MY2, SUMX2PY2 and SUMXMY2 mathematical functions
This commit is contained in:
committed by
Nicolás Hatcher Andrés
parent
cd0baf5ba7
commit
4649a0c78c
@@ -872,12 +872,10 @@ fn get_function_args_signature(kind: &Function, arg_count: usize) -> Vec<Signatu
|
|||||||
Function::Combin => args_signature_scalars(arg_count, 2, 0),
|
Function::Combin => args_signature_scalars(arg_count, 2, 0),
|
||||||
Function::Combina => args_signature_scalars(arg_count, 2, 0),
|
Function::Combina => args_signature_scalars(arg_count, 2, 0),
|
||||||
Function::Sumsq => vec![Signature::Vector; arg_count],
|
Function::Sumsq => vec![Signature::Vector; arg_count],
|
||||||
|
|
||||||
Function::N => args_signature_scalars(arg_count, 1, 0),
|
Function::N => args_signature_scalars(arg_count, 1, 0),
|
||||||
Function::Sheets => args_signature_scalars(arg_count, 0, 1),
|
Function::Sheets => args_signature_scalars(arg_count, 0, 1),
|
||||||
Function::Cell => args_signature_scalars(arg_count, 1, 1),
|
Function::Cell => args_signature_scalars(arg_count, 1, 1),
|
||||||
Function::Info => args_signature_scalars(arg_count, 1, 1),
|
Function::Info => args_signature_scalars(arg_count, 1, 1),
|
||||||
|
|
||||||
Function::Daverage => vec![Signature::Vector, Signature::Scalar, Signature::Vector],
|
Function::Daverage => vec![Signature::Vector, Signature::Scalar, Signature::Vector],
|
||||||
Function::Dcount => vec![Signature::Vector, Signature::Scalar, Signature::Vector],
|
Function::Dcount => vec![Signature::Vector, Signature::Scalar, Signature::Vector],
|
||||||
Function::Dget => vec![Signature::Vector, Signature::Scalar, Signature::Vector],
|
Function::Dget => vec![Signature::Vector, Signature::Scalar, Signature::Vector],
|
||||||
@@ -890,7 +888,6 @@ fn get_function_args_signature(kind: &Function, arg_count: usize) -> Vec<Signatu
|
|||||||
Function::Dvar => vec![Signature::Vector, Signature::Scalar, Signature::Vector],
|
Function::Dvar => vec![Signature::Vector, Signature::Scalar, Signature::Vector],
|
||||||
Function::Dvarp => vec![Signature::Vector, Signature::Scalar, Signature::Vector],
|
Function::Dvarp => vec![Signature::Vector, Signature::Scalar, Signature::Vector],
|
||||||
Function::Dstdevp => vec![Signature::Vector, Signature::Scalar, Signature::Vector],
|
Function::Dstdevp => vec![Signature::Vector, Signature::Scalar, Signature::Vector],
|
||||||
|
|
||||||
Function::BetaDist => args_signature_scalars(arg_count, 4, 2),
|
Function::BetaDist => args_signature_scalars(arg_count, 4, 2),
|
||||||
Function::BetaInv => args_signature_scalars(arg_count, 3, 2),
|
Function::BetaInv => args_signature_scalars(arg_count, 3, 2),
|
||||||
Function::BinomDist => args_signature_scalars(arg_count, 4, 0),
|
Function::BinomDist => args_signature_scalars(arg_count, 4, 0),
|
||||||
@@ -990,6 +987,9 @@ fn get_function_args_signature(kind: &Function, arg_count: usize) -> Vec<Signatu
|
|||||||
vec![Signature::Error; arg_count]
|
vec![Signature::Error; arg_count]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Function::Sumx2my2 => vec![Signature::Vector; 2],
|
||||||
|
Function::Sumx2py2 => vec![Signature::Vector; 2],
|
||||||
|
Function::Sumxmy2 => vec![Signature::Vector; 2],
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1015,7 +1015,7 @@ fn static_analysis_on_function(kind: &Function, args: &[Node]) -> StaticResult {
|
|||||||
Function::Atan => scalar_arguments(args),
|
Function::Atan => scalar_arguments(args),
|
||||||
Function::Atan2 => scalar_arguments(args),
|
Function::Atan2 => scalar_arguments(args),
|
||||||
Function::Atanh => scalar_arguments(args),
|
Function::Atanh => scalar_arguments(args),
|
||||||
Function::Choose => scalar_arguments(args), // static_analysis_choose(args, cell),
|
Function::Choose => scalar_arguments(args),
|
||||||
Function::Column => not_implemented(args),
|
Function::Column => not_implemented(args),
|
||||||
Function::Columns => not_implemented(args),
|
Function::Columns => not_implemented(args),
|
||||||
Function::Cos => scalar_arguments(args),
|
Function::Cos => scalar_arguments(args),
|
||||||
@@ -1062,7 +1062,6 @@ fn static_analysis_on_function(kind: &Function, args: &[Node]) -> StaticResult {
|
|||||||
Function::Lookup => not_implemented(args),
|
Function::Lookup => not_implemented(args),
|
||||||
Function::Match => not_implemented(args),
|
Function::Match => not_implemented(args),
|
||||||
Function::Offset => static_analysis_offset(args),
|
Function::Offset => static_analysis_offset(args),
|
||||||
// FIXME: Row could return an array
|
|
||||||
Function::Row => StaticResult::Scalar,
|
Function::Row => StaticResult::Scalar,
|
||||||
Function::Rows => not_implemented(args),
|
Function::Rows => not_implemented(args),
|
||||||
Function::Vlookup => not_implemented(args),
|
Function::Vlookup => not_implemented(args),
|
||||||
@@ -1254,7 +1253,6 @@ fn static_analysis_on_function(kind: &Function, args: &[Node]) -> StaticResult {
|
|||||||
Function::Sheets => scalar_arguments(args),
|
Function::Sheets => scalar_arguments(args),
|
||||||
Function::Cell => scalar_arguments(args),
|
Function::Cell => scalar_arguments(args),
|
||||||
Function::Info => scalar_arguments(args),
|
Function::Info => scalar_arguments(args),
|
||||||
|
|
||||||
Function::Dget => not_implemented(args),
|
Function::Dget => not_implemented(args),
|
||||||
Function::Dmax => not_implemented(args),
|
Function::Dmax => not_implemented(args),
|
||||||
Function::Dmin => not_implemented(args),
|
Function::Dmin => not_implemented(args),
|
||||||
@@ -1267,7 +1265,6 @@ fn static_analysis_on_function(kind: &Function, args: &[Node]) -> StaticResult {
|
|||||||
Function::Dvar => not_implemented(args),
|
Function::Dvar => not_implemented(args),
|
||||||
Function::Dvarp => not_implemented(args),
|
Function::Dvarp => not_implemented(args),
|
||||||
Function::Dstdevp => not_implemented(args),
|
Function::Dstdevp => not_implemented(args),
|
||||||
|
|
||||||
Function::BetaDist => StaticResult::Scalar,
|
Function::BetaDist => StaticResult::Scalar,
|
||||||
Function::BetaInv => StaticResult::Scalar,
|
Function::BetaInv => StaticResult::Scalar,
|
||||||
Function::BinomDist => StaticResult::Scalar,
|
Function::BinomDist => StaticResult::Scalar,
|
||||||
@@ -1324,5 +1321,8 @@ fn static_analysis_on_function(kind: &Function, args: &[Node]) -> StaticResult {
|
|||||||
Function::VarA => StaticResult::Scalar,
|
Function::VarA => StaticResult::Scalar,
|
||||||
Function::WeibullDist => StaticResult::Scalar,
|
Function::WeibullDist => StaticResult::Scalar,
|
||||||
Function::ZTest => StaticResult::Scalar,
|
Function::ZTest => StaticResult::Scalar,
|
||||||
|
Function::Sumx2my2 => StaticResult::Scalar,
|
||||||
|
Function::Sumx2py2 => StaticResult::Scalar,
|
||||||
|
Function::Sumxmy2 => StaticResult::Scalar,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
225
base/src/functions/mathematical_sum.rs
Normal file
225
base/src/functions/mathematical_sum.rs
Normal file
@@ -0,0 +1,225 @@
|
|||||||
|
use crate::expressions::types::CellReferenceIndex;
|
||||||
|
|
||||||
|
use crate::{
|
||||||
|
calc_result::CalcResult, expressions::parser::Node, expressions::token::Error, model::Model,
|
||||||
|
};
|
||||||
|
|
||||||
|
type TwoMatricesResult = (i32, i32, Vec<Option<f64>>, Vec<Option<f64>>);
|
||||||
|
|
||||||
|
// Helper to check if two shapes are the same or compatible 1D shapes
|
||||||
|
fn is_same_shape_or_1d(rows1: i32, cols1: i32, rows2: i32, cols2: i32) -> bool {
|
||||||
|
(rows1 == rows2 && cols1 == cols2)
|
||||||
|
|| (rows1 == 1 && cols2 == 1 && cols1 == rows2)
|
||||||
|
|| (rows2 == 1 && cols1 == 1 && cols2 == rows1)
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Model {
|
||||||
|
pub(crate) fn fn_sumx2my2(&mut self, args: &[Node], cell: CellReferenceIndex) -> CalcResult {
|
||||||
|
let result = match self.fn_get_two_matrices(args, cell) {
|
||||||
|
Ok(s) => s,
|
||||||
|
Err(s) => return s,
|
||||||
|
};
|
||||||
|
|
||||||
|
let (_, _, values_left, values_right) = result;
|
||||||
|
|
||||||
|
let mut sum = 0.0;
|
||||||
|
for (x_opt, y_opt) in values_left.into_iter().zip(values_right.into_iter()) {
|
||||||
|
let x = x_opt.unwrap_or(0.0);
|
||||||
|
let y = y_opt.unwrap_or(0.0);
|
||||||
|
sum += x * x - y * y;
|
||||||
|
}
|
||||||
|
|
||||||
|
CalcResult::Number(sum)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn fn_sumx2py2(&mut self, args: &[Node], cell: CellReferenceIndex) -> CalcResult {
|
||||||
|
let result = match self.fn_get_two_matrices(args, cell) {
|
||||||
|
Ok(s) => s,
|
||||||
|
Err(s) => return s,
|
||||||
|
};
|
||||||
|
|
||||||
|
let (_rows, _cols, values_left, values_right) = result;
|
||||||
|
|
||||||
|
let mut sum = 0.0;
|
||||||
|
for (x_opt, y_opt) in values_left.into_iter().zip(values_right.into_iter()) {
|
||||||
|
let x = x_opt.unwrap_or(0.0);
|
||||||
|
let y = y_opt.unwrap_or(0.0);
|
||||||
|
sum += x * x + y * y;
|
||||||
|
}
|
||||||
|
|
||||||
|
CalcResult::Number(sum)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn fn_sumxmy2(&mut self, args: &[Node], cell: CellReferenceIndex) -> CalcResult {
|
||||||
|
let result = match self.fn_get_two_matrices(args, cell) {
|
||||||
|
Ok(s) => s,
|
||||||
|
Err(s) => return s,
|
||||||
|
};
|
||||||
|
|
||||||
|
let (_, _, values_left, values_right) = result;
|
||||||
|
|
||||||
|
let mut sum = 0.0;
|
||||||
|
for (x_opt, y_opt) in values_left.into_iter().zip(values_right.into_iter()) {
|
||||||
|
let x = x_opt.unwrap_or(0.0);
|
||||||
|
let y = y_opt.unwrap_or(0.0);
|
||||||
|
let diff = x - y;
|
||||||
|
sum += diff * diff;
|
||||||
|
}
|
||||||
|
|
||||||
|
CalcResult::Number(sum)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn fn_get_two_matrices(
|
||||||
|
&mut self,
|
||||||
|
args: &[Node],
|
||||||
|
cell: CellReferenceIndex,
|
||||||
|
) -> Result<TwoMatricesResult, CalcResult> {
|
||||||
|
if args.len() != 2 {
|
||||||
|
return Err(CalcResult::new_args_number_error(cell));
|
||||||
|
}
|
||||||
|
let x_range = self.evaluate_node_in_context(&args[0], cell);
|
||||||
|
let y_range = self.evaluate_node_in_context(&args[1], cell);
|
||||||
|
|
||||||
|
let result = match (x_range, y_range) {
|
||||||
|
(
|
||||||
|
CalcResult::Range {
|
||||||
|
left: l1,
|
||||||
|
right: r1,
|
||||||
|
},
|
||||||
|
CalcResult::Range {
|
||||||
|
left: l2,
|
||||||
|
right: r2,
|
||||||
|
},
|
||||||
|
) => {
|
||||||
|
if l1.sheet != l2.sheet {
|
||||||
|
return Err(CalcResult::new_error(
|
||||||
|
Error::VALUE,
|
||||||
|
cell,
|
||||||
|
"Ranges are in different sheets".to_string(),
|
||||||
|
));
|
||||||
|
}
|
||||||
|
let rows1 = r1.row - l1.row + 1;
|
||||||
|
let cols1 = r1.column - l1.column + 1;
|
||||||
|
let rows2 = r2.row - l2.row + 1;
|
||||||
|
let cols2 = r2.column - l2.column + 1;
|
||||||
|
if !is_same_shape_or_1d(rows1, cols1, rows2, cols2) {
|
||||||
|
return Err(CalcResult::new_error(
|
||||||
|
Error::VALUE,
|
||||||
|
cell,
|
||||||
|
"Ranges must be of the same shape".to_string(),
|
||||||
|
));
|
||||||
|
}
|
||||||
|
let values_left = self.values_from_range(l1, r1)?;
|
||||||
|
let values_right = self.values_from_range(l2, r2)?;
|
||||||
|
(rows1, cols1, values_left, values_right)
|
||||||
|
}
|
||||||
|
(
|
||||||
|
CalcResult::Array(left),
|
||||||
|
CalcResult::Range {
|
||||||
|
left: l2,
|
||||||
|
right: r2,
|
||||||
|
},
|
||||||
|
) => {
|
||||||
|
let rows2 = r2.row - l2.row + 1;
|
||||||
|
let cols2 = r2.column - l2.column + 1;
|
||||||
|
|
||||||
|
let rows1 = left.len() as i32;
|
||||||
|
let cols1 = if rows1 > 0 { left[0].len() as i32 } else { 0 };
|
||||||
|
if !is_same_shape_or_1d(rows1, cols1, rows2, cols2) {
|
||||||
|
return Err(CalcResult::new_error(
|
||||||
|
Error::VALUE,
|
||||||
|
cell,
|
||||||
|
"Array and range must be of the same shape".to_string(),
|
||||||
|
));
|
||||||
|
}
|
||||||
|
let values_left = match self.values_from_array(left) {
|
||||||
|
Err(error) => {
|
||||||
|
return Err(CalcResult::new_error(
|
||||||
|
Error::VALUE,
|
||||||
|
cell,
|
||||||
|
format!("Error in first array: {:?}", error),
|
||||||
|
));
|
||||||
|
}
|
||||||
|
Ok(v) => v,
|
||||||
|
};
|
||||||
|
let values_right = self.values_from_range(l2, r2)?;
|
||||||
|
(rows2, cols2, values_left, values_right)
|
||||||
|
}
|
||||||
|
(
|
||||||
|
CalcResult::Range {
|
||||||
|
left: l1,
|
||||||
|
right: r1,
|
||||||
|
},
|
||||||
|
CalcResult::Array(right),
|
||||||
|
) => {
|
||||||
|
let rows1 = r1.row - l1.row + 1;
|
||||||
|
let cols1 = r1.column - l1.column + 1;
|
||||||
|
|
||||||
|
let rows2 = right.len() as i32;
|
||||||
|
let cols2 = if rows2 > 0 { right[0].len() as i32 } else { 0 };
|
||||||
|
if !is_same_shape_or_1d(rows1, cols1, rows2, cols2) {
|
||||||
|
return Err(CalcResult::new_error(
|
||||||
|
Error::VALUE,
|
||||||
|
cell,
|
||||||
|
"Range and array must be of the same shape".to_string(),
|
||||||
|
));
|
||||||
|
}
|
||||||
|
let values_left = self.values_from_range(l1, r1)?;
|
||||||
|
let values_right = match self.values_from_array(right) {
|
||||||
|
Err(error) => {
|
||||||
|
return Err(CalcResult::new_error(
|
||||||
|
Error::VALUE,
|
||||||
|
cell,
|
||||||
|
format!("Error in second array: {:?}", error),
|
||||||
|
));
|
||||||
|
}
|
||||||
|
Ok(v) => v,
|
||||||
|
};
|
||||||
|
(rows1, cols1, values_left, values_right)
|
||||||
|
}
|
||||||
|
(CalcResult::Array(left), CalcResult::Array(right)) => {
|
||||||
|
let rows1 = left.len() as i32;
|
||||||
|
let rows2 = right.len() as i32;
|
||||||
|
let cols1 = if rows1 > 0 { left[0].len() as i32 } else { 0 };
|
||||||
|
let cols2 = if rows2 > 0 { right[0].len() as i32 } else { 0 };
|
||||||
|
|
||||||
|
if !is_same_shape_or_1d(rows1, cols1, rows2, cols2) {
|
||||||
|
return Err(CalcResult::new_error(
|
||||||
|
Error::VALUE,
|
||||||
|
cell,
|
||||||
|
"Arrays must be of the same shape".to_string(),
|
||||||
|
));
|
||||||
|
}
|
||||||
|
let values_left = match self.values_from_array(left) {
|
||||||
|
Err(error) => {
|
||||||
|
return Err(CalcResult::new_error(
|
||||||
|
Error::VALUE,
|
||||||
|
cell,
|
||||||
|
format!("Error in first array: {:?}", error),
|
||||||
|
));
|
||||||
|
}
|
||||||
|
Ok(v) => v,
|
||||||
|
};
|
||||||
|
let values_right = match self.values_from_array(right) {
|
||||||
|
Err(error) => {
|
||||||
|
return Err(CalcResult::new_error(
|
||||||
|
Error::VALUE,
|
||||||
|
cell,
|
||||||
|
format!("Error in second array: {:?}", error),
|
||||||
|
));
|
||||||
|
}
|
||||||
|
Ok(v) => v,
|
||||||
|
};
|
||||||
|
(rows1, cols1, values_left, values_right)
|
||||||
|
}
|
||||||
|
_ => {
|
||||||
|
return Err(CalcResult::new_error(
|
||||||
|
Error::VALUE,
|
||||||
|
cell,
|
||||||
|
"Both arguments must be ranges or arrays".to_string(),
|
||||||
|
));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
Ok(result)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -19,6 +19,7 @@ mod lookup_and_reference;
|
|||||||
mod macros;
|
mod macros;
|
||||||
mod math_util;
|
mod math_util;
|
||||||
mod mathematical;
|
mod mathematical;
|
||||||
|
mod mathematical_sum;
|
||||||
mod statistical;
|
mod statistical;
|
||||||
mod subtotal;
|
mod subtotal;
|
||||||
mod text;
|
mod text;
|
||||||
@@ -76,6 +77,9 @@ pub enum Function {
|
|||||||
Sum,
|
Sum,
|
||||||
Sumif,
|
Sumif,
|
||||||
Sumifs,
|
Sumifs,
|
||||||
|
Sumx2my2,
|
||||||
|
Sumx2py2,
|
||||||
|
Sumxmy2,
|
||||||
Tan,
|
Tan,
|
||||||
Tanh,
|
Tanh,
|
||||||
Acot,
|
Acot,
|
||||||
@@ -420,7 +424,7 @@ pub enum Function {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl Function {
|
impl Function {
|
||||||
pub fn into_iter() -> IntoIter<Function, 325> {
|
pub fn into_iter() -> IntoIter<Function, 328> {
|
||||||
[
|
[
|
||||||
Function::And,
|
Function::And,
|
||||||
Function::False,
|
Function::False,
|
||||||
@@ -497,6 +501,9 @@ impl Function {
|
|||||||
Function::Sum,
|
Function::Sum,
|
||||||
Function::Sumif,
|
Function::Sumif,
|
||||||
Function::Sumifs,
|
Function::Sumifs,
|
||||||
|
Function::Sumx2my2,
|
||||||
|
Function::Sumx2py2,
|
||||||
|
Function::Sumxmy2,
|
||||||
Function::Choose,
|
Function::Choose,
|
||||||
Function::Column,
|
Function::Column,
|
||||||
Function::Columns,
|
Function::Columns,
|
||||||
@@ -1224,6 +1231,9 @@ impl Function {
|
|||||||
"VARA" => Some(Function::VarA),
|
"VARA" => Some(Function::VarA),
|
||||||
"WEIBULL.DIST" | "_XLFN.WEIBULL.DIST" => Some(Function::WeibullDist),
|
"WEIBULL.DIST" | "_XLFN.WEIBULL.DIST" => Some(Function::WeibullDist),
|
||||||
"Z.TEST" | "_XLFN.Z.TEST" => Some(Function::ZTest),
|
"Z.TEST" | "_XLFN.Z.TEST" => Some(Function::ZTest),
|
||||||
|
"SUMX2MY2" => Some(Function::Sumx2my2),
|
||||||
|
"SUMX2PY2" => Some(Function::Sumx2py2),
|
||||||
|
"SUMXMY2" => Some(Function::Sumxmy2),
|
||||||
|
|
||||||
_ => None,
|
_ => None,
|
||||||
}
|
}
|
||||||
@@ -1560,6 +1570,9 @@ impl fmt::Display for Function {
|
|||||||
Function::VarA => write!(f, "VARA"),
|
Function::VarA => write!(f, "VARA"),
|
||||||
Function::WeibullDist => write!(f, "WEIBULL.DIST"),
|
Function::WeibullDist => write!(f, "WEIBULL.DIST"),
|
||||||
Function::ZTest => write!(f, "Z.TEST"),
|
Function::ZTest => write!(f, "Z.TEST"),
|
||||||
|
Function::Sumx2my2 => write!(f, "SUMX2MY2"),
|
||||||
|
Function::Sumx2py2 => write!(f, "SUMX2PY2"),
|
||||||
|
Function::Sumxmy2 => write!(f, "SUMXMY2"),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1913,6 +1926,9 @@ impl Model {
|
|||||||
Function::VarA => self.fn_vara(args, cell),
|
Function::VarA => self.fn_vara(args, cell),
|
||||||
Function::WeibullDist => self.fn_weibull_dist(args, cell),
|
Function::WeibullDist => self.fn_weibull_dist(args, cell),
|
||||||
Function::ZTest => self.fn_z_test(args, cell),
|
Function::ZTest => self.fn_z_test(args, cell),
|
||||||
|
Function::Sumx2my2 => self.fn_sumx2my2(args, cell),
|
||||||
|
Function::Sumx2py2 => self.fn_sumx2py2(args, cell),
|
||||||
|
Function::Sumxmy2 => self.fn_sumxmy2(args, cell),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,13 +6,6 @@ use crate::{
|
|||||||
calc_result::CalcResult, expressions::parser::Node, expressions::token::Error, model::Model,
|
calc_result::CalcResult, expressions::parser::Node, expressions::token::Error, model::Model,
|
||||||
};
|
};
|
||||||
|
|
||||||
// Helper to check if two shapes are the same or compatible 1D shapes
|
|
||||||
pub(crate) fn is_same_shape_or_1d(rows1: i32, cols1: i32, rows2: i32, cols2: i32) -> bool {
|
|
||||||
(rows1 == rows2 && cols1 == cols2)
|
|
||||||
|| (rows1 == 1 && cols2 == 1 && cols1 == rows2)
|
|
||||||
|| (rows2 == 1 && cols1 == 1 && cols2 == rows1)
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Model {
|
impl Model {
|
||||||
// CHISQ.DIST(x, deg_freedom, cumulative)
|
// CHISQ.DIST(x, deg_freedom, cumulative)
|
||||||
pub(crate) fn fn_chisq_dist(&mut self, args: &[Node], cell: CellReferenceIndex) -> CalcResult {
|
pub(crate) fn fn_chisq_dist(&mut self, args: &[Node], cell: CellReferenceIndex) -> CalcResult {
|
||||||
@@ -310,171 +303,10 @@ impl Model {
|
|||||||
|
|
||||||
// CHISQ.TEST(actual_range, expected_range)
|
// CHISQ.TEST(actual_range, expected_range)
|
||||||
pub(crate) fn fn_chisq_test(&mut self, args: &[Node], cell: CellReferenceIndex) -> CalcResult {
|
pub(crate) fn fn_chisq_test(&mut self, args: &[Node], cell: CellReferenceIndex) -> CalcResult {
|
||||||
if args.len() != 2 {
|
let (width, height, values_left, values_right) = match self.fn_get_two_matrices(args, cell)
|
||||||
return CalcResult::new_args_number_error(cell);
|
{
|
||||||
}
|
|
||||||
let actual_range = self.evaluate_node_in_context(&args[0], cell);
|
|
||||||
let expected_range = self.evaluate_node_in_context(&args[1], cell);
|
|
||||||
|
|
||||||
let (width, height, values_left, values_right) = match (actual_range, expected_range) {
|
|
||||||
(
|
|
||||||
CalcResult::Range {
|
|
||||||
left: l1,
|
|
||||||
right: r1,
|
|
||||||
},
|
|
||||||
CalcResult::Range {
|
|
||||||
left: l2,
|
|
||||||
right: r2,
|
|
||||||
},
|
|
||||||
) => {
|
|
||||||
if l1.sheet != l2.sheet {
|
|
||||||
return CalcResult::new_error(
|
|
||||||
Error::VALUE,
|
|
||||||
cell,
|
|
||||||
"Ranges are in different sheets".to_string(),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
let rows1 = r1.row - l1.row + 1;
|
|
||||||
let cols1 = r1.column - l1.column + 1;
|
|
||||||
let rows2 = r2.row - l2.row + 1;
|
|
||||||
let cols2 = r2.column - l2.column + 1;
|
|
||||||
if !is_same_shape_or_1d(rows1, cols1, rows2, cols2) {
|
|
||||||
return CalcResult::new_error(
|
|
||||||
Error::VALUE,
|
|
||||||
cell,
|
|
||||||
"Ranges must be of the same shape".to_string(),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
let values_left = match self.values_from_range(l1, r1) {
|
|
||||||
Err(error) => {
|
|
||||||
return error;
|
|
||||||
}
|
|
||||||
Ok(v) => v,
|
Ok(v) => v,
|
||||||
};
|
Err(r) => return r,
|
||||||
let values_right = match self.values_from_range(l2, r2) {
|
|
||||||
Err(error) => {
|
|
||||||
return error;
|
|
||||||
}
|
|
||||||
Ok(v) => v,
|
|
||||||
};
|
|
||||||
(rows1, cols1, values_left, values_right)
|
|
||||||
}
|
|
||||||
(
|
|
||||||
CalcResult::Array(left),
|
|
||||||
CalcResult::Range {
|
|
||||||
left: l2,
|
|
||||||
right: r2,
|
|
||||||
},
|
|
||||||
) => {
|
|
||||||
let rows2 = r2.row - l2.row + 1;
|
|
||||||
let cols2 = r2.column - l2.column + 1;
|
|
||||||
|
|
||||||
let rows1 = left.len() as i32;
|
|
||||||
let cols1 = if rows1 > 0 { left[0].len() as i32 } else { 0 };
|
|
||||||
if !is_same_shape_or_1d(rows1, cols1, rows2, cols2) {
|
|
||||||
return CalcResult::new_error(
|
|
||||||
Error::VALUE,
|
|
||||||
cell,
|
|
||||||
"Array and range must be of the same shape".to_string(),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
let values_left = match self.values_from_array(left) {
|
|
||||||
Err(error) => {
|
|
||||||
return CalcResult::new_error(
|
|
||||||
Error::VALUE,
|
|
||||||
cell,
|
|
||||||
format!("Error in first array: {:?}", error),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
Ok(v) => v,
|
|
||||||
};
|
|
||||||
let values_right = match self.values_from_range(l2, r2) {
|
|
||||||
Err(error) => {
|
|
||||||
return error;
|
|
||||||
}
|
|
||||||
Ok(v) => v,
|
|
||||||
};
|
|
||||||
(rows2, cols2, values_left, values_right)
|
|
||||||
}
|
|
||||||
(
|
|
||||||
CalcResult::Range {
|
|
||||||
left: l1,
|
|
||||||
right: r1,
|
|
||||||
},
|
|
||||||
CalcResult::Array(right),
|
|
||||||
) => {
|
|
||||||
let rows1 = r1.row - l1.row + 1;
|
|
||||||
let cols1 = r1.column - l1.column + 1;
|
|
||||||
|
|
||||||
let rows2 = right.len() as i32;
|
|
||||||
let cols2 = if rows2 > 0 { right[0].len() as i32 } else { 0 };
|
|
||||||
if !is_same_shape_or_1d(rows1, cols1, rows2, cols2) {
|
|
||||||
return CalcResult::new_error(
|
|
||||||
Error::VALUE,
|
|
||||||
cell,
|
|
||||||
"Range and array must be of the same shape".to_string(),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
let values_left = match self.values_from_range(l1, r1) {
|
|
||||||
Err(error) => {
|
|
||||||
return error;
|
|
||||||
}
|
|
||||||
Ok(v) => v,
|
|
||||||
};
|
|
||||||
let values_right = match self.values_from_array(right) {
|
|
||||||
Err(error) => {
|
|
||||||
return CalcResult::new_error(
|
|
||||||
Error::VALUE,
|
|
||||||
cell,
|
|
||||||
format!("Error in second array: {:?}", error),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
Ok(v) => v,
|
|
||||||
};
|
|
||||||
(rows1, cols1, values_left, values_right)
|
|
||||||
}
|
|
||||||
(CalcResult::Array(left), CalcResult::Array(right)) => {
|
|
||||||
let rows1 = left.len() as i32;
|
|
||||||
let rows2 = right.len() as i32;
|
|
||||||
let cols1 = if rows1 > 0 { left[0].len() as i32 } else { 0 };
|
|
||||||
let cols2 = if rows2 > 0 { right[0].len() as i32 } else { 0 };
|
|
||||||
|
|
||||||
if !is_same_shape_or_1d(rows1, cols1, rows2, cols2) {
|
|
||||||
return CalcResult::new_error(
|
|
||||||
Error::VALUE,
|
|
||||||
cell,
|
|
||||||
"Arrays must be of the same shape".to_string(),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
let values_left = match self.values_from_array(left) {
|
|
||||||
Err(error) => {
|
|
||||||
return CalcResult::new_error(
|
|
||||||
Error::VALUE,
|
|
||||||
cell,
|
|
||||||
format!("Error in first array: {:?}", error),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
Ok(v) => v,
|
|
||||||
};
|
|
||||||
let values_right = match self.values_from_array(right) {
|
|
||||||
Err(error) => {
|
|
||||||
return CalcResult::new_error(
|
|
||||||
Error::VALUE,
|
|
||||||
cell,
|
|
||||||
format!("Error in second array: {:?}", error),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
Ok(v) => v,
|
|
||||||
};
|
|
||||||
(rows1, cols1, values_left, values_right)
|
|
||||||
}
|
|
||||||
_ => {
|
|
||||||
return CalcResult::new_error(
|
|
||||||
Error::VALUE,
|
|
||||||
cell,
|
|
||||||
"Both arguments must be ranges or arrays".to_string(),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
let mut values = Vec::with_capacity(values_left.len());
|
let mut values = Vec::with_capacity(values_left.len());
|
||||||
|
|||||||
@@ -1,5 +1,4 @@
|
|||||||
use crate::expressions::types::CellReferenceIndex;
|
use crate::expressions::types::CellReferenceIndex;
|
||||||
use crate::functions::statistical::chisq::is_same_shape_or_1d;
|
|
||||||
use crate::{
|
use crate::{
|
||||||
calc_result::CalcResult, expressions::parser::Node, expressions::token::Error, model::Model,
|
calc_result::CalcResult, expressions::parser::Node, expressions::token::Error, model::Model,
|
||||||
};
|
};
|
||||||
@@ -7,176 +6,9 @@ use crate::{
|
|||||||
impl Model {
|
impl Model {
|
||||||
// PEARSON(array1, array2)
|
// PEARSON(array1, array2)
|
||||||
pub(crate) fn fn_pearson(&mut self, args: &[Node], cell: CellReferenceIndex) -> CalcResult {
|
pub(crate) fn fn_pearson(&mut self, args: &[Node], cell: CellReferenceIndex) -> CalcResult {
|
||||||
if args.len() != 2 {
|
let (_, _, values_left, values_right) = match self.fn_get_two_matrices(args, cell) {
|
||||||
return CalcResult::new_args_number_error(cell);
|
Ok(result) => result,
|
||||||
}
|
Err(e) => return e,
|
||||||
|
|
||||||
let left_arg = self.evaluate_node_in_context(&args[0], cell);
|
|
||||||
let right_arg = self.evaluate_node_in_context(&args[1], cell);
|
|
||||||
|
|
||||||
let (values_left, values_right) = match (left_arg, right_arg) {
|
|
||||||
(
|
|
||||||
CalcResult::Range {
|
|
||||||
left: l1,
|
|
||||||
right: r1,
|
|
||||||
},
|
|
||||||
CalcResult::Range {
|
|
||||||
left: l2,
|
|
||||||
right: r2,
|
|
||||||
},
|
|
||||||
) => {
|
|
||||||
if l1.sheet != l2.sheet {
|
|
||||||
return CalcResult::new_error(
|
|
||||||
Error::VALUE,
|
|
||||||
cell,
|
|
||||||
"Ranges are in different sheets".to_string(),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
let rows1 = r1.row - l1.row + 1;
|
|
||||||
let cols1 = r1.column - l1.column + 1;
|
|
||||||
let rows2 = r2.row - l2.row + 1;
|
|
||||||
let cols2 = r2.column - l2.column + 1;
|
|
||||||
|
|
||||||
if !is_same_shape_or_1d(rows1, cols1, rows2, cols2) {
|
|
||||||
return CalcResult::new_error(
|
|
||||||
Error::VALUE,
|
|
||||||
cell,
|
|
||||||
"Ranges must be of the same shape".to_string(),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
let values_left = match self.values_from_range(l1, r1) {
|
|
||||||
Err(error) => return error,
|
|
||||||
Ok(v) => v,
|
|
||||||
};
|
|
||||||
let values_right = match self.values_from_range(l2, r2) {
|
|
||||||
Err(error) => return error,
|
|
||||||
Ok(v) => v,
|
|
||||||
};
|
|
||||||
|
|
||||||
(values_left, values_right)
|
|
||||||
}
|
|
||||||
(
|
|
||||||
CalcResult::Array(left),
|
|
||||||
CalcResult::Range {
|
|
||||||
left: l2,
|
|
||||||
right: r2,
|
|
||||||
},
|
|
||||||
) => {
|
|
||||||
let rows2 = r2.row - l2.row + 1;
|
|
||||||
let cols2 = r2.column - l2.column + 1;
|
|
||||||
|
|
||||||
let rows1 = left.len() as i32;
|
|
||||||
let cols1 = if rows1 > 0 { left[0].len() as i32 } else { 0 };
|
|
||||||
|
|
||||||
if !is_same_shape_or_1d(rows1, cols1, rows2, cols2) {
|
|
||||||
return CalcResult::new_error(
|
|
||||||
Error::VALUE,
|
|
||||||
cell,
|
|
||||||
"Array and range must be of the same shape".to_string(),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
let values_left = match self.values_from_array(left) {
|
|
||||||
Err(error) => {
|
|
||||||
return CalcResult::new_error(
|
|
||||||
Error::VALUE,
|
|
||||||
cell,
|
|
||||||
format!("Error in first array: {:?}", error),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
Ok(v) => v,
|
|
||||||
};
|
|
||||||
let values_right = match self.values_from_range(l2, r2) {
|
|
||||||
Err(error) => return error,
|
|
||||||
Ok(v) => v,
|
|
||||||
};
|
|
||||||
|
|
||||||
(values_left, values_right)
|
|
||||||
}
|
|
||||||
(
|
|
||||||
CalcResult::Range {
|
|
||||||
left: l1,
|
|
||||||
right: r1,
|
|
||||||
},
|
|
||||||
CalcResult::Array(right),
|
|
||||||
) => {
|
|
||||||
let rows1 = r1.row - l1.row + 1;
|
|
||||||
let cols1 = r1.column - l1.column + 1;
|
|
||||||
|
|
||||||
let rows2 = right.len() as i32;
|
|
||||||
let cols2 = if rows2 > 0 { right[0].len() as i32 } else { 0 };
|
|
||||||
|
|
||||||
if !is_same_shape_or_1d(rows1, cols1, rows2, cols2) {
|
|
||||||
return CalcResult::new_error(
|
|
||||||
Error::VALUE,
|
|
||||||
cell,
|
|
||||||
"Range and array must be of the same shape".to_string(),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
let values_left = match self.values_from_range(l1, r1) {
|
|
||||||
Err(error) => return error,
|
|
||||||
Ok(v) => v,
|
|
||||||
};
|
|
||||||
let values_right = match self.values_from_array(right) {
|
|
||||||
Err(error) => {
|
|
||||||
return CalcResult::new_error(
|
|
||||||
Error::VALUE,
|
|
||||||
cell,
|
|
||||||
format!("Error in second array: {:?}", error),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
Ok(v) => v,
|
|
||||||
};
|
|
||||||
|
|
||||||
(values_left, values_right)
|
|
||||||
}
|
|
||||||
(CalcResult::Array(left), CalcResult::Array(right)) => {
|
|
||||||
let rows1 = left.len() as i32;
|
|
||||||
let rows2 = right.len() as i32;
|
|
||||||
let cols1 = if rows1 > 0 { left[0].len() as i32 } else { 0 };
|
|
||||||
let cols2 = if rows2 > 0 { right[0].len() as i32 } else { 0 };
|
|
||||||
|
|
||||||
if !is_same_shape_or_1d(rows1, cols1, rows2, cols2) {
|
|
||||||
return CalcResult::new_error(
|
|
||||||
Error::VALUE,
|
|
||||||
cell,
|
|
||||||
"Arrays must be of the same shape".to_string(),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
let values_left = match self.values_from_array(left) {
|
|
||||||
Err(error) => {
|
|
||||||
return CalcResult::new_error(
|
|
||||||
Error::VALUE,
|
|
||||||
cell,
|
|
||||||
format!("Error in first array: {:?}", error),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
Ok(v) => v,
|
|
||||||
};
|
|
||||||
let values_right = match self.values_from_array(right) {
|
|
||||||
Err(error) => {
|
|
||||||
return CalcResult::new_error(
|
|
||||||
Error::VALUE,
|
|
||||||
cell,
|
|
||||||
format!("Error in second array: {:?}", error),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
Ok(v) => v,
|
|
||||||
};
|
|
||||||
|
|
||||||
(values_left, values_right)
|
|
||||||
}
|
|
||||||
_ => {
|
|
||||||
return CalcResult::new_error(
|
|
||||||
Error::VALUE,
|
|
||||||
cell,
|
|
||||||
"Both arguments must be ranges or arrays".to_string(),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
// Flatten into (x, y) pairs, skipping non-numeric entries (None)
|
// Flatten into (x, y) pairs, skipping non-numeric entries (None)
|
||||||
@@ -228,8 +60,7 @@ impl Model {
|
|||||||
}
|
}
|
||||||
|
|
||||||
let denom = (denom_x * denom_y).sqrt();
|
let denom = (denom_x * denom_y).sqrt();
|
||||||
let r = num / denom;
|
|
||||||
|
|
||||||
CalcResult::Number(r)
|
CalcResult::Number(num / denom)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
BIN
xlsx/tests/calc_tests/SUMX2MY2_SUMX2PY2_SUMXMY2.xlsx
Normal file
BIN
xlsx/tests/calc_tests/SUMX2MY2_SUMX2PY2_SUMXMY2.xlsx
Normal file
Binary file not shown.
Reference in New Issue
Block a user