UPDATE: Adds SUMX2MY2, SUMX2PY2 and SUMXMY2 mathematical functions

This commit is contained in:
Nicolás Hatcher
2025-11-26 19:16:59 +01:00
committed by Nicolás Hatcher Andrés
parent cd0baf5ba7
commit 4649a0c78c
6 changed files with 257 additions and 353 deletions

View File

@@ -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,
} }
} }

View 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)
}
}

View File

@@ -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),
} }
} }
} }

View File

@@ -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); {
} Ok(v) => v,
let actual_range = self.evaluate_node_in_context(&args[0], cell); Err(r) => return r,
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,
};
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());

View File

@@ -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)
} }
} }

Binary file not shown.