UPDATE: Adds COMBIN, COMBINA and SUMSQ (#511)
This commit is contained in:
committed by
GitHub
parent
e5854ab3d7
commit
68a33a5f87
@@ -868,6 +868,9 @@ fn get_function_args_signature(kind: &Function, arg_count: usize) -> Vec<Signatu
|
|||||||
Function::Decimal => args_signature_scalars(arg_count, 2, 0),
|
Function::Decimal => args_signature_scalars(arg_count, 2, 0),
|
||||||
Function::Roman => args_signature_scalars(arg_count, 1, 1),
|
Function::Roman => args_signature_scalars(arg_count, 1, 1),
|
||||||
Function::Arabic => args_signature_scalars(arg_count, 1, 0),
|
Function::Arabic => args_signature_scalars(arg_count, 1, 0),
|
||||||
|
Function::Combin => args_signature_scalars(arg_count, 2, 0),
|
||||||
|
Function::Combina => args_signature_scalars(arg_count, 2, 0),
|
||||||
|
Function::Sumsq => vec![Signature::Vector; arg_count],
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1124,5 +1127,8 @@ fn static_analysis_on_function(kind: &Function, args: &[Node]) -> StaticResult {
|
|||||||
Function::Decimal => scalar_arguments(args),
|
Function::Decimal => scalar_arguments(args),
|
||||||
Function::Roman => scalar_arguments(args),
|
Function::Roman => scalar_arguments(args),
|
||||||
Function::Arabic => scalar_arguments(args),
|
Function::Arabic => scalar_arguments(args),
|
||||||
|
Function::Combin => scalar_arguments(args),
|
||||||
|
Function::Combina => scalar_arguments(args),
|
||||||
|
Function::Sumsq => StaticResult::Scalar,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -633,6 +633,101 @@ impl Model {
|
|||||||
CalcResult::Number(result)
|
CalcResult::Number(result)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub(crate) fn fn_sumsq(&mut self, args: &[Node], cell: CellReferenceIndex) -> CalcResult {
|
||||||
|
if args.is_empty() {
|
||||||
|
return CalcResult::new_args_number_error(cell);
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut result = 0.0;
|
||||||
|
for arg in args {
|
||||||
|
match self.evaluate_node_in_context(arg, cell) {
|
||||||
|
CalcResult::Number(value) => result += value * value,
|
||||||
|
CalcResult::Range { left, right } => {
|
||||||
|
if left.sheet != right.sheet {
|
||||||
|
return CalcResult::new_error(
|
||||||
|
Error::VALUE,
|
||||||
|
cell,
|
||||||
|
"Ranges are in different sheets".to_string(),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
// TODO: We should do this for all functions that run through ranges
|
||||||
|
// Running cargo test for the ironcalc takes around .8 seconds with this speedup
|
||||||
|
// and ~ 3.5 seconds without it. Note that once properly in place sheet.dimension should be almost a noop
|
||||||
|
let row1 = left.row;
|
||||||
|
let mut row2 = right.row;
|
||||||
|
let column1 = left.column;
|
||||||
|
let mut column2 = right.column;
|
||||||
|
if row1 == 1 && row2 == LAST_ROW {
|
||||||
|
row2 = match self.workbook.worksheet(left.sheet) {
|
||||||
|
Ok(s) => s.dimension().max_row,
|
||||||
|
Err(_) => {
|
||||||
|
return CalcResult::new_error(
|
||||||
|
Error::ERROR,
|
||||||
|
cell,
|
||||||
|
format!("Invalid worksheet index: '{}'", left.sheet),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
if column1 == 1 && column2 == LAST_COLUMN {
|
||||||
|
column2 = match self.workbook.worksheet(left.sheet) {
|
||||||
|
Ok(s) => s.dimension().max_column,
|
||||||
|
Err(_) => {
|
||||||
|
return CalcResult::new_error(
|
||||||
|
Error::ERROR,
|
||||||
|
cell,
|
||||||
|
format!("Invalid worksheet index: '{}'", left.sheet),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
for row in row1..row2 + 1 {
|
||||||
|
for column in column1..(column2 + 1) {
|
||||||
|
match self.evaluate_cell(CellReferenceIndex {
|
||||||
|
sheet: left.sheet,
|
||||||
|
row,
|
||||||
|
column,
|
||||||
|
}) {
|
||||||
|
CalcResult::Number(value) => {
|
||||||
|
result += value * value;
|
||||||
|
}
|
||||||
|
error @ CalcResult::Error { .. } => return error,
|
||||||
|
_ => {
|
||||||
|
// We ignore booleans and strings
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
CalcResult::Array(array) => {
|
||||||
|
for row in array {
|
||||||
|
for value in row {
|
||||||
|
match value {
|
||||||
|
ArrayNode::Number(value) => {
|
||||||
|
result += value * value;
|
||||||
|
}
|
||||||
|
ArrayNode::Error(error) => {
|
||||||
|
return CalcResult::Error {
|
||||||
|
error,
|
||||||
|
origin: cell,
|
||||||
|
message: "Error in array".to_string(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_ => {
|
||||||
|
// We ignore booleans and strings
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
error @ CalcResult::Error { .. } => return error,
|
||||||
|
_ => {
|
||||||
|
// We ignore booleans and strings
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
CalcResult::Number(result)
|
||||||
|
}
|
||||||
pub(crate) fn fn_product(&mut self, args: &[Node], cell: CellReferenceIndex) -> CalcResult {
|
pub(crate) fn fn_product(&mut self, args: &[Node], cell: CellReferenceIndex) -> CalcResult {
|
||||||
if args.is_empty() {
|
if args.is_empty() {
|
||||||
return CalcResult::new_args_number_error(cell);
|
return CalcResult::new_args_number_error(cell);
|
||||||
@@ -1465,4 +1560,67 @@ impl Model {
|
|||||||
},
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub(crate) fn fn_combin(&mut self, args: &[Node], cell: CellReferenceIndex) -> CalcResult {
|
||||||
|
if args.len() != 2 {
|
||||||
|
return CalcResult::new_args_number_error(cell);
|
||||||
|
}
|
||||||
|
let n = match self.get_number(&args[0], cell) {
|
||||||
|
Ok(f) => f.floor(),
|
||||||
|
Err(s) => return s,
|
||||||
|
};
|
||||||
|
let k = match self.get_number(&args[1], cell) {
|
||||||
|
Ok(f) => f.floor(),
|
||||||
|
Err(s) => return s,
|
||||||
|
};
|
||||||
|
if n < 0.0 || k < 0.0 {
|
||||||
|
return CalcResult::Error {
|
||||||
|
error: Error::NUM,
|
||||||
|
origin: cell,
|
||||||
|
message: "Arguments must be non-negative integers".to_string(),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
if k > n {
|
||||||
|
return CalcResult::Error {
|
||||||
|
error: Error::NUM,
|
||||||
|
origin: cell,
|
||||||
|
message: "k cannot be greater than n".to_string(),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
let k = k as usize;
|
||||||
|
let mut result = 1.0;
|
||||||
|
for i in 0..k {
|
||||||
|
let t = i as f64;
|
||||||
|
result *= (n - t) / (t + 1.0);
|
||||||
|
}
|
||||||
|
CalcResult::Number(result)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn fn_combina(&mut self, args: &[Node], cell: CellReferenceIndex) -> CalcResult {
|
||||||
|
if args.len() != 2 {
|
||||||
|
return CalcResult::new_args_number_error(cell);
|
||||||
|
}
|
||||||
|
let n = match self.get_number(&args[0], cell) {
|
||||||
|
Ok(f) => f.floor(),
|
||||||
|
Err(s) => return s,
|
||||||
|
};
|
||||||
|
let k = match self.get_number(&args[1], cell) {
|
||||||
|
Ok(f) => f.floor(),
|
||||||
|
Err(s) => return s,
|
||||||
|
};
|
||||||
|
if n < 0.0 || k < 0.0 || (n == 0.0 && k > 0.0) {
|
||||||
|
return CalcResult::Error {
|
||||||
|
error: Error::NUM,
|
||||||
|
origin: cell,
|
||||||
|
message: "Arguments must be non-negative integers".to_string(),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
let k = k as usize;
|
||||||
|
let mut result = 1.0;
|
||||||
|
for i in 0..k {
|
||||||
|
let t = i as f64;
|
||||||
|
result *= (n + t) / (t + 1.0);
|
||||||
|
}
|
||||||
|
CalcResult::Number(result)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -111,6 +111,9 @@ pub enum Function {
|
|||||||
Decimal,
|
Decimal,
|
||||||
Roman,
|
Roman,
|
||||||
Arabic,
|
Arabic,
|
||||||
|
Combin,
|
||||||
|
Combina,
|
||||||
|
Sumsq,
|
||||||
|
|
||||||
// Information
|
// Information
|
||||||
ErrorType,
|
ErrorType,
|
||||||
@@ -305,7 +308,7 @@ pub enum Function {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl Function {
|
impl Function {
|
||||||
pub fn into_iter() -> IntoIter<Function, 249> {
|
pub fn into_iter() -> IntoIter<Function, 252> {
|
||||||
[
|
[
|
||||||
Function::And,
|
Function::And,
|
||||||
Function::False,
|
Function::False,
|
||||||
@@ -556,6 +559,9 @@ impl Function {
|
|||||||
Function::Subtotal,
|
Function::Subtotal,
|
||||||
Function::Roman,
|
Function::Roman,
|
||||||
Function::Arabic,
|
Function::Arabic,
|
||||||
|
Function::Combin,
|
||||||
|
Function::Combina,
|
||||||
|
Function::Sumsq,
|
||||||
]
|
]
|
||||||
.into_iter()
|
.into_iter()
|
||||||
}
|
}
|
||||||
@@ -607,6 +613,7 @@ impl Function {
|
|||||||
Function::Base => "_xlfn.BASE".to_string(),
|
Function::Base => "_xlfn.BASE".to_string(),
|
||||||
Function::Decimal => "_xlfn.DECIMAL".to_string(),
|
Function::Decimal => "_xlfn.DECIMAL".to_string(),
|
||||||
Function::Arabic => "_xlfn.ARABIC".to_string(),
|
Function::Arabic => "_xlfn.ARABIC".to_string(),
|
||||||
|
Function::Combina => "_xlfn.COMBINA".to_string(),
|
||||||
|
|
||||||
_ => self.to_string(),
|
_ => self.to_string(),
|
||||||
}
|
}
|
||||||
@@ -696,6 +703,9 @@ impl Function {
|
|||||||
"SUM" => Some(Function::Sum),
|
"SUM" => Some(Function::Sum),
|
||||||
"SUMIF" => Some(Function::Sumif),
|
"SUMIF" => Some(Function::Sumif),
|
||||||
"SUMIFS" => Some(Function::Sumifs),
|
"SUMIFS" => Some(Function::Sumifs),
|
||||||
|
"COMBIN" => Some(Function::Combin),
|
||||||
|
"COMBINA" | "_XLFN.COMBINA" => Some(Function::Combina),
|
||||||
|
"SUMSQ" => Some(Function::Sumsq),
|
||||||
|
|
||||||
// Lookup and Reference
|
// Lookup and Reference
|
||||||
"CHOOSE" => Some(Function::Choose),
|
"CHOOSE" => Some(Function::Choose),
|
||||||
@@ -1141,6 +1151,9 @@ impl fmt::Display for Function {
|
|||||||
Function::Decimal => write!(f, "DECIMAL"),
|
Function::Decimal => write!(f, "DECIMAL"),
|
||||||
Function::Roman => write!(f, "ROMAN"),
|
Function::Roman => write!(f, "ROMAN"),
|
||||||
Function::Arabic => write!(f, "ARABIC"),
|
Function::Arabic => write!(f, "ARABIC"),
|
||||||
|
Function::Combin => write!(f, "COMBIN"),
|
||||||
|
Function::Combina => write!(f, "COMBINA"),
|
||||||
|
Function::Sumsq => write!(f, "SUMSQ"),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1418,6 +1431,9 @@ impl Model {
|
|||||||
Function::Decimal => self.fn_decimal(args, cell),
|
Function::Decimal => self.fn_decimal(args, cell),
|
||||||
Function::Roman => self.fn_roman(args, cell),
|
Function::Roman => self.fn_roman(args, cell),
|
||||||
Function::Arabic => self.fn_arabic(args, cell),
|
Function::Arabic => self.fn_arabic(args, cell),
|
||||||
|
Function::Combin => self.fn_combin(args, cell),
|
||||||
|
Function::Combina => self.fn_combina(args, cell),
|
||||||
|
Function::Sumsq => self.fn_sumsq(args, cell),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
BIN
xlsx/tests/calc_tests/COMBIN_COMBINA.xlsx
Normal file
BIN
xlsx/tests/calc_tests/COMBIN_COMBINA.xlsx
Normal file
Binary file not shown.
BIN
xlsx/tests/calc_tests/SUMSQ.xlsx
Normal file
BIN
xlsx/tests/calc_tests/SUMSQ.xlsx
Normal file
Binary file not shown.
Reference in New Issue
Block a user