UPDATE: Adds LOG10 and LN for Elsa
This commit is contained in:
committed by
Nicolás Hatcher Andrés
parent
df913e73d4
commit
2a5f001361
@@ -314,6 +314,9 @@ impl Lexer {
|
|||||||
} else if name_upper == self.language.booleans.r#false {
|
} else if name_upper == self.language.booleans.r#false {
|
||||||
return TokenType::Boolean(false);
|
return TokenType::Boolean(false);
|
||||||
}
|
}
|
||||||
|
if self.peek_char() == Some('(') {
|
||||||
|
return TokenType::Ident(name);
|
||||||
|
}
|
||||||
if self.mode == LexerMode::A1 {
|
if self.mode == LexerMode::A1 {
|
||||||
let parsed_reference = utils::parse_reference_a1(&name_upper);
|
let parsed_reference = utils::parse_reference_a1(&name_upper);
|
||||||
if parsed_reference.is_some()
|
if parsed_reference.is_some()
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
#![allow(clippy::unwrap_used)]
|
#![allow(clippy::unwrap_used)]
|
||||||
|
|
||||||
|
use crate::expressions::utils::column_to_number;
|
||||||
use crate::language::get_language;
|
use crate::language::get_language;
|
||||||
use crate::locale::get_locale;
|
use crate::locale::get_locale;
|
||||||
|
|
||||||
@@ -685,3 +686,29 @@ fn test_comparisons() {
|
|||||||
assert_eq!(lx.next_token(), Number(7.0));
|
assert_eq!(lx.next_token(), Number(7.0));
|
||||||
assert_eq!(lx.next_token(), EOF);
|
assert_eq!(lx.next_token(), EOF);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_log10_is_cell_reference() {
|
||||||
|
let mut lx = new_lexer("LOG10", true);
|
||||||
|
assert_eq!(
|
||||||
|
lx.next_token(),
|
||||||
|
Reference {
|
||||||
|
sheet: None,
|
||||||
|
column: column_to_number("LOG").unwrap(),
|
||||||
|
row: 10,
|
||||||
|
absolute_column: false,
|
||||||
|
absolute_row: false,
|
||||||
|
}
|
||||||
|
);
|
||||||
|
assert_eq!(lx.next_token(), EOF);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_log10_is_function() {
|
||||||
|
let mut lx = new_lexer("LOG10(100)", true);
|
||||||
|
assert_eq!(lx.next_token(), Ident("LOG10".to_string()));
|
||||||
|
assert_eq!(lx.next_token(), LeftParenthesis);
|
||||||
|
assert_eq!(lx.next_token(), Number(100.0));
|
||||||
|
assert_eq!(lx.next_token(), RightParenthesis);
|
||||||
|
assert_eq!(lx.next_token(), EOF);
|
||||||
|
}
|
||||||
|
|||||||
@@ -609,6 +609,9 @@ fn get_function_args_signature(kind: &Function, arg_count: usize) -> Vec<Signatu
|
|||||||
Function::Choose => vec![Signature::Scalar; arg_count],
|
Function::Choose => vec![Signature::Scalar; arg_count],
|
||||||
Function::Column => args_signature_row(arg_count),
|
Function::Column => args_signature_row(arg_count),
|
||||||
Function::Columns => args_signature_one_vector(arg_count),
|
Function::Columns => args_signature_one_vector(arg_count),
|
||||||
|
Function::Ln => args_signature_scalars(arg_count, 1, 0),
|
||||||
|
Function::Log => args_signature_scalars(arg_count, 1, 1),
|
||||||
|
Function::Log10 => args_signature_scalars(arg_count, 1, 0),
|
||||||
Function::Cos => args_signature_scalars(arg_count, 1, 0),
|
Function::Cos => args_signature_scalars(arg_count, 1, 0),
|
||||||
Function::Cosh => args_signature_scalars(arg_count, 1, 0),
|
Function::Cosh => args_signature_scalars(arg_count, 1, 0),
|
||||||
Function::Max => vec![Signature::Vector; arg_count],
|
Function::Max => vec![Signature::Vector; arg_count],
|
||||||
@@ -820,6 +823,9 @@ fn static_analysis_on_function(kind: &Function, args: &[Node]) -> StaticResult {
|
|||||||
Function::Round => scalar_arguments(args),
|
Function::Round => scalar_arguments(args),
|
||||||
Function::Rounddown => scalar_arguments(args),
|
Function::Rounddown => scalar_arguments(args),
|
||||||
Function::Roundup => scalar_arguments(args),
|
Function::Roundup => scalar_arguments(args),
|
||||||
|
Function::Ln => scalar_arguments(args),
|
||||||
|
Function::Log => scalar_arguments(args),
|
||||||
|
Function::Log10 => scalar_arguments(args),
|
||||||
Function::Sin => scalar_arguments(args),
|
Function::Sin => scalar_arguments(args),
|
||||||
Function::Sinh => scalar_arguments(args),
|
Function::Sinh => scalar_arguments(args),
|
||||||
Function::Sqrt => scalar_arguments(args),
|
Function::Sqrt => scalar_arguments(args),
|
||||||
|
|||||||
@@ -211,4 +211,6 @@ fn test_names() {
|
|||||||
assert!(!is_valid_identifier("test€"));
|
assert!(!is_valid_identifier("test€"));
|
||||||
assert!(!is_valid_identifier("truñe"));
|
assert!(!is_valid_identifier("truñe"));
|
||||||
assert!(!is_valid_identifier("tr&ue"));
|
assert!(!is_valid_identifier("tr&ue"));
|
||||||
|
|
||||||
|
assert!(!is_valid_identifier("LOG10"));
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -378,6 +378,16 @@ impl Model {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
single_number_fn!(fn_log10, |f| if f <= 0.0 {
|
||||||
|
Err(Error::NUM)
|
||||||
|
} else {
|
||||||
|
Ok(f64::log10(f))
|
||||||
|
});
|
||||||
|
single_number_fn!(fn_ln, |f| if f <= 0.0 {
|
||||||
|
Err(Error::NUM)
|
||||||
|
} else {
|
||||||
|
Ok(f64::ln(f))
|
||||||
|
});
|
||||||
single_number_fn!(fn_sin, |f| Ok(f64::sin(f)));
|
single_number_fn!(fn_sin, |f| Ok(f64::sin(f)));
|
||||||
single_number_fn!(fn_cos, |f| Ok(f64::cos(f)));
|
single_number_fn!(fn_cos, |f| Ok(f64::cos(f)));
|
||||||
single_number_fn!(fn_tan, |f| Ok(f64::tan(f)));
|
single_number_fn!(fn_tan, |f| Ok(f64::tan(f)));
|
||||||
@@ -431,6 +441,47 @@ impl Model {
|
|||||||
CalcResult::Number(f64::atan2(y, x))
|
CalcResult::Number(f64::atan2(y, x))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub(crate) fn fn_log(&mut self, args: &[Node], cell: CellReferenceIndex) -> CalcResult {
|
||||||
|
let n_args = args.len();
|
||||||
|
if !(1..=2).contains(&n_args) {
|
||||||
|
return CalcResult::new_args_number_error(cell);
|
||||||
|
}
|
||||||
|
let x = match self.get_number(&args[0], cell) {
|
||||||
|
Ok(f) => f,
|
||||||
|
Err(s) => return s,
|
||||||
|
};
|
||||||
|
let y = if n_args == 1 {
|
||||||
|
10.0
|
||||||
|
} else {
|
||||||
|
match self.get_number(&args[1], cell) {
|
||||||
|
Ok(f) => f,
|
||||||
|
Err(s) => return s,
|
||||||
|
}
|
||||||
|
};
|
||||||
|
if x <= 0.0 {
|
||||||
|
return CalcResult::Error {
|
||||||
|
error: Error::NUM,
|
||||||
|
origin: cell,
|
||||||
|
message: "Number must be positive".to_string(),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
if y == 1.0 {
|
||||||
|
return CalcResult::Error {
|
||||||
|
error: Error::DIV,
|
||||||
|
origin: cell,
|
||||||
|
message: "Logarithm base cannot be 1".to_string(),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
if y <= 0.0 {
|
||||||
|
return CalcResult::Error {
|
||||||
|
error: Error::NUM,
|
||||||
|
origin: cell,
|
||||||
|
message: "Logarithm base must be positive".to_string(),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
CalcResult::Number(f64::log(x, y))
|
||||||
|
}
|
||||||
|
|
||||||
pub(crate) fn fn_power(&mut self, args: &[Node], cell: CellReferenceIndex) -> CalcResult {
|
pub(crate) fn fn_power(&mut self, args: &[Node], cell: CellReferenceIndex) -> CalcResult {
|
||||||
if args.len() != 2 {
|
if args.len() != 2 {
|
||||||
return CalcResult::new_args_number_error(cell);
|
return CalcResult::new_args_number_error(cell);
|
||||||
|
|||||||
@@ -54,6 +54,9 @@ pub enum Function {
|
|||||||
Columns,
|
Columns,
|
||||||
Cos,
|
Cos,
|
||||||
Cosh,
|
Cosh,
|
||||||
|
Log,
|
||||||
|
Log10,
|
||||||
|
Ln,
|
||||||
Max,
|
Max,
|
||||||
Min,
|
Min,
|
||||||
Pi,
|
Pi,
|
||||||
@@ -250,7 +253,7 @@ pub enum Function {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl Function {
|
impl Function {
|
||||||
pub fn into_iter() -> IntoIter<Function, 195> {
|
pub fn into_iter() -> IntoIter<Function, 198> {
|
||||||
[
|
[
|
||||||
Function::And,
|
Function::And,
|
||||||
Function::False,
|
Function::False,
|
||||||
@@ -277,6 +280,9 @@ impl Function {
|
|||||||
Function::Atanh,
|
Function::Atanh,
|
||||||
Function::Abs,
|
Function::Abs,
|
||||||
Function::Pi,
|
Function::Pi,
|
||||||
|
Function::Ln,
|
||||||
|
Function::Log,
|
||||||
|
Function::Log10,
|
||||||
Function::Sqrt,
|
Function::Sqrt,
|
||||||
Function::Sqrtpi,
|
Function::Sqrtpi,
|
||||||
Function::Atan2,
|
Function::Atan2,
|
||||||
@@ -534,6 +540,10 @@ impl Function {
|
|||||||
"POWER" => Some(Function::Power),
|
"POWER" => Some(Function::Power),
|
||||||
"ATAN2" => Some(Function::Atan2),
|
"ATAN2" => Some(Function::Atan2),
|
||||||
|
|
||||||
|
"LN" => Some(Function::Ln),
|
||||||
|
"LOG" => Some(Function::Log),
|
||||||
|
"LOG10" => Some(Function::Log10),
|
||||||
|
|
||||||
"MAX" => Some(Function::Max),
|
"MAX" => Some(Function::Max),
|
||||||
"MIN" => Some(Function::Min),
|
"MIN" => Some(Function::Min),
|
||||||
"PRODUCT" => Some(Function::Product),
|
"PRODUCT" => Some(Function::Product),
|
||||||
@@ -734,6 +744,9 @@ impl fmt::Display for Function {
|
|||||||
Function::Switch => write!(f, "SWITCH"),
|
Function::Switch => write!(f, "SWITCH"),
|
||||||
Function::True => write!(f, "TRUE"),
|
Function::True => write!(f, "TRUE"),
|
||||||
Function::Xor => write!(f, "XOR"),
|
Function::Xor => write!(f, "XOR"),
|
||||||
|
Function::Log => write!(f, "LOG"),
|
||||||
|
Function::Log10 => write!(f, "LOG10"),
|
||||||
|
Function::Ln => write!(f, "LN"),
|
||||||
Function::Sin => write!(f, "SIN"),
|
Function::Sin => write!(f, "SIN"),
|
||||||
Function::Cos => write!(f, "COS"),
|
Function::Cos => write!(f, "COS"),
|
||||||
Function::Tan => write!(f, "TAN"),
|
Function::Tan => write!(f, "TAN"),
|
||||||
@@ -961,6 +974,9 @@ impl Model {
|
|||||||
Function::True => self.fn_true(args, cell),
|
Function::True => self.fn_true(args, cell),
|
||||||
Function::Xor => self.fn_xor(args, cell),
|
Function::Xor => self.fn_xor(args, cell),
|
||||||
// Math and trigonometry
|
// Math and trigonometry
|
||||||
|
Function::Log => self.fn_log(args, cell),
|
||||||
|
Function::Log10 => self.fn_log10(args, cell),
|
||||||
|
Function::Ln => self.fn_ln(args, cell),
|
||||||
Function::Sin => self.fn_sin(args, cell),
|
Function::Sin => self.fn_sin(args, cell),
|
||||||
Function::Cos => self.fn_cos(args, cell),
|
Function::Cos => self.fn_cos(args, cell),
|
||||||
Function::Tan => self.fn_tan(args, cell),
|
Function::Tan => self.fn_tan(args, cell),
|
||||||
|
|||||||
@@ -61,6 +61,9 @@ mod test_geomean;
|
|||||||
mod test_get_cell_content;
|
mod test_get_cell_content;
|
||||||
mod test_implicit_intersection;
|
mod test_implicit_intersection;
|
||||||
mod test_issue_155;
|
mod test_issue_155;
|
||||||
|
mod test_ln;
|
||||||
|
mod test_log;
|
||||||
|
mod test_log10;
|
||||||
mod test_percentage;
|
mod test_percentage;
|
||||||
mod test_set_functions_error_handling;
|
mod test_set_functions_error_handling;
|
||||||
mod test_today;
|
mod test_today;
|
||||||
|
|||||||
17
base/src/test/test_ln.rs
Normal file
17
base/src/test/test_ln.rs
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
#![allow(clippy::unwrap_used)]
|
||||||
|
|
||||||
|
use crate::test::util::new_empty_model;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn arguments() {
|
||||||
|
let mut model = new_empty_model();
|
||||||
|
model._set("A1", "=LN(100)");
|
||||||
|
model._set("A2", "=LN()");
|
||||||
|
model._set("A3", "=LN(100, 10)");
|
||||||
|
|
||||||
|
model.evaluate();
|
||||||
|
|
||||||
|
assert_eq!(model._get_text("A1"), *"4.605170186");
|
||||||
|
assert_eq!(model._get_text("A2"), *"#ERROR!");
|
||||||
|
assert_eq!(model._get_text("A3"), *"#ERROR!");
|
||||||
|
}
|
||||||
19
base/src/test/test_log.rs
Normal file
19
base/src/test/test_log.rs
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
#![allow(clippy::unwrap_used)]
|
||||||
|
|
||||||
|
use crate::test::util::new_empty_model;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn arguments() {
|
||||||
|
let mut model = new_empty_model();
|
||||||
|
model._set("A1", "=LOG(100)");
|
||||||
|
model._set("A2", "=LOG()");
|
||||||
|
model._set("A3", "=LOG(10000, 10)");
|
||||||
|
model._set("A4", "=LOG(100, 10, 1)");
|
||||||
|
|
||||||
|
model.evaluate();
|
||||||
|
|
||||||
|
assert_eq!(model._get_text("A1"), *"2");
|
||||||
|
assert_eq!(model._get_text("A2"), *"#ERROR!");
|
||||||
|
assert_eq!(model._get_text("A3"), *"4");
|
||||||
|
assert_eq!(model._get_text("A4"), *"#ERROR!");
|
||||||
|
}
|
||||||
35
base/src/test/test_log10.rs
Normal file
35
base/src/test/test_log10.rs
Normal file
@@ -0,0 +1,35 @@
|
|||||||
|
#![allow(clippy::unwrap_used)]
|
||||||
|
|
||||||
|
use crate::test::util::new_empty_model;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn arguments() {
|
||||||
|
let mut model = new_empty_model();
|
||||||
|
model._set("A1", "=LOG10(100)");
|
||||||
|
model._set("A2", "=LOG10()");
|
||||||
|
model._set("A3", "=LOG10(100, 10)");
|
||||||
|
|
||||||
|
model.evaluate();
|
||||||
|
|
||||||
|
assert_eq!(model._get_text("A1"), *"2");
|
||||||
|
assert_eq!(model._get_text("A2"), *"#ERROR!");
|
||||||
|
assert_eq!(model._get_text("A3"), *"#ERROR!");
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn cell_and_function() {
|
||||||
|
let mut model = new_empty_model();
|
||||||
|
model._set("A1", "=LOG10");
|
||||||
|
|
||||||
|
model.evaluate();
|
||||||
|
|
||||||
|
// This is the cell LOG10
|
||||||
|
assert_eq!(model._get_text("A1"), *"0");
|
||||||
|
|
||||||
|
model._set("LOG10", "1000");
|
||||||
|
model._set("A2", "=LOG10(LOG10)");
|
||||||
|
model.evaluate();
|
||||||
|
|
||||||
|
assert_eq!(model._get_text("A1"), *"1000");
|
||||||
|
assert_eq!(model._get_text("A2"), *"3");
|
||||||
|
}
|
||||||
BIN
xlsx/tests/calc_tests/LOG_LOG10_LN.xlsx
Normal file
BIN
xlsx/tests/calc_tests/LOG_LOG10_LN.xlsx
Normal file
Binary file not shown.
Reference in New Issue
Block a user