use std::fmt; use crate::{ calc_result::CalcResult, expressions::{ lexer::util::get_tokens, parser::Node, token::{Error, OpSum, TokenType}, types::CellReferenceIndex, }, model::Model, number_format::to_precision, }; /// This implements all functions with complex arguments in the standard /// NOTE: If performance is ever needed we should have a new entry in CalcResult, /// So this functions will return CalcResult::Complex(x,y, Suffix) /// and not having to parse it over and over again. #[derive(PartialEq, Debug)] enum Suffix { I, J, } impl fmt::Display for Suffix { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { match self { Suffix::I => write!(f, "i"), Suffix::J => write!(f, "j"), } } } struct Complex { x: f64, y: f64, suffix: Suffix, } impl fmt::Display for Complex { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { let x = to_precision(self.x, 15); let y = to_precision(self.y, 15); let suffix = &self.suffix; // it is a bit weird what Excel does but it seems it uses general notation for // numbers > 1e-20 and scientific notation for the rest let y_str = if y.abs() <= 9e-20 { format!("{:E}", y) } else if y == 1.0 { "".to_string() } else if y == -1.0 { "-".to_string() } else { format!("{}", y) }; let x_str = if x.abs() <= 9e-20 { format!("{:E}", x) } else { format!("{}", x) }; if y == 0.0 && x == 0.0 { write!(f, "0") } else if y == 0.0 { write!(f, "{x_str}") } else if x == 0.0 { write!(f, "{y_str}{suffix}") } else if y > 0.0 { write!(f, "{x_str}+{y_str}{suffix}") } else { write!(f, "{x_str}{y_str}{suffix}") } } } fn parse_complex_number(s: &str) -> Result<(f64, f64, Suffix), String> { // Check for i, j, -i, -j let (sign, s) = match s.strip_prefix('-') { Some(r) => (-1.0, r), None => (1.0, s), }; match s { "i" => return Ok((0.0, sign * 1.0, Suffix::I)), "j" => return Ok((0.0, sign * 1.0, Suffix::J)), _ => { // Let it go } }; // TODO: This is an overuse let tokens = get_tokens(s); // There has to be 1, 2 3, or 4 tokens // number // number suffix // number1+suffix // number1+number2 suffix match tokens.len() { 1 => { // Real number let number1 = match tokens[0].token { TokenType::Number(f) => f, _ => return Err(format!("Not a complex number: {s}")), }; // i is the default Ok((sign * number1, 0.0, Suffix::I)) } 2 => { // number2 i let number2 = match tokens[0].token { TokenType::Number(f) => f, _ => return Err(format!("Not a complex number: {s}")), }; let suffix = match &tokens[1].token { TokenType::Ident(w) => match w.as_str() { "i" => Suffix::I, "j" => Suffix::J, _ => return Err(format!("Not a complex number: {s}")), }, _ => { return Err(format!("Not a complex number: {s}")); } }; Ok((0.0, sign * number2, suffix)) } 3 => { let number1 = match tokens[0].token { TokenType::Number(f) => f, _ => return Err(format!("Not a complex number: {s}")), }; let operation = match &tokens[1].token { TokenType::Addition(f) => f, _ => return Err(format!("Not a complex number: {s}")), }; let suffix = match &tokens[2].token { TokenType::Ident(w) => match w.as_str() { "i" => Suffix::I, "j" => Suffix::J, _ => return Err(format!("Not a complex number: {s}")), }, _ => { return Err(format!("Not a complex number: {s}")); } }; let number2 = if matches!(operation, OpSum::Minus) { -1.0 } else { 1.0 }; Ok((sign * number1, number2, suffix)) } 4 => { let number1 = match tokens[0].token { TokenType::Number(f) => f, _ => return Err(format!("Not a complex number: {s}")), }; let operation = match &tokens[1].token { TokenType::Addition(f) => f, _ => return Err(format!("Not a complex number: {s}")), }; let mut number2 = match tokens[2].token { TokenType::Number(f) => f, _ => return Err(format!("Not a complex number: {s}")), }; let suffix = match &tokens[3].token { TokenType::Ident(w) => match w.as_str() { "i" => Suffix::I, "j" => Suffix::J, _ => return Err(format!("Not a complex number: {s}")), }, _ => { return Err(format!("Not a complex number: {s}")); } }; if matches!(operation, OpSum::Minus) { number2 = -number2 } Ok((sign * number1, number2, suffix)) } _ => Err(format!("Not a complex number: {s}")), } } impl Model { fn get_complex_number( &mut self, node: &Node, cell: CellReferenceIndex, ) -> Result<(f64, f64, Suffix), CalcResult> { let value = match self.get_string(node, cell) { Ok(s) => s, Err(s) => return Err(s), }; if value.is_empty() { return Ok((0.0, 0.0, Suffix::I)); } match parse_complex_number(&value) { Ok(s) => Ok(s), Err(message) => Err(CalcResult::new_error(Error::NUM, cell, message)), } } // COMPLEX(real_num, i_num, [suffix]) pub(crate) fn fn_complex(&mut self, args: &[Node], cell: CellReferenceIndex) -> CalcResult { if !(2..=3).contains(&args.len()) { return CalcResult::new_args_number_error(cell); } let x = match self.get_number(&args[0], cell) { Ok(s) => s, Err(s) => return s, }; let y = match self.get_number(&args[1], cell) { Ok(s) => s, Err(s) => return s, }; let suffix = if args.len() == 3 { match self.get_string(&args[2], cell) { Ok(s) => { if s == "i" || s.is_empty() { Suffix::I } else if s == "j" { Suffix::J } else { return CalcResult::new_error( Error::VALUE, cell, "Invalid suffix".to_string(), ); } } Err(s) => return s, } } else { Suffix::I }; let complex = Complex { x, y, suffix }; CalcResult::String(complex.to_string()) } pub(crate) fn fn_imabs(&mut self, args: &[Node], cell: CellReferenceIndex) -> CalcResult { if args.len() != 1 { return CalcResult::new_args_number_error(cell); } let (x, y, _) = match self.get_complex_number(&args[0], cell) { Ok(s) => s, Err(error) => return error, }; CalcResult::Number(f64::sqrt(x * x + y * y)) } pub(crate) fn fn_imaginary(&mut self, args: &[Node], cell: CellReferenceIndex) -> CalcResult { if args.len() != 1 { return CalcResult::new_args_number_error(cell); } let (_, y, _) = match self.get_complex_number(&args[0], cell) { Ok(s) => s, Err(error) => return error, }; CalcResult::Number(y) } pub(crate) fn fn_imargument(&mut self, args: &[Node], cell: CellReferenceIndex) -> CalcResult { if args.len() != 1 { return CalcResult::new_args_number_error(cell); } let (x, y, _) = match self.get_complex_number(&args[0], cell) { Ok(s) => s, Err(error) => return error, }; if x == 0.0 && y == 0.0 { return CalcResult::new_error(Error::DIV, cell, "Division by zero".to_string()); } let angle = f64::atan2(y, x); CalcResult::Number(angle) } pub(crate) fn fn_imconjugate(&mut self, args: &[Node], cell: CellReferenceIndex) -> CalcResult { if args.len() != 1 { return CalcResult::new_args_number_error(cell); } let (x, y, suffix) = match self.get_complex_number(&args[0], cell) { Ok(s) => s, Err(error) => return error, }; let complex = Complex { x, y: -y, suffix }; CalcResult::String(complex.to_string()) } pub(crate) fn fn_imcos(&mut self, args: &[Node], cell: CellReferenceIndex) -> CalcResult { if args.len() != 1 { return CalcResult::new_args_number_error(cell); } let value = match self.get_string(&args[0], cell) { Ok(s) => s, Err(s) => return s, }; let (x, y, suffix) = match parse_complex_number(&value) { Ok(s) => s, Err(message) => return CalcResult::new_error(Error::NUM, cell, message), }; let complex = Complex { x: x.cos() * y.cosh(), y: -x.sin() * y.sinh(), suffix, }; CalcResult::String(complex.to_string()) } pub(crate) fn fn_imcosh(&mut self, args: &[Node], cell: CellReferenceIndex) -> CalcResult { if args.len() != 1 { return CalcResult::new_args_number_error(cell); } let value = match self.get_string(&args[0], cell) { Ok(s) => s, Err(s) => return s, }; let (x, y, suffix) = match parse_complex_number(&value) { Ok(s) => s, Err(message) => return CalcResult::new_error(Error::NUM, cell, message), }; let complex = Complex { x: x.cosh() * y.cos(), y: x.sinh() * y.sin(), suffix, }; CalcResult::String(complex.to_string()) } pub(crate) fn fn_imcot(&mut self, args: &[Node], cell: CellReferenceIndex) -> CalcResult { if args.len() != 1 { return CalcResult::new_args_number_error(cell); } let value = match self.get_string(&args[0], cell) { Ok(s) => s, Err(s) => return s, }; let (x, y, suffix) = match parse_complex_number(&value) { Ok(s) => s, Err(message) => return CalcResult::new_error(Error::NUM, cell, message), }; if x == 0.0 && y != 0.0 { let complex = Complex { x: 0.0, y: -1.0 / y.tanh(), suffix, }; return CalcResult::String(complex.to_string()); } else if y == 0.0 { let complex = Complex { x: 1.0 / x.tan(), y: 0.0, suffix, }; return CalcResult::String(complex.to_string()); } let x_cot = 1.0 / x.tan(); let y_coth = 1.0 / y.tanh(); let t = x_cot * x_cot + y_coth * y_coth; let x = (x_cot * y_coth * y_coth - x_cot) / t; let y = (-x_cot * x_cot * y_coth - y_coth) / t; if x.is_infinite() || y.is_infinite() || x.is_nan() || y.is_nan() { return CalcResult::new_error(Error::NUM, cell, "Invalid operation".to_string()); } let complex = Complex { x, y, suffix }; CalcResult::String(complex.to_string()) } pub(crate) fn fn_imcsc(&mut self, args: &[Node], cell: CellReferenceIndex) -> CalcResult { if args.len() != 1 { return CalcResult::new_args_number_error(cell); } let value = match self.get_string(&args[0], cell) { Ok(s) => s, Err(s) => return s, }; let (x, y, suffix) = match parse_complex_number(&value) { Ok(s) => s, Err(message) => return CalcResult::new_error(Error::NUM, cell, message), }; let x_cos = x.cos(); let x_sin = x.sin(); let y_cosh = y.cosh(); let y_sinh = y.sinh(); let t = x_sin * x_sin * y_cosh * y_cosh + x_cos * x_cos * y_sinh * y_sinh; let complex = Complex { x: x_sin * y_cosh / t, y: -x_cos * y_sinh / t, suffix, }; CalcResult::String(complex.to_string()) } pub(crate) fn fn_imcsch(&mut self, args: &[Node], cell: CellReferenceIndex) -> CalcResult { if args.len() != 1 { return CalcResult::new_args_number_error(cell); } let value = match self.get_string(&args[0], cell) { Ok(s) => s, Err(s) => return s, }; let (x, y, suffix) = match parse_complex_number(&value) { Ok(s) => s, Err(message) => return CalcResult::new_error(Error::NUM, cell, message), }; let x_cosh = x.cosh(); let x_sinh = x.sinh(); let y_cos = y.cos(); let y_sin = y.sin(); let t = x_sinh * x_sinh * y_cos * y_cos + x_cosh * x_cosh * y_sin * y_sin; let complex = Complex { x: x_sinh * y_cos / t, y: -x_cosh * y_sin / t, suffix, }; CalcResult::String(complex.to_string()) } pub(crate) fn fn_imdiv(&mut self, args: &[Node], cell: CellReferenceIndex) -> CalcResult { if args.len() != 2 { return CalcResult::new_args_number_error(cell); } let (x1, y1, suffix) = match self.get_complex_number(&args[0], cell) { Ok(s) => s, Err(error) => return error, }; let (x2, y2, suffix2) = match self.get_complex_number(&args[1], cell) { Ok(s) => s, Err(error) => return error, }; if suffix != suffix2 { return CalcResult::new_error(Error::VALUE, cell, "Different suffixes".to_string()); } let t = x2 * x2 + y2 * y2; if t == 0.0 { return CalcResult::new_error(Error::NUM, cell, "Invalid".to_string()); } let complex = Complex { x: (x1 * x2 + y1 * y2) / t, y: (-x1 * y2 + y1 * x2) / t, suffix, }; CalcResult::String(complex.to_string()) } pub(crate) fn fn_imexp(&mut self, args: &[Node], cell: CellReferenceIndex) -> CalcResult { if args.len() != 1 { return CalcResult::new_args_number_error(cell); } let (x, y, suffix) = match self.get_complex_number(&args[0], cell) { Ok(s) => s, Err(error) => return error, }; let complex = Complex { x: x.exp() * y.cos(), y: x.exp() * y.sin(), suffix, }; CalcResult::String(complex.to_string()) } pub(crate) fn fn_imln(&mut self, args: &[Node], cell: CellReferenceIndex) -> CalcResult { if args.len() != 1 { return CalcResult::new_args_number_error(cell); } let (x, y, suffix) = match self.get_complex_number(&args[0], cell) { Ok(s) => s, Err(error) => return error, }; let r = f64::sqrt(x * x + y * y); let a = f64::atan2(y, x); let complex = Complex { x: r.ln(), y: a, suffix, }; CalcResult::String(complex.to_string()) } pub(crate) fn fn_imlog10(&mut self, args: &[Node], cell: CellReferenceIndex) -> CalcResult { if args.len() != 1 { return CalcResult::new_args_number_error(cell); } let (x, y, suffix) = match self.get_complex_number(&args[0], cell) { Ok(s) => s, Err(error) => return error, }; let r = f64::sqrt(x * x + y * y); let a = f64::atan2(y, x); let complex = Complex { x: r.log10(), y: a * f64::log10(f64::exp(1.0)), suffix, }; CalcResult::String(complex.to_string()) } pub(crate) fn fn_imlog2(&mut self, args: &[Node], cell: CellReferenceIndex) -> CalcResult { if args.len() != 1 { return CalcResult::new_args_number_error(cell); } let (x, y, suffix) = match self.get_complex_number(&args[0], cell) { Ok(s) => s, Err(error) => return error, }; let r = f64::sqrt(x * x + y * y); let a = f64::atan2(y, x); let complex = Complex { x: r.log2(), y: a * f64::log2(f64::exp(1.0)), suffix, }; CalcResult::String(complex.to_string()) } // IMPOWER(imnumber, power) // If $(r, \theta)$ is the polar representation the formula is: // $$ x = r^n*\cos(n\dot\theta), y = r^n*\csin(n\dot\theta) $ pub(crate) fn fn_impower(&mut self, args: &[Node], cell: CellReferenceIndex) -> CalcResult { if args.len() != 2 { return CalcResult::new_args_number_error(cell); } let (x, y, suffix) = match self.get_complex_number(&args[0], cell) { Ok(s) => s, Err(error) => return error, }; let n = match self.get_number_no_bools(&args[1], cell) { Ok(f) => f, Err(s) => return s, }; // if n == n.trunc() && n < 10.0 { // // for small powers we compute manually // let (mut x0, mut y0) = (x, y); // for _ in 1..(n.trunc() as i32) { // (x0, y0) = (x0 * x - y0 * y, x0 * y + y0 * x); // } // let complex = Complex { // x: x0, // y: y0, // suffix, // }; // return CalcResult::String(complex.to_string()); // }; let r = f64::sqrt(x * x + y * y); let a = f64::atan2(y, x); let x = r.powf(n) * f64::cos(a * n); let y = r.powf(n) * f64::sin(a * n); if x.is_infinite() || y.is_infinite() || x.is_nan() || y.is_nan() { return CalcResult::new_error(Error::NUM, cell, "Invalid operation".to_string()); } let complex = Complex { x, y, suffix }; CalcResult::String(complex.to_string()) } pub(crate) fn fn_improduct(&mut self, args: &[Node], cell: CellReferenceIndex) -> CalcResult { if args.len() != 2 { return CalcResult::new_args_number_error(cell); } let (x1, y1, suffix) = match self.get_complex_number(&args[0], cell) { Ok(s) => s, Err(error) => return error, }; let (x2, y2, suffix2) = match self.get_complex_number(&args[1], cell) { Ok(s) => s, Err(error) => return error, }; if suffix != suffix2 { return CalcResult::new_error(Error::VALUE, cell, "Different suffixes".to_string()); } let complex = Complex { x: x1 * x2 - y1 * y2, y: x1 * y2 + y1 * x2, suffix, }; CalcResult::String(complex.to_string()) } pub(crate) fn fn_imreal(&mut self, args: &[Node], cell: CellReferenceIndex) -> CalcResult { if args.len() != 1 { return CalcResult::new_args_number_error(cell); } let (x, _, _) = match self.get_complex_number(&args[0], cell) { Ok(s) => s, Err(error) => return error, }; CalcResult::Number(x) } pub(crate) fn fn_imsec(&mut self, args: &[Node], cell: CellReferenceIndex) -> CalcResult { if args.len() != 1 { return CalcResult::new_args_number_error(cell); } let (x, y, suffix) = match self.get_complex_number(&args[0], cell) { Ok(s) => s, Err(error) => return error, }; let x_cos = x.cos(); let x_sin = x.sin(); let y_cosh = y.cosh(); let y_sinh = y.sinh(); let t = x_cos * x_cos * y_cosh * y_cosh + x_sin * x_sin * y_sinh * y_sinh; let complex = Complex { x: x_cos * y_cosh / t, y: x_sin * y_sinh / t, suffix, }; CalcResult::String(complex.to_string()) } pub(crate) fn fn_imsech(&mut self, args: &[Node], cell: CellReferenceIndex) -> CalcResult { if args.len() != 1 { return CalcResult::new_args_number_error(cell); } let (x, y, suffix) = match self.get_complex_number(&args[0], cell) { Ok(s) => s, Err(error) => return error, }; let x_cosh = x.cosh(); let x_sinh = x.sinh(); let y_cos = y.cos(); let y_sin = y.sin(); let t = x_cosh * x_cosh * y_cos * y_cos + x_sinh * x_sinh * y_sin * y_sin; let complex = Complex { x: x_cosh * y_cos / t, y: -x_sinh * y_sin / t, suffix, }; CalcResult::String(complex.to_string()) } pub(crate) fn fn_imsin(&mut self, args: &[Node], cell: CellReferenceIndex) -> CalcResult { if args.len() != 1 { return CalcResult::new_args_number_error(cell); } let (x, y, suffix) = match self.get_complex_number(&args[0], cell) { Ok(s) => s, Err(error) => return error, }; let complex = Complex { x: x.sin() * y.cosh(), y: x.cos() * y.sinh(), suffix, }; CalcResult::String(complex.to_string()) } pub(crate) fn fn_imsinh(&mut self, args: &[Node], cell: CellReferenceIndex) -> CalcResult { if args.len() != 1 { return CalcResult::new_args_number_error(cell); } let (x, y, suffix) = match self.get_complex_number(&args[0], cell) { Ok(s) => s, Err(error) => return error, }; let complex = Complex { x: x.sinh() * y.cos(), y: x.cosh() * y.sin(), suffix, }; CalcResult::String(complex.to_string()) } pub(crate) fn fn_imsqrt(&mut self, args: &[Node], cell: CellReferenceIndex) -> CalcResult { if args.len() != 1 { return CalcResult::new_args_number_error(cell); } let (x, y, suffix) = match self.get_complex_number(&args[0], cell) { Ok(s) => s, Err(error) => return error, }; let r = f64::sqrt(x * x + y * y).sqrt(); let a = f64::atan2(y, x); let complex = Complex { x: r * f64::cos(a / 2.0), y: r * f64::sin(a / 2.0), suffix, }; CalcResult::String(complex.to_string()) } pub(crate) fn fn_imsub(&mut self, args: &[Node], cell: CellReferenceIndex) -> CalcResult { if args.len() != 2 { return CalcResult::new_args_number_error(cell); } let (x1, y1, suffix) = match self.get_complex_number(&args[0], cell) { Ok(s) => s, Err(error) => return error, }; let (x2, y2, suffix2) = match self.get_complex_number(&args[1], cell) { Ok(s) => s, Err(error) => return error, }; if suffix != suffix2 { return CalcResult::new_error(Error::VALUE, cell, "Different suffixes".to_string()); } let complex = Complex { x: x1 - x2, y: y1 - y2, suffix, }; CalcResult::String(complex.to_string()) } pub(crate) fn fn_imsum(&mut self, args: &[Node], cell: CellReferenceIndex) -> CalcResult { if args.len() != 2 { return CalcResult::new_args_number_error(cell); } let (x1, y1, suffix) = match self.get_complex_number(&args[0], cell) { Ok(s) => s, Err(error) => return error, }; let (x2, y2, suffix2) = match self.get_complex_number(&args[1], cell) { Ok(s) => s, Err(error) => return error, }; if suffix != suffix2 { return CalcResult::new_error(Error::VALUE, cell, "Different suffixes".to_string()); } let complex = Complex { x: x1 + x2, y: y1 + y2, suffix, }; CalcResult::String(complex.to_string()) } pub(crate) fn fn_imtan(&mut self, args: &[Node], cell: CellReferenceIndex) -> CalcResult { if args.len() != 1 { return CalcResult::new_args_number_error(cell); } let (x, y, suffix) = match self.get_complex_number(&args[0], cell) { Ok(s) => s, Err(error) => return error, }; let x_tan = x.tan(); let y_tanh = y.tanh(); let t = 1.0 + x_tan * x_tan * y_tanh * y_tanh; let complex = Complex { x: (x_tan - x_tan * y_tanh * y_tanh) / t, y: (y_tanh + x_tan * x_tan * y_tanh) / t, suffix, }; CalcResult::String(complex.to_string()) } } #[cfg(test)] mod tests { use crate::functions::engineering::complex::Suffix; use super::parse_complex_number as parse; #[test] fn test_parse_complex() { assert_eq!(parse("1+2i"), Ok((1.0, 2.0, Suffix::I))); assert_eq!(parse("2i"), Ok((0.0, 2.0, Suffix::I))); assert_eq!(parse("7.5"), Ok((7.5, 0.0, Suffix::I))); assert_eq!(parse("-7.5"), Ok((-7.5, 0.0, Suffix::I))); assert_eq!(parse("7-5i"), Ok((7.0, -5.0, Suffix::I))); assert_eq!(parse("i"), Ok((0.0, 1.0, Suffix::I))); assert_eq!(parse("7+i"), Ok((7.0, 1.0, Suffix::I))); assert_eq!(parse("7-i"), Ok((7.0, -1.0, Suffix::I))); assert_eq!(parse("-i"), Ok((0.0, -1.0, Suffix::I))); assert_eq!(parse("0"), Ok((0.0, 0.0, Suffix::I))); } }