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::Roman => args_signature_scalars(arg_count, 1, 1),
|
||||
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::Roman => 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)
|
||||
}
|
||||
|
||||
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 {
|
||||
if args.is_empty() {
|
||||
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,
|
||||
Roman,
|
||||
Arabic,
|
||||
Combin,
|
||||
Combina,
|
||||
Sumsq,
|
||||
|
||||
// Information
|
||||
ErrorType,
|
||||
@@ -305,7 +308,7 @@ pub enum Function {
|
||||
}
|
||||
|
||||
impl Function {
|
||||
pub fn into_iter() -> IntoIter<Function, 249> {
|
||||
pub fn into_iter() -> IntoIter<Function, 252> {
|
||||
[
|
||||
Function::And,
|
||||
Function::False,
|
||||
@@ -556,6 +559,9 @@ impl Function {
|
||||
Function::Subtotal,
|
||||
Function::Roman,
|
||||
Function::Arabic,
|
||||
Function::Combin,
|
||||
Function::Combina,
|
||||
Function::Sumsq,
|
||||
]
|
||||
.into_iter()
|
||||
}
|
||||
@@ -607,6 +613,7 @@ impl Function {
|
||||
Function::Base => "_xlfn.BASE".to_string(),
|
||||
Function::Decimal => "_xlfn.DECIMAL".to_string(),
|
||||
Function::Arabic => "_xlfn.ARABIC".to_string(),
|
||||
Function::Combina => "_xlfn.COMBINA".to_string(),
|
||||
|
||||
_ => self.to_string(),
|
||||
}
|
||||
@@ -696,6 +703,9 @@ impl Function {
|
||||
"SUM" => Some(Function::Sum),
|
||||
"SUMIF" => Some(Function::Sumif),
|
||||
"SUMIFS" => Some(Function::Sumifs),
|
||||
"COMBIN" => Some(Function::Combin),
|
||||
"COMBINA" | "_XLFN.COMBINA" => Some(Function::Combina),
|
||||
"SUMSQ" => Some(Function::Sumsq),
|
||||
|
||||
// Lookup and Reference
|
||||
"CHOOSE" => Some(Function::Choose),
|
||||
@@ -1141,6 +1151,9 @@ impl fmt::Display for Function {
|
||||
Function::Decimal => write!(f, "DECIMAL"),
|
||||
Function::Roman => write!(f, "ROMAN"),
|
||||
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::Roman => self.fn_roman(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),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user