diff --git a/kalk/src/analysis.rs b/kalk/src/analysis.rs index 0823763..242cdff 100644 --- a/kalk/src/analysis.rs +++ b/kalk/src/analysis.rs @@ -1,9 +1,9 @@ use crate::{ ast::{ConditionalPiece, Expr, Identifier, RangedVar, Stmt}, + errors::KalkError, inverter, lexer::TokenKind, - parser::{self, KalkError}, - prelude, + parser, prelude, symbol_table::SymbolTable, }; diff --git a/kalk/src/calculus.rs b/kalk/src/calculus.rs index d9f81b3..3f5dc81 100644 --- a/kalk/src/calculus.rs +++ b/kalk/src/calculus.rs @@ -3,11 +3,11 @@ use crate::ast; use crate::ast::Expr; use crate::ast::Identifier; use crate::ast::Stmt; +use crate::errors::KalkError; use crate::float; use crate::interpreter; use crate::kalk_value::KalkValue; use crate::lexer::TokenKind; -use crate::parser::KalkError; pub fn derive_func( context: &mut interpreter::Context, @@ -17,8 +17,8 @@ pub fn derive_func( const H: f64 = 0.000001; let unit = argument.get_unit().cloned(); - let argument_with_h = ast::build_literal_ast(&argument.clone().add_without_unit(&H.into())); - let argument_without_h = ast::build_literal_ast(&argument.sub_without_unit(&H.into())); + let argument_with_h = ast::build_literal_ast(&argument.clone().add_without_unit(&H.into())?); + let argument_without_h = ast::build_literal_ast(&argument.sub_without_unit(&H.into())?); let new_identifier = Identifier::from_name_and_primes(&name.pure_name, name.prime_count - 1); let f_x_h = interpreter::eval_fn_call_expr( @@ -35,8 +35,8 @@ pub fn derive_func( )?; Ok(f_x_h - .sub_without_unit(&f_x) - .div_without_unit(&(2f64 * H).into()) + .sub_without_unit(&f_x)? + .div_without_unit(&(2f64 * H).into())? .round_if_needed()) } @@ -98,11 +98,11 @@ fn simpsons_rule( const N: i32 = 900; let a = interpreter::eval_expr(context, a_expr, None)?; let b = interpreter::eval_expr(context, b_expr, None)?; - let h = (b.sub_without_unit(&a)).div_without_unit(&KalkValue::from(N)); + let h = (b.sub_without_unit(&a))?.div_without_unit(&KalkValue::from(N))?; for i in 0..=N { let variable_value = a .clone() - .add_without_unit(&KalkValue::from(i).mul_without_unit(&h.clone())); + .add_without_unit(&KalkValue::from(i).mul_without_unit(&h.clone())?)?; context.symbol_table.set(Stmt::VarDecl( Identifier::from_full_name(integration_variable), Box::new(crate::ast::build_literal_ast(&variable_value)), @@ -116,7 +116,7 @@ fn simpsons_rule( // factor * f(x_n) let (mul_real, mul_imaginary, _) = as_number_or_zero!( - factor.mul_without_unit(&interpreter::eval_expr(context, expr, None)?) + factor.mul_without_unit(&interpreter::eval_expr(context, expr, None)?)? ); result_real += mul_real; result_imaginary += mul_imaginary; @@ -133,11 +133,11 @@ fn simpsons_rule( let result = KalkValue::Number(result_real, result_imaginary, None); let (h_real, h_imaginary, h_unit) = as_number_or_zero!(h); - Ok(result.mul_without_unit(&KalkValue::Number( + result.mul_without_unit(&KalkValue::Number( 3f64 / 8f64 * h_real, 3f64 / 8f64 * h_imaginary, h_unit, - ))) + )) } #[cfg(test)] diff --git a/kalk/src/errors.rs b/kalk/src/errors.rs new file mode 100644 index 0000000..71f09af --- /dev/null +++ b/kalk/src/errors.rs @@ -0,0 +1,83 @@ +use crate::lexer::TokenKind; + +/// Error that occured during parsing or evaluation. +#[derive(Debug, Clone, PartialEq)] +pub enum KalkError { + CannotIndexByImaginary, + CanOnlyIndexX, + Expected(String), + ExpectedDx, + ExpectedIf, + ExpectedReal, + IncompatibleTypesForOperation(String, String, String), + IncompatibleVectorsMatrixes, + IncorrectAmountOfArguments(usize, String, usize), + IncorrectAmountOfIndexes(usize, usize), + ItemOfIndexDoesNotExist(Vec), + InconsistentColumnWidths, + InvalidComprehension(String), + InvalidNumberLiteral(String), + InvalidOperator, + InvalidUnit, + TimedOut, + VariableReferencesItself, + PiecewiseConditionsAreFalse, + EvaluationError(String), + UnexpectedToken(TokenKind, TokenKind), + UnexpectedType(String, Vec), + UndefinedFn(String), + UndefinedVar(String), + UnableToInvert(String), + UnableToSolveEquation, + UnableToOverrideConstant(String), + UnableToParseExpression, + UnrecognizedBase, + Unknown, +} + +impl ToString for KalkError { + fn to_string(&self) -> String { + match self { + KalkError::CannotIndexByImaginary => String::from("Cannot index by imaginary numbers."), + KalkError::CanOnlyIndexX => String::from("Indexing (getting an item with a specific index) is only possible on vectors and matrices."), + KalkError::Expected(description) => format!("Expected: {}", description), + KalkError::ExpectedDx => String::from("Expected eg. dx, to specify for which variable the operation is being done to. Example with integration: ∫(0, 1, x dx) or ∫(0, 1, x, dx). You may need to put parenthesis around the expression before dx/dy/du/etc."), + KalkError::ExpectedIf => String::from("Expected 'if', with a condition after it."), + KalkError::ExpectedReal => String::from("Expected a real value but got imaginary."), + KalkError::IncompatibleTypesForOperation(operation, got1, got2) => format!("Incompatible types for operation '{}': {} and {}.", operation, got1, got2), + KalkError::IncompatibleVectorsMatrixes => String::from("Incompatible vectors/matrixes."), + KalkError::IncorrectAmountOfArguments(expected, func, got) => format!( + "Expected {} arguments for function {}, but got {}.", + expected, func, got + ), + KalkError::IncorrectAmountOfIndexes(expected, got) => format!( + "Expected {} indexes but got {}.", + expected, got + ), + KalkError::ItemOfIndexDoesNotExist(indices) => format!("Item of index ⟦{}⟧ does not exist.", indices.iter().map(|x| x.to_string()).collect::>().join(", ")), + KalkError::InconsistentColumnWidths => String::from("Inconsistent column widths. Matrix columns must be the same size."), + KalkError::InvalidComprehension(x) => format!("Invalid comprehension: {}", x), + KalkError::InvalidNumberLiteral(x) => format!("Invalid number literal: '{}'.", x), + KalkError::InvalidOperator => String::from("Invalid operator."), + KalkError::InvalidUnit => String::from("Invalid unit."), + KalkError::TimedOut => String::from("Operation took too long."), + KalkError::VariableReferencesItself => String::from("Variable references itself."), + KalkError::PiecewiseConditionsAreFalse => String::from("All the conditions in the piecewise are false."), + KalkError::EvaluationError(msg) => format!("Evaluation error: {}", msg), + KalkError::UnexpectedToken(got, expected) => { + format!("Unexpected token: '{:?}', expected '{:?}'.", got, expected) + } + KalkError::UnexpectedType(got, expected) => { + format!("Unexpected type. Got {:?} but expected: {:?}.", got, expected.join(", ")) + } + KalkError::UnableToInvert(msg) => format!("Unable to invert: {}", msg), + KalkError::UndefinedFn(name) => format!("Undefined function: '{}'.", name), + KalkError::UndefinedVar(name) => format!("Undefined variable: '{}'.", name), + KalkError::UnableToParseExpression => String::from("Unable to parse expression."), + KalkError::UnableToSolveEquation => String::from("Unable to solve equation."), + KalkError::UnableToOverrideConstant(name) => format!("Unable to override constant: '{}'.", name), + KalkError::UnrecognizedBase => String::from("Unrecognized base."), + KalkError::Unknown => String::from("Unknown error."), + } + } +} diff --git a/kalk/src/interpreter.rs b/kalk/src/interpreter.rs index 929b6d7..276aa51 100644 --- a/kalk/src/interpreter.rs +++ b/kalk/src/interpreter.rs @@ -1,9 +1,9 @@ use crate::ast::{Expr, Stmt}; use crate::ast::{Identifier, RangedVar}; use crate::calculation_result::CalculationResult; +use crate::errors::KalkError; use crate::kalk_value::KalkValue; use crate::lexer::TokenKind; -use crate::parser::KalkError; use crate::parser::DECL_UNIT; use crate::symbol_table::SymbolTable; use crate::{as_number_or_zero, calculus}; @@ -163,7 +163,7 @@ fn eval_binary_expr( let left = eval_expr(context, left_expr, None)?; let mut right = eval_expr(context, right_expr, None)?; if let Expr::Unary(TokenKind::Percent, _) = right_expr { - right = right.mul(context, left.clone()); + right = right.mul(context, left.clone())?; if let TokenKind::Star = op { return Ok(right); } @@ -184,8 +184,8 @@ fn eval_binary_expr( TokenKind::LessOrEquals => left.less_or_equals(context, right), TokenKind::And => left.and(&right), TokenKind::Or => left.or(&right), - _ => KalkValue::from(1f64), - }; + _ => Ok(KalkValue::from(1f64)), + }?; if unit.is_some() { if let KalkValue::Number(real, imaginary, _) = result { @@ -205,9 +205,9 @@ fn eval_unary_expr( let num = eval_expr(context, expr, unit)?; match op { - TokenKind::Minus => Ok(num.mul(context, KalkValue::from(-1f64))), - TokenKind::Percent => Ok(num.mul(context, KalkValue::from(0.01f64))), - TokenKind::Exclamation => Ok(prelude::special_funcs::factorial(num)), + TokenKind::Minus => num.mul(context, KalkValue::from(-1f64)), + TokenKind::Percent => num.mul(context, KalkValue::from(0.01f64)), + TokenKind::Exclamation => prelude::special_funcs::factorial(num), _ => Err(KalkError::InvalidOperator), } } @@ -397,10 +397,8 @@ pub(crate) fn eval_fn_call_expr( values.push(value); } - return Ok( - prelude::call_vector_func(&identifier.full_name, KalkValue::Vector(values)) - .unwrap_or_else(KalkValue::nan), - ); + return prelude::call_vector_func(&identifier.full_name, KalkValue::Vector(values)) + .unwrap_or_else(|| Ok(KalkValue::nan())); } // Prelude @@ -433,9 +431,7 @@ pub(crate) fn eval_fn_call_expr( }; if let Some((result, _)) = prelude_func { - // If the result is nan and only one argument was given, - // it may be due to incompatible types. - if result.is_nan() && expressions.len() == 1 { + if result.is_err() && expressions.len() == 1 { let x = eval_expr(context, &expressions[0], None)?; // If a vector/matrix was given, call the function on every item @@ -450,7 +446,7 @@ pub(crate) fn eval_fn_call_expr( value, &context.angle_unit.clone(), ) { - new_values.push(result.0); + new_values.push(result.0?); } else { success = false; break; @@ -472,7 +468,7 @@ pub(crate) fn eval_fn_call_expr( value, &context.angle_unit.clone(), ) { - new_row.push(result.0); + new_row.push(result.0?); } else { success = false; break; @@ -488,8 +484,9 @@ pub(crate) fn eval_fn_call_expr( } } - return Ok(result); + return result; } + // Symbol Table let stmt_definition = context.symbol_table.get_fn(&identifier.full_name).cloned(); @@ -593,9 +590,9 @@ fn eval_loop( let eval = eval_expr(context, expression, None)?; if sum_else_prod { - sum = sum.add(context, eval); + sum = sum.add(context, eval)?; } else { - sum = sum.mul(context, eval); + sum = sum.mul(context, eval)?; } } @@ -829,7 +826,7 @@ mod tests { } #[cfg(not(feature = "rug"))] - fn context<'a>(symbol_table: &'a mut SymbolTable, angle_unit: Option<&String>) -> Context<'a> { + fn context<'a>(symbol_table: &'a mut SymbolTable, angle_unit: &str) -> Context<'a> { Context::new(symbol_table, angle_unit, None) } diff --git a/kalk/src/inverter.rs b/kalk/src/inverter.rs index 05c3204..8d7a391 100644 --- a/kalk/src/inverter.rs +++ b/kalk/src/inverter.rs @@ -1,7 +1,7 @@ use crate::ast::Identifier; use crate::ast::{Expr, Stmt}; +use crate::errors::KalkError; use crate::lexer::TokenKind; -use crate::parser::KalkError; use crate::prelude; use crate::symbol_table::SymbolTable; use lazy_static::lazy_static; diff --git a/kalk/src/kalk_value/mod.rs b/kalk/src/kalk_value/mod.rs index cdb80a7..4a19f3f 100644 --- a/kalk/src/kalk_value/mod.rs +++ b/kalk/src/kalk_value/mod.rs @@ -14,6 +14,7 @@ pub use regular::*; mod rounding; use crate::ast::Expr; +use crate::errors::KalkError; use crate::radix; use wasm_bindgen::prelude::*; @@ -66,7 +67,10 @@ macro_rules! as_number_or_return { unit, ) } else { - return KalkValue::nan(); + return Err(KalkError::UnexpectedType( + $x.get_type_name(), + vec![String::from("number")], + )); } }}; } @@ -76,12 +80,15 @@ macro_rules! as_vector_or_return { ($x:expr) => {{ if let KalkValue::Vector(values) = $x { if values.len() == 0 { - return KalkValue::nan(); + return Err(KalkError::Expected(String::from("a non-empty vector"))); } values } else { - return KalkValue::nan(); + return Err(KalkError::UnexpectedType( + $x.get_type_name(), + vec![String::from("vector")], + )); } }}; } @@ -230,6 +237,15 @@ impl KalkValue { KalkValue::Number(float!(f64::NAN), float!(0f64), None) } + pub fn get_type_name(&self) -> String { + match self { + KalkValue::Number(_, _, _) => String::from("number"), + KalkValue::Boolean(_) => String::from("boolean"), + KalkValue::Vector(_) => String::from("vector"), + KalkValue::Matrix(_) => String::from("matrix"), + } + } + pub fn to_string_big(&self) -> String { if let KalkValue::Number(real, imaginary, _) = self { if !self.has_imaginary() { @@ -532,7 +548,7 @@ impl KalkValue { self, context: &mut crate::interpreter::Context, rhs: KalkValue, - ) -> KalkValue { + ) -> Result { let right = calculate_unit(context, &self, rhs.clone()).unwrap_or(rhs); self.add_without_unit(&right) } @@ -541,7 +557,7 @@ impl KalkValue { self, context: &mut crate::interpreter::Context, rhs: KalkValue, - ) -> KalkValue { + ) -> Result { let right = calculate_unit(context, &self, rhs.clone()).unwrap_or(rhs); self.sub_without_unit(&right) } @@ -550,7 +566,7 @@ impl KalkValue { self, context: &mut crate::interpreter::Context, rhs: KalkValue, - ) -> KalkValue { + ) -> Result { let right = calculate_unit(context, &self, rhs.clone()).unwrap_or(rhs); self.mul_without_unit(&right) } @@ -559,7 +575,7 @@ impl KalkValue { self, context: &mut crate::interpreter::Context, rhs: KalkValue, - ) -> KalkValue { + ) -> Result { let right = calculate_unit(context, &self, rhs.clone()).unwrap_or(rhs); self.div_without_unit(&right) } @@ -568,7 +584,7 @@ impl KalkValue { self, context: &mut crate::interpreter::Context, rhs: KalkValue, - ) -> KalkValue { + ) -> Result { let right = calculate_unit(context, &self, rhs.clone()).unwrap_or(rhs); self.pow_without_unit(&right) } @@ -577,8 +593,8 @@ impl KalkValue { self, context: &mut crate::interpreter::Context, rhs: KalkValue, - ) -> KalkValue { - if let KalkValue::Number(real, _, _) = &self { + ) -> Result { + Ok(if let KalkValue::Number(real, _, _) = &self { let right = calculate_unit(context, &self, rhs.clone()).unwrap_or(rhs); if let KalkValue::Number(right_real, _, right_unit) = right { KalkValue::Number(real % right_real, float!(0f64), right_unit) @@ -587,10 +603,14 @@ impl KalkValue { } } else { self - } + }) } - pub(crate) fn eq(self, context: &mut crate::interpreter::Context, rhs: KalkValue) -> KalkValue { + pub(crate) fn eq( + self, + context: &mut crate::interpreter::Context, + rhs: KalkValue, + ) -> Result { let right = calculate_unit(context, &self, rhs.clone()).unwrap_or(rhs); self.eq_without_unit(&right) } @@ -599,7 +619,7 @@ impl KalkValue { self, context: &mut crate::interpreter::Context, rhs: KalkValue, - ) -> KalkValue { + ) -> Result { let right = calculate_unit(context, &self, rhs.clone()).unwrap_or(rhs); self.not_eq_without_unit(&right) } @@ -608,7 +628,7 @@ impl KalkValue { self, context: &mut crate::interpreter::Context, rhs: KalkValue, - ) -> KalkValue { + ) -> Result { let right = calculate_unit(context, &self, rhs.clone()).unwrap_or(rhs); self.greater_than_without_unit(&right) } @@ -617,7 +637,7 @@ impl KalkValue { self, context: &mut crate::interpreter::Context, rhs: KalkValue, - ) -> KalkValue { + ) -> Result { let right = calculate_unit(context, &self, rhs.clone()).unwrap_or(rhs); self.less_than_without_unit(&right) } @@ -626,13 +646,13 @@ impl KalkValue { self, context: &mut crate::interpreter::Context, rhs: KalkValue, - ) -> KalkValue { + ) -> Result { let right = calculate_unit(context, &self, rhs.clone()).unwrap_or(rhs); if let (KalkValue::Boolean(greater), KalkValue::Boolean(equal)) = ( - self.greater_than_without_unit(&right), - self.eq_without_unit(&right), + self.greater_than_without_unit(&right)?, + self.eq_without_unit(&right)?, ) { - KalkValue::Boolean(greater || equal) + Ok(KalkValue::Boolean(greater || equal)) } else { unreachable!() } @@ -642,69 +662,93 @@ impl KalkValue { self, context: &mut crate::interpreter::Context, rhs: KalkValue, - ) -> KalkValue { + ) -> Result { let right = calculate_unit(context, &self, rhs.clone()).unwrap_or(rhs); if let (KalkValue::Boolean(less), KalkValue::Boolean(equal)) = ( - self.less_than_without_unit(&right), - self.eq_without_unit(&right), + self.less_than_without_unit(&right)?, + self.eq_without_unit(&right)?, ) { - KalkValue::Boolean(less || equal) + Ok(KalkValue::Boolean(less || equal)) } else { unreachable!() } } - pub(crate) fn and(self, rhs: &KalkValue) -> KalkValue { + pub(crate) fn and(self, rhs: &KalkValue) -> Result { match (self, rhs) { (KalkValue::Boolean(boolean), KalkValue::Boolean(boolean_rhs)) => { - KalkValue::Boolean(boolean && *boolean_rhs) + Ok(KalkValue::Boolean(boolean && *boolean_rhs)) } - _ => KalkValue::nan(), + (lhs, rhs) => Err(KalkError::IncompatibleTypesForOperation( + String::from("and"), + lhs.get_type_name(), + rhs.get_type_name(), + )), } } - pub(crate) fn or(self, rhs: &KalkValue) -> KalkValue { + pub(crate) fn or(self, rhs: &KalkValue) -> Result { match (self, rhs) { (KalkValue::Boolean(boolean), KalkValue::Boolean(boolean_rhs)) => { - KalkValue::Boolean(boolean || *boolean_rhs) + Ok(KalkValue::Boolean(boolean || *boolean_rhs)) } - _ => KalkValue::nan(), + (lhs, rhs) => Err(KalkError::IncompatibleTypesForOperation( + String::from("or"), + lhs.get_type_name(), + rhs.get_type_name(), + )), } } - pub(crate) fn add_without_unit(self, rhs: &KalkValue) -> KalkValue { + pub(crate) fn add_without_unit(self, rhs: &KalkValue) -> Result { match (self.clone(), rhs) { ( KalkValue::Number(real, imaginary, _), KalkValue::Number(real_rhs, imaginary_rhs, unit), - ) => KalkValue::Number(real + real_rhs, imaginary + imaginary_rhs, unit.clone()), + ) => Ok(KalkValue::Number( + real + real_rhs, + imaginary + imaginary_rhs, + unit.clone(), + )), (KalkValue::Matrix(_), _) | (_, KalkValue::Matrix(_)) => { calculate_matrix(self, rhs, &KalkValue::add_without_unit) } (KalkValue::Vector(_), _) | (_, KalkValue::Vector(_)) => { calculate_vector(self, rhs, &KalkValue::add_without_unit) } - _ => KalkValue::nan(), + _ => Err(KalkError::IncompatibleTypesForOperation( + String::from("addition"), + self.get_type_name(), + rhs.get_type_name(), + )), } } - pub(crate) fn sub_without_unit(self, rhs: &KalkValue) -> KalkValue { + pub(crate) fn sub_without_unit(self, rhs: &KalkValue) -> Result { match (self.clone(), rhs) { ( KalkValue::Number(real, imaginary, _), KalkValue::Number(real_rhs, imaginary_rhs, unit), - ) => KalkValue::Number(real - real_rhs, imaginary - imaginary_rhs, unit.clone()), + ) => Ok(KalkValue::Number( + real - real_rhs, + imaginary - imaginary_rhs, + unit.clone(), + )), (KalkValue::Matrix(_), _) | (_, KalkValue::Matrix(_)) => { calculate_matrix(self, rhs, &KalkValue::sub_without_unit) } (KalkValue::Vector(_), _) | (_, KalkValue::Vector(_)) => { calculate_vector(self, rhs, &KalkValue::sub_without_unit) } - _ => KalkValue::nan(), + _ => Err(KalkError::IncompatibleTypesForOperation( + String::from("subtraction"), + self.get_type_name(), + rhs.get_type_name(), + )), } } - pub(crate) fn mul_without_unit(self, rhs: &KalkValue) -> KalkValue { + pub(crate) fn mul_without_unit(self, rhs: &KalkValue) -> Result { // Make sure matrix is always first to avoid having to match // different orders in the next match expression. let (lhs, rhs) = match (&self, rhs) { @@ -717,42 +761,38 @@ impl KalkValue { ( KalkValue::Number(real, imaginary, _), KalkValue::Number(real_rhs, imaginary_rhs, unit), - ) => KalkValue::Number( + ) => Ok(KalkValue::Number( // (a + bi)(c + di) = ac + adi + bci + bdi² real.clone() * real_rhs - imaginary.clone() * imaginary_rhs, real.clone() * imaginary_rhs + imaginary * real_rhs, unit.clone(), - ), - (KalkValue::Matrix(rows), KalkValue::Number(_, _, _)) => KalkValue::Matrix( - rows.iter() - .map(|row| { - row.iter() - .map(|x| x.clone().mul_without_unit(rhs)) - .collect() - }) - .collect(), - ), + )), + (KalkValue::Matrix(_), KalkValue::Number(_, _, _)) => { + calculate_matrix(lhs.clone(), rhs, &KalkValue::mul_without_unit) + } (KalkValue::Matrix(rows), KalkValue::Vector(values_rhs)) => { if rows.first().unwrap().len() != values_rhs.len() { - return KalkValue::nan(); + return Err(KalkError::IncompatibleVectorsMatrixes); } let mut new_values: Vec = Vec::new(); for row in rows { - new_values.push( - row.iter() - .zip(values_rhs) - .map(|(x, y)| x.clone().mul_without_unit(y)) - .sum(), - ) + let mut sum = KalkValue::from(0); + for (x, y) in row.iter().zip(values_rhs) { + sum = sum + .clone() + .add_without_unit(&x.clone().mul_without_unit(y)?)?; + } + + new_values.push(sum); } - KalkValue::Vector(new_values) + Ok(KalkValue::Vector(new_values)) } (KalkValue::Matrix(rows), KalkValue::Matrix(rows_rhs)) => { let lhs_columns = rows.first().unwrap(); if lhs_columns.len() != rows_rhs.len() { - return KalkValue::nan(); + return Err(KalkError::IncompatibleVectorsMatrixes); } let rhs_columns = rows_rhs.first().unwrap(); @@ -767,55 +807,62 @@ impl KalkValue { // For every value in the current lhs row for (k, value) in rows[i].iter().enumerate() { let value_rhs = &rows_rhs[k][j]; - sum = sum.add_without_unit(&value.clone().mul_without_unit(value_rhs)); + sum = + sum.add_without_unit(&value.clone().mul_without_unit(value_rhs)?)?; } result[i][j] = sum; } } - KalkValue::Matrix(result) + Ok(KalkValue::Matrix(result)) + } + (KalkValue::Vector(values), KalkValue::Number(_, _, _)) => { + let mut multiplied_values = Vec::new(); + for value in values { + multiplied_values.push(value.clone().mul_without_unit(rhs)?); + } + + Ok(KalkValue::Vector(multiplied_values)) } - (KalkValue::Vector(values), KalkValue::Number(_, _, _)) => KalkValue::Vector( - values - .iter() - .map(|x| x.clone().mul_without_unit(rhs)) - .collect(), - ), (KalkValue::Vector(values), KalkValue::Vector(values_rhs)) => { if values.len() != values_rhs.len() { - return KalkValue::nan(); + return Err(KalkError::IncompatibleVectorsMatrixes); } let mut sum = KalkValue::from(0f64); for (value, value_rhs) in values.iter().zip(values_rhs) { - sum = sum.add_without_unit(&value.clone().mul_without_unit(value_rhs)); + sum = sum.add_without_unit(&value.clone().mul_without_unit(value_rhs)?)?; } - sum + Ok(sum) } - _ => KalkValue::nan(), + _ => Err(KalkError::IncompatibleTypesForOperation( + String::from("multiplication"), + self.get_type_name(), + rhs.get_type_name(), + )), } } - pub(crate) fn div_without_unit(self, rhs: &KalkValue) -> KalkValue { + pub(crate) fn div_without_unit(self, rhs: &KalkValue) -> Result { match (self.clone(), rhs.clone()) { (KalkValue::Number(real, _, _), KalkValue::Number(real_rhs, _, unit)) => { // Avoid unecessary calculations if !self.has_imaginary() && !rhs.has_imaginary() { - KalkValue::Number(real / real_rhs, float!(0f64), unit) + Ok(KalkValue::Number(real / real_rhs, float!(0f64), unit)) } else { // Multiply both the numerator and denominator // with the conjugate of the denominator, and divide. - let conjugate = rhs.get_conjugate(); + let conjugate = rhs.get_conjugate()?; let (numerator, numerator_imaginary) = - self.mul_without_unit(&conjugate).values(); - let (denominator, _) = rhs.clone().mul_without_unit(&conjugate).values(); - KalkValue::Number( + self.mul_without_unit(&conjugate)?.values(); + let (denominator, _) = rhs.clone().mul_without_unit(&conjugate)?.values(); + Ok(KalkValue::Number( numerator / denominator.clone(), numerator_imaginary / denominator, unit, - ) + )) } } (KalkValue::Matrix(_), _) | (_, KalkValue::Matrix(_)) => { @@ -824,11 +871,15 @@ impl KalkValue { (KalkValue::Vector(_), _) | (_, KalkValue::Vector(_)) => { calculate_vector(self, rhs, &KalkValue::div_without_unit) } - _ => KalkValue::nan(), + _ => Err(KalkError::IncompatibleTypesForOperation( + String::from("division"), + self.get_type_name(), + rhs.get_type_name(), + )), } } - pub(crate) fn pow_without_unit(self, rhs: &KalkValue) -> KalkValue { + pub(crate) fn pow_without_unit(self, rhs: &KalkValue) -> Result { match (self.clone(), rhs) { ( KalkValue::Number(real, imaginary, _), @@ -842,75 +893,92 @@ impl KalkValue { let b = imaginary; let c = real_rhs; let d = imaginary_rhs; - let arg = crate::prelude::funcs::arg(self).values().0; + let arg = crate::prelude::funcs::arg(self)?.values().0; let raised = a.clone() * a + b.clone() * b; let exp = pow(raised.clone(), c.clone() / 2f64) * (-d.clone() * arg.clone()).exp(); let polar = c * arg + d.clone() / 2f64 * raised.ln(); - KalkValue::Number( + Ok(KalkValue::Number( polar.clone().cos() * exp.clone(), polar.sin() * exp, unit.clone(), - ) + )) } else { - KalkValue::Number(pow(real, real_rhs.clone()), float!(0), unit.clone()) + Ok(KalkValue::Number( + pow(real, real_rhs.clone()), + float!(0), + unit.clone(), + )) } } - (KalkValue::Matrix(rows), KalkValue::Number(real, imaginary, _)) => { - if real < &0f64 - || real.clone().fract() > 0.000001f64 - || !(imaginary == &0f64 || imaginary == &-0f64) - || rows.len() != rows.first().unwrap().len() - { - return KalkValue::nan(); + (KalkValue::Matrix(rows), KalkValue::Number(real, _, _)) => { + if real < &0f64 || real.clone().fract() > 0.000001f64 { + return Err(KalkError::Expected(String::from("a positive integer"))); + } + + if rhs.has_imaginary() { + return Err(KalkError::ExpectedReal); + } + + if rows.len() != rows.first().unwrap().len() { + return Err(KalkError::Expected(String::from("a square matrix"))); } if real == &0f64 { - return KalkValue::from(1f64); + return Ok(KalkValue::from(1f64)); } let mut result = KalkValue::from(1f64); for _ in 0..primitive!(real) as i32 { - result = result.mul_without_unit(&self); + result = result.mul_without_unit(&self)?; } - result + Ok(result) + } + (KalkValue::Number(_, _, _), KalkValue::Matrix(rows)) => { + let mut new_rows = Vec::new(); + for row in rows { + new_rows.push(Vec::new()); + for item in row { + new_rows + .last_mut() + .unwrap() + .push(self.clone().pow_without_unit(item)?); + } + } + + Ok(KalkValue::Matrix(new_rows)) } - (KalkValue::Number(_, _, _), KalkValue::Matrix(rows)) => KalkValue::Matrix( - rows.iter() - .map(|row| { - row.iter() - .map(|x| self.clone().pow_without_unit(x)) - .collect() - }) - .collect(), - ), (KalkValue::Vector(_), _) | (_, KalkValue::Vector(_)) => { calculate_vector(self, rhs, &KalkValue::pow_without_unit) } - _ => KalkValue::nan(), + _ => Err(KalkError::IncompatibleTypesForOperation( + String::from("pow"), + self.get_type_name(), + rhs.get_type_name(), + )), } } - pub(crate) fn eq_without_unit(&self, rhs: &KalkValue) -> KalkValue { + pub(crate) fn eq_without_unit(&self, rhs: &KalkValue) -> Result { match (self, rhs) { ( KalkValue::Number(real, imaginary, _), KalkValue::Number(real_rhs, imaginary_rhs, _), - ) => KalkValue::Boolean( + ) => Ok(KalkValue::Boolean( (real.clone() - real_rhs.clone()).abs() < ACCEPTABLE_COMPARISON_MARGIN && (imaginary.clone() - imaginary_rhs.clone()).abs() < ACCEPTABLE_COMPARISON_MARGIN, - ), + )), (KalkValue::Boolean(boolean), KalkValue::Boolean(boolean_rhs)) => { - KalkValue::Boolean(boolean == boolean_rhs) + Ok(KalkValue::Boolean(boolean == boolean_rhs)) } (KalkValue::Matrix(rows), KalkValue::Matrix(rows_rhs)) => { let mut matrices_are_equal = true; for (row, row_rhs) in rows.iter().zip(rows_rhs) { for (value, value_rhs) in row.iter().zip(row_rhs) { - if let KalkValue::Boolean(are_equal) = value.eq_without_unit(value_rhs) { + if let KalkValue::Boolean(are_equal) = value.eq_without_unit(value_rhs)? { if !are_equal { matrices_are_equal = false; } @@ -918,82 +986,106 @@ impl KalkValue { } } - KalkValue::Boolean(matrices_are_equal) + Ok(KalkValue::Boolean(matrices_are_equal)) } (KalkValue::Vector(values), KalkValue::Vector(values_rhs)) => { let mut vecs_are_equal = true; for (value, value_rhs) in values.iter().zip(values_rhs) { - if let KalkValue::Boolean(are_equal) = value.eq_without_unit(value_rhs) { + if let KalkValue::Boolean(are_equal) = value.eq_without_unit(value_rhs)? { if !are_equal { vecs_are_equal = false; } } } - KalkValue::Boolean(vecs_are_equal) + Ok(KalkValue::Boolean(vecs_are_equal)) } - _ => KalkValue::nan(), + _ => Err(KalkError::IncompatibleTypesForOperation( + String::from("equal"), + self.get_type_name(), + rhs.get_type_name(), + )), } } - pub(crate) fn not_eq_without_unit(&self, rhs: &KalkValue) -> KalkValue { + pub(crate) fn not_eq_without_unit(&self, rhs: &KalkValue) -> Result { match (self, rhs) { ( KalkValue::Number(real, imaginary, _), KalkValue::Number(real_rhs, imaginary_rhs, _), - ) => KalkValue::Boolean( + ) => Ok(KalkValue::Boolean( (real.clone() - real_rhs.clone()).abs() > ACCEPTABLE_COMPARISON_MARGIN || (imaginary.clone() - imaginary_rhs.clone()).abs() > ACCEPTABLE_COMPARISON_MARGIN, - ), + )), (KalkValue::Boolean(boolean), KalkValue::Boolean(boolean_rhs)) => { - KalkValue::Boolean(boolean != boolean_rhs) + Ok(KalkValue::Boolean(boolean != boolean_rhs)) } (KalkValue::Vector(_), KalkValue::Vector(_)) | (KalkValue::Matrix(_), KalkValue::Matrix(_)) => { - if let KalkValue::Boolean(boolean) = self.eq_without_unit(rhs) { - KalkValue::Boolean(!boolean) + if let KalkValue::Boolean(boolean) = self.eq_without_unit(rhs)? { + Ok(KalkValue::Boolean(!boolean)) } else { - KalkValue::nan() + unreachable!() } } - _ => KalkValue::nan(), + _ => Err(KalkError::IncompatibleTypesForOperation( + String::from("not equal"), + self.get_type_name(), + rhs.get_type_name(), + )), } } - pub(crate) fn greater_than_without_unit(&self, rhs: &KalkValue) -> KalkValue { + pub(crate) fn greater_than_without_unit( + &self, + rhs: &KalkValue, + ) -> Result { if self.has_imaginary() || rhs.has_imaginary() { - return KalkValue::nan(); + return Err(KalkError::ExpectedReal); } match (self, rhs) { - (KalkValue::Number(real, _, _), KalkValue::Number(real_rhs, _, _)) => { - KalkValue::Boolean(real.clone() - real_rhs.clone() > ACCEPTABLE_COMPARISON_MARGIN) - } - _ => KalkValue::nan(), + (KalkValue::Number(real, _, _), KalkValue::Number(real_rhs, _, _)) => Ok( + KalkValue::Boolean(real.clone() - real_rhs.clone() > ACCEPTABLE_COMPARISON_MARGIN), + ), + _ => Err(KalkError::IncompatibleTypesForOperation( + String::from("greater than"), + self.get_type_name(), + rhs.get_type_name(), + )), } } - pub(crate) fn less_than_without_unit(&self, rhs: &KalkValue) -> KalkValue { + pub(crate) fn less_than_without_unit(&self, rhs: &KalkValue) -> Result { if self.has_imaginary() || rhs.has_imaginary() { - return KalkValue::nan(); + return Err(KalkError::ExpectedReal); } match (self, rhs) { - (KalkValue::Number(real, _, _), KalkValue::Number(real_rhs, _, _)) => { - KalkValue::Boolean(real.clone() - real_rhs.clone() < -ACCEPTABLE_COMPARISON_MARGIN) - } - _ => KalkValue::nan(), + (KalkValue::Number(real, _, _), KalkValue::Number(real_rhs, _, _)) => Ok( + KalkValue::Boolean(real.clone() - real_rhs.clone() < -ACCEPTABLE_COMPARISON_MARGIN), + ), + _ => Err(KalkError::IncompatibleTypesForOperation( + String::from("less than"), + self.get_type_name(), + rhs.get_type_name(), + )), } } - pub fn get_conjugate(&self) -> KalkValue { + pub fn get_conjugate(&self) -> Result { match self { - KalkValue::Number(real, imaginary, unit) => { - KalkValue::Number(real.clone(), imaginary.clone() * (-1f64), unit.clone()) - } - _ => KalkValue::nan(), + KalkValue::Number(real, imaginary, unit) => Ok(KalkValue::Number( + real.clone(), + imaginary.clone() * (-1f64), + unit.clone(), + )), + _ => Err(KalkError::UnexpectedType( + self.get_type_name(), + vec![String::from("number")], + )), } } } @@ -1013,37 +1105,50 @@ pub fn format_number(input: f64) -> String { fn calculate_vector( x: KalkValue, y: &KalkValue, - action: &dyn Fn(KalkValue, &KalkValue) -> KalkValue, -) -> KalkValue { + action: &dyn Fn(KalkValue, &KalkValue) -> Result, +) -> Result { match (x, y) { (KalkValue::Vector(values), KalkValue::Number(_, _, _)) => { - KalkValue::Vector(values.iter().map(|x| action(x.clone(), y)).collect()) + let mut new_values = Vec::new(); + for value in values { + new_values.push(action(value.clone(), y)?); + } + + Ok(KalkValue::Vector(new_values)) } (KalkValue::Number(_, _, _), KalkValue::Vector(values_rhs)) => { - KalkValue::Vector(values_rhs.iter().map(|x| action(y.clone(), x)).collect()) + let mut new_values = Vec::new(); + for value in values_rhs { + new_values.push(action(y.clone(), value)?); + } + + Ok(KalkValue::Vector(new_values)) } (KalkValue::Vector(values), KalkValue::Vector(values_rhs)) => { - if values.len() == values_rhs.len() { - KalkValue::Vector( - values - .iter() - .zip(values_rhs) - .map(|(x, y)| action(x.clone(), y)) - .collect(), - ) - } else { - KalkValue::nan() + if values.len() != values_rhs.len() { + return Err(KalkError::IncompatibleVectorsMatrixes); } + + let mut new_values = Vec::new(); + for (value, value_rhs) in values.iter().zip(values_rhs) { + new_values.push(action(value.clone(), value_rhs)?); + } + + Ok(KalkValue::Vector(new_values)) } - _ => KalkValue::nan(), + (x, y) => Err(KalkError::IncompatibleTypesForOperation( + String::from("vector operation"), + x.get_type_name(), + y.get_type_name(), + )), } } fn calculate_matrix( x: KalkValue, y: &KalkValue, - action: &dyn Fn(KalkValue, &KalkValue) -> KalkValue, -) -> KalkValue { + action: &dyn Fn(KalkValue, &KalkValue) -> Result, +) -> Result { // Make sure matrix is always first to avoid having to match // different orders in the next match expression. let (x, y) = match (&x, y) { @@ -1053,14 +1158,20 @@ fn calculate_matrix( }; match (x, y) { - (KalkValue::Matrix(rows), KalkValue::Number(_, _, _)) => KalkValue::Matrix( - rows.iter() - .map(|row| row.iter().map(|x| action(x.clone(), y)).collect()) - .collect(), - ), + (KalkValue::Matrix(rows), KalkValue::Number(_, _, _)) => { + let mut new_rows = Vec::new(); + for row in rows { + new_rows.push(Vec::new()); + for item in row { + new_rows.last_mut().unwrap().push(action(item.clone(), y)?); + } + } + + Ok(KalkValue::Matrix(new_rows)) + } (KalkValue::Matrix(rows), KalkValue::Vector(values_rhs)) => { if rows.len() != values_rhs.len() { - return KalkValue::nan(); + return Err(KalkError::IncompatibleVectorsMatrixes); } let mut new_rows = Vec::new(); @@ -1070,17 +1181,17 @@ fn calculate_matrix( new_rows .last_mut() .unwrap() - .push(action(value.clone(), &values_rhs[i])) + .push(action(value.clone(), &values_rhs[i])?) } } - KalkValue::Matrix(new_rows) + Ok(KalkValue::Matrix(new_rows)) } (KalkValue::Matrix(rows), KalkValue::Matrix(rows_rhs)) => { if rows.len() != rows_rhs.len() || rows.first().unwrap().len() != rows_rhs.first().unwrap().len() { - return KalkValue::nan(); + return Err(KalkError::IncompatibleVectorsMatrixes); } let mut new_rows = Vec::new(); @@ -1090,13 +1201,17 @@ fn calculate_matrix( new_rows .last_mut() .unwrap() - .push(action(value.clone(), &rows_rhs[i][j])) + .push(action(value.clone(), &rows_rhs[i][j])?) } } - KalkValue::Matrix(new_rows) + Ok(KalkValue::Matrix(new_rows)) } - _ => KalkValue::nan(), + _ => Err(KalkError::IncompatibleTypesForOperation( + String::from("matrix operation"), + x.get_type_name(), + y.get_type_name(), + )), } } @@ -1139,7 +1254,7 @@ impl From for String { } } -impl std::iter::Sum for KalkValue { +/*impl std::iter::Sum for KalkValue { fn sum(iter: I) -> KalkValue where I: std::iter::Iterator, @@ -1151,7 +1266,7 @@ impl std::iter::Sum for KalkValue { sum } -} +}*/ impl From for String { fn from(val: KalkValue) -> Self { @@ -1211,7 +1326,8 @@ mod tests { for (a, b, expected_result) in in_out { let actual_result = KalkValue::Number(float!(a.0), float!(a.1), None) - .add_without_unit(&KalkValue::Number(float!(b.0), float!(b.1), None)); + .add_without_unit(&KalkValue::Number(float!(b.0), float!(b.1), None)) + .unwrap(); assert_eq!(actual_result.to_f64(), expected_result.0); assert_eq!(actual_result.imaginary_to_f64(), expected_result.1); } @@ -1228,7 +1344,8 @@ mod tests { for (a, b, expected_result) in in_out { let actual_result = KalkValue::Number(float!(a.0), float!(a.1), None) - .sub_without_unit(&KalkValue::Number(float!(b.0), float!(b.1), None)); + .sub_without_unit(&KalkValue::Number(float!(b.0), float!(b.1), None)) + .unwrap(); assert_eq!(actual_result.to_f64(), expected_result.0); assert_eq!(actual_result.imaginary_to_f64(), expected_result.1); } @@ -1245,7 +1362,8 @@ mod tests { for (a, b, expected_result) in in_out { let actual_result = KalkValue::Number(float!(a.0), float!(a.1), None) - .mul_without_unit(&KalkValue::Number(float!(b.0), float!(b.1), None)); + .mul_without_unit(&KalkValue::Number(float!(b.0), float!(b.1), None)) + .unwrap(); assert_eq!(actual_result.to_f64(), expected_result.0); assert_eq!(actual_result.imaginary_to_f64(), expected_result.1); } @@ -1261,7 +1379,8 @@ mod tests { for (a, b, expected_result) in in_out { let actual_result = KalkValue::Number(float!(a.0), float!(a.1), None) - .div_without_unit(&KalkValue::Number(float!(b.0), float!(b.1), None)); + .div_without_unit(&KalkValue::Number(float!(b.0), float!(b.1), None)) + .unwrap(); assert_eq!(actual_result.to_f64(), expected_result.0); assert_eq!(actual_result.imaginary_to_f64(), expected_result.1); } @@ -1289,7 +1408,8 @@ mod tests { for (a, b, expected_result) in in_out { let actual_result = KalkValue::Number(float!(a.0), float!(a.1), None) - .pow_without_unit(&KalkValue::Number(float!(b.0), float!(b.1), None)); + .pow_without_unit(&KalkValue::Number(float!(b.0), float!(b.1), None)) + .unwrap(); assert!(cmp(actual_result.to_f64(), expected_result.0)); assert!(cmp(actual_result.imaginary_to_f64(), expected_result.1)); } diff --git a/kalk/src/lib.rs b/kalk/src/lib.rs index 12e8897..0f41702 100644 --- a/kalk/src/lib.rs +++ b/kalk/src/lib.rs @@ -1,9 +1,11 @@ #![allow(clippy::unused_unit)] #![allow(clippy::float_cmp)] +#![allow(clippy::clone_on_copy)] // the float type needs explicit cloning if the rug feature is enabled mod analysis; pub mod ast; pub mod calculation_result; mod calculus; +mod errors; mod integration_testing; mod interpreter; mod inverter; diff --git a/kalk/src/parser.rs b/kalk/src/parser.rs index 8960b78..dfbfb3c 100644 --- a/kalk/src/parser.rs +++ b/kalk/src/parser.rs @@ -3,6 +3,7 @@ use std::cell::Cell; use crate::analysis; use crate::ast::Identifier; use crate::calculation_result::CalculationResult; +use crate::errors::KalkError; use crate::{ ast::{Expr, Stmt}, interpreter, @@ -86,76 +87,6 @@ impl Default for Context { } } -/// Error that occured during parsing or evaluation. -#[derive(Debug, Clone, PartialEq)] -pub enum KalkError { - CannotIndexByImaginary, - CanOnlyIndexX, - Expected(String), - ExpectedDx, - ExpectedIf, - IncorrectAmountOfArguments(usize, String, usize), - IncorrectAmountOfIndexes(usize, usize), - ItemOfIndexDoesNotExist(Vec), - InconsistentColumnWidths, - InvalidComprehension(String), - InvalidNumberLiteral(String), - InvalidOperator, - InvalidUnit, - TimedOut, - VariableReferencesItself, - PiecewiseConditionsAreFalse, - UnexpectedToken(TokenKind, TokenKind), - UndefinedFn(String), - UndefinedVar(String), - UnableToInvert(String), - UnableToSolveEquation, - UnableToOverrideConstant(String), - UnableToParseExpression, - UnrecognizedBase, - Unknown, -} - -impl ToString for KalkError { - fn to_string(&self) -> String { - match self { - KalkError::CannotIndexByImaginary => String::from("Cannot index by imaginary numbers."), - KalkError::CanOnlyIndexX => String::from("Indexing (getting an item with a specific index) is only possible on vectors and matrices."), - KalkError::Expected(description) => format!("Expected: {}", description), - KalkError::ExpectedDx => String::from("Expected eg. dx, to specify for which variable the operation is being done to. Example with integration: ∫(0, 1, x dx) or ∫(0, 1, x, dx). You may need to put parenthesis around the expression before dx/dy/du/etc."), - KalkError::ExpectedIf => String::from("Expected 'if', with a condition after it."), - KalkError::IncorrectAmountOfArguments(expected, func, got) => format!( - "Expected {} arguments for function {}, but got {}.", - expected, func, got - ), - KalkError::IncorrectAmountOfIndexes(expected, got) => format!( - "Expected {} indexes but got {}.", - expected, got - ), - KalkError::ItemOfIndexDoesNotExist(indexes) => format!("Item of index ⟦{}⟧ does not exist.", indexes.iter().map(|x| x.to_string()).collect::>().join(", ")), - KalkError::InconsistentColumnWidths => String::from("Inconsistent column widths. Matrix columns must be the same size."), - KalkError::InvalidComprehension(x) => format!("Invalid comprehension: {}", x), - KalkError::InvalidNumberLiteral(x) => format!("Invalid number literal: '{}'.", x), - KalkError::InvalidOperator => String::from("Invalid operator."), - KalkError::InvalidUnit => String::from("Invalid unit."), - KalkError::TimedOut => String::from("Operation took too long."), - KalkError::VariableReferencesItself => String::from("Variable references itself."), - KalkError::PiecewiseConditionsAreFalse => String::from("All the conditions in the piecewise are false."), - KalkError::UnexpectedToken(got, expected) => { - format!("Unexpected token: '{:?}', expected '{:?}'.", got, expected) - } - KalkError::UnableToInvert(msg) => format!("Unable to invert: {}", msg), - KalkError::UndefinedFn(name) => format!("Undefined function: '{}'.", name), - KalkError::UndefinedVar(name) => format!("Undefined variable: '{}'.", name), - KalkError::UnableToParseExpression => String::from("Unable to parse expression."), - KalkError::UnableToSolveEquation => String::from("Unable to solve equation."), - KalkError::UnableToOverrideConstant(name) => format!("Unable to override constant: '{}'.", name), - KalkError::UnrecognizedBase => String::from("Unrecognized base."), - KalkError::Unknown => String::from("Unknown error."), - } - } -} - /// Evaluate expressions/declarations and return the answer. /// /// `None` will be returned if the last statement is a declaration. diff --git a/kalk/src/prelude/mod.rs b/kalk/src/prelude/mod.rs index af1b312..03f39d4 100644 --- a/kalk/src/prelude/mod.rs +++ b/kalk/src/prelude/mod.rs @@ -1,3 +1,5 @@ +#![allow(clippy::redundant_clone)] // giving false flags +use crate::errors::KalkError; use crate::kalk_value::KalkValue; use lazy_static::lazy_static; use std::collections::HashMap; @@ -123,11 +125,14 @@ enum FuncType { Other, } -pub struct UnaryFuncInfo(fn(KalkValue) -> KalkValue, FuncType); +pub struct UnaryFuncInfo(fn(KalkValue) -> Result, FuncType); -pub struct BinaryFuncInfo(fn(KalkValue, KalkValue) -> KalkValue, FuncType); +pub struct BinaryFuncInfo( + fn(KalkValue, KalkValue) -> Result, + FuncType, +); -pub struct VectorFuncInfo(fn(KalkValue) -> KalkValue, FuncType); +pub struct VectorFuncInfo(fn(KalkValue) -> Result, FuncType); impl UnaryFuncInfo { fn call( @@ -135,11 +140,11 @@ impl UnaryFuncInfo { context: &mut interpreter::Context, x: KalkValue, angle_unit: &str, - ) -> KalkValue { + ) -> Result { let func = self.0; match self.1 { FuncType::Trig => func(from_angle_unit(context, x, angle_unit)), - FuncType::InverseTrig => to_angle_unit(context, func(x), angle_unit), + FuncType::InverseTrig => Ok(to_angle_unit(context, func(x)?, angle_unit)), FuncType::Other => func(x), } } @@ -152,21 +157,21 @@ impl BinaryFuncInfo { x: KalkValue, y: KalkValue, angle_unit: &str, - ) -> KalkValue { + ) -> Result { let func = self.0; match self.1 { FuncType::Trig => func( from_angle_unit(context, x, angle_unit), from_angle_unit(context, y, angle_unit), ), - FuncType::InverseTrig => to_angle_unit(context, func(x, y), angle_unit), + FuncType::InverseTrig => Ok(to_angle_unit(context, func(x, y)?, angle_unit)), FuncType::Other => func(x, y), } } } impl VectorFuncInfo { - fn call(&self, x: KalkValue) -> KalkValue { + fn call(&self, x: KalkValue) -> Result { let func = self.0; func(x) } @@ -199,7 +204,7 @@ pub fn call_unary_func( name: &str, x: KalkValue, angle_unit: &str, -) -> Option<(KalkValue, String)> { +) -> Option<(Result, String)> { if let Some((func_info, func_unit)) = UNARY_FUNCS.get(name) { Some(( func_info.call(context, x, angle_unit), @@ -216,7 +221,7 @@ pub fn call_binary_func( x: KalkValue, y: KalkValue, angle_unit: &str, -) -> Option<(KalkValue, String)> { +) -> Option<(Result, String)> { if let Some((func_info, func_unit)) = BINARY_FUNCS.get(name) { Some(( func_info.call(context, x, y, angle_unit), @@ -227,7 +232,7 @@ pub fn call_binary_func( } } -pub fn call_vector_func(name: &str, x: KalkValue) -> Option { +pub fn call_vector_func(name: &str, x: KalkValue) -> Option> { VECTOR_FUNCS.get(name).map(|func_info| func_info.call(x)) } @@ -269,9 +274,11 @@ pub mod funcs { use super::special_funcs::factorial; #[cfg(feature = "rug")] pub use super::with_rug::funcs::*; - use crate::{as_number_or_return, as_vector_or_return, float, kalk_value::KalkValue}; + use crate::{ + as_number_or_return, as_vector_or_return, errors::KalkError, float, kalk_value::KalkValue, + }; - pub fn abs(x: KalkValue) -> KalkValue { + pub fn abs(x: KalkValue) -> Result { let has_imaginary = x.has_imaginary(); let (real, imaginary, unit) = as_number_or_return!(x); if has_imaginary { @@ -281,165 +288,165 @@ pub mod funcs { sqrt(KalkValue::Number(a + b, float!(0), unit)) } else { - KalkValue::Number(real.abs(), float!(0), unit) + Ok(KalkValue::Number(real.abs(), float!(0), unit)) } } - pub fn acos(x: KalkValue) -> KalkValue { + pub fn acos(x: KalkValue) -> Result { let (real, _, unit) = as_number_or_return!(x.clone()); if x.has_imaginary() || real > 1f64 || real < -1f64 { // -i * ln(i * sqrt(1 - z²) + z) let root = - sqrt(KalkValue::from(1f64).sub_without_unit(&x.clone().mul_without_unit(&x))); - let iroot = multiply_with_i(root); - let (ln_real, ln_imaginary, ln_unit) = - as_number_or_return!(ln(iroot.add_without_unit(&x))); - + sqrt(KalkValue::from(1f64).sub_without_unit(&x.clone().mul_without_unit(&x)?)?)?; + let iroot = multiply_with_i(root)?; + let ln = ln(iroot.add_without_unit(&x)?)?; + let (ln_real, ln_imaginary, ln_unit) = as_number_or_return!(ln); // -iz = -i(a + bi) = b - ai - KalkValue::Number(ln_imaginary, -ln_real, ln_unit) + Ok(KalkValue::Number(ln_imaginary, -ln_real, ln_unit)) } else { - KalkValue::Number(real.acos(), float!(0), unit) + Ok(KalkValue::Number(real.acos(), float!(0), unit)) } } - pub fn acosh(x: KalkValue) -> KalkValue { + pub fn acosh(x: KalkValue) -> Result { let (real, imaginary, unit) = as_number_or_return!(x.clone()); if x.has_imaginary() || real < 1f64 { let sqrt1 = sqrt(KalkValue::Number( real.clone() + 1f64, imaginary.clone(), unit.clone(), - )); - let sqrt2 = sqrt(KalkValue::Number(real - 1f64, imaginary, unit)); + ))?; + let sqrt2 = sqrt(KalkValue::Number(real - 1f64, imaginary, unit))?; - ln(x.add_without_unit(&sqrt1.mul_without_unit(&sqrt2))) + ln(x.add_without_unit(&sqrt1.mul_without_unit(&sqrt2)?)?) } else { - KalkValue::Number(real.acosh(), float!(0), unit) + Ok(KalkValue::Number(real.acosh(), float!(0), unit)) } } - pub fn acot(x: KalkValue) -> KalkValue { + pub fn acot(x: KalkValue) -> Result { let (real, _, unit) = as_number_or_return!(x.clone()); if x.has_imaginary() { // atan(1/z) - atan(KalkValue::from(1f64).div_without_unit(&x)) + atan(KalkValue::from(1f64).div_without_unit(&x)?) } else { - KalkValue::Number((1f64 / real).atan(), float!(0), unit) + Ok(KalkValue::Number((1f64 / real).atan(), float!(0), unit)) } } - pub fn acoth(x: KalkValue) -> KalkValue { + pub fn acoth(x: KalkValue) -> Result { let (real, _, unit) = as_number_or_return!(x.clone()); if x.has_imaginary() || real <= 1f64 || real >= -1f64 { // 1 / z let (inv_real, inv_imaginary, inv_unit) = - as_number_or_return!(KalkValue::from(1f64).div_without_unit(&x)); + as_number_or_return!(KalkValue::from(1f64).div_without_unit(&x)?); let ln1 = ln(KalkValue::Number( 1f64 + inv_real.clone(), inv_imaginary.clone(), inv_unit.clone(), - )); - let ln2 = ln(KalkValue::Number(1f64 - inv_real, -inv_imaginary, inv_unit)); + ))?; + let ln2 = ln(KalkValue::Number(1f64 - inv_real, -inv_imaginary, inv_unit))?; - ln1.sub_without_unit(&ln2) + ln1.sub_without_unit(&ln2)? .div_without_unit(&KalkValue::from(2f64)) } else { - KalkValue::Number((1f64 / real).atanh(), float!(0), unit) + Ok(KalkValue::Number((1f64 / real).atanh(), float!(0), unit)) } } - pub fn acsc(x: KalkValue) -> KalkValue { + pub fn acsc(x: KalkValue) -> Result { let (real, _, unit) = as_number_or_return!(x.clone()); if x.has_imaginary() || real < 1f64 || real > -1f64 { // asin(1/z) - asin(KalkValue::from(1f64).div_without_unit(&x)) + asin(KalkValue::from(1f64).div_without_unit(&x)?) } else { - KalkValue::Number((1f64 / real).asin(), float!(0), unit) + Ok(KalkValue::Number((1f64 / real).asin(), float!(0), unit)) } } - pub fn acsch(x: KalkValue) -> KalkValue { + pub fn acsch(x: KalkValue) -> Result { let (real, _, unit) = as_number_or_return!(x.clone()); if x.has_imaginary() || real == 0f64 { let (inv_x2_real, inv_x2_imaginary, inv_x2_unit) = as_number_or_return!( - KalkValue::from(1f64).div_without_unit(&x.clone().mul_without_unit(&x)) + KalkValue::from(1f64).div_without_unit(&x.clone().mul_without_unit(&x)?)? ); let sqrt = sqrt(KalkValue::Number( 1f64 + inv_x2_real, inv_x2_imaginary, inv_x2_unit, - )); - let inv_x = KalkValue::from(1f64).div_without_unit(&x); + ))?; + let inv_x = KalkValue::from(1f64).div_without_unit(&x)?; - ln(sqrt.add_without_unit(&inv_x)) + ln(sqrt.add_without_unit(&inv_x)?) } else { - KalkValue::Number((1f64 / real).asinh(), float!(0), unit) + Ok(KalkValue::Number((1f64 / real).asinh(), float!(0), unit)) } } - pub fn asec(x: KalkValue) -> KalkValue { + pub fn asec(x: KalkValue) -> Result { let (real, _, unit) = as_number_or_return!(x.clone()); if x.has_imaginary() || real < 1f64 || real > -1f64 { // acos(1/z) - acos(KalkValue::from(1f64).div_without_unit(&x)) + acos(KalkValue::from(1f64).div_without_unit(&x)?) } else { - KalkValue::Number((1f64 / real).acos(), float!(0), unit) + Ok(KalkValue::Number((1f64 / real).acos(), float!(0), unit)) } } - pub fn asech(x: KalkValue) -> KalkValue { + pub fn asech(x: KalkValue) -> Result { let (real, _, unit) = as_number_or_return!(x.clone()); if x.has_imaginary() || real <= 0f64 || real > 1f64 { // 1/z - let inv = KalkValue::from(1f64).div_without_unit(&x); + let inv = KalkValue::from(1f64).div_without_unit(&x)?; let (inv_real, inv_imaginary, inv_unit) = as_number_or_return!(inv.clone()); // sqrt(1/z - 1) let sqrt1 = sqrt(KalkValue::Number( inv_real.clone() - 1f64, inv_imaginary.clone(), inv_unit.clone(), - )); + ))?; // sqrt(1/z + 1) - let sqrt2 = sqrt(KalkValue::Number(inv_real + 1f64, inv_imaginary, inv_unit)); + let sqrt2 = sqrt(KalkValue::Number(inv_real + 1f64, inv_imaginary, inv_unit))?; // ln(1/z + sqrt(1/z - 1) * sqrt(1/z + 1)) - ln(sqrt1.mul_without_unit(&sqrt2).add_without_unit(&inv)) + ln(sqrt1.mul_without_unit(&sqrt2)?.add_without_unit(&inv)?) } else { - KalkValue::Number((1f64 / real).acosh(), float!(0), unit) + Ok(KalkValue::Number((1f64 / real).acosh(), float!(0), unit)) } } - pub fn asin(x: KalkValue) -> KalkValue { + pub fn asin(x: KalkValue) -> Result { let (real, _, unit) = as_number_or_return!(x.clone()); if x.has_imaginary() || real > 1f64 || real < -1f64 { // i * ln(sqrt(1 - z²) - iz) let root = - sqrt(KalkValue::from(1f64).sub_without_unit(&x.clone().mul_without_unit(&x))); - let iz = multiply_with_i(x); - let ln = ln(root.sub_without_unit(&iz)); + sqrt(KalkValue::from(1f64).sub_without_unit(&x.clone().mul_without_unit(&x)?)?)?; + let iz = multiply_with_i(x)?; + let ln = ln(root.sub_without_unit(&iz)?)?; multiply_with_i(ln) } else { - KalkValue::Number(real.asin(), float!(0), unit) + Ok(KalkValue::Number(real.asin(), float!(0), unit)) } } - pub fn asinh(x: KalkValue) -> KalkValue { + pub fn asinh(x: KalkValue) -> Result { let (real, _, unit) = as_number_or_return!(x.clone()); if x.has_imaginary() { let (x2_real, x2_imaginary, x2_unit) = - as_number_or_return!(x.clone().mul_without_unit(&x)); - let sqrt = sqrt(KalkValue::Number(x2_real + 1f64, x2_imaginary, x2_unit)); + as_number_or_return!(x.clone().mul_without_unit(&x)?); + let sqrt = sqrt(KalkValue::Number(x2_real + 1f64, x2_imaginary, x2_unit))?; - ln(x.add_without_unit(&sqrt)) + ln(x.add_without_unit(&sqrt)?) } else { - KalkValue::Number(real.asinh(), float!(0), unit) + Ok(KalkValue::Number(real.asinh(), float!(0), unit)) } } - pub fn atan(x: KalkValue) -> KalkValue { + pub fn atan(x: KalkValue) -> Result { let (real, _, unit) = as_number_or_return!(x.clone()); if x.has_imaginary() { - let (iz_real, iz_imaginary, iz_unit) = as_number_or_return!(multiply_with_i(x)); + let mul_i = multiply_with_i(x)?; + let (iz_real, iz_imaginary, iz_unit) = as_number_or_return!(mul_i); // 1 - iz let neg = KalkValue::Number( @@ -452,15 +459,15 @@ pub mod funcs { let pos = KalkValue::Number(1f64 + iz_real, iz_imaginary, iz_unit); // ln(1 - iz) - ln(1 + iz) - let ln = ln(neg).sub_without_unit(&ln(pos)); + let ln = ln(neg)?.sub_without_unit(&ln(pos)?)?; - multiply_with_i(ln).div_without_unit(&KalkValue::from(2f64)) + multiply_with_i(ln)?.div_without_unit(&KalkValue::from(2f64)) } else { - KalkValue::Number(real.atan(), float!(0), unit) + Ok(KalkValue::Number(real.atan(), float!(0), unit)) } } - pub fn atanh(x: KalkValue) -> KalkValue { + pub fn atanh(x: KalkValue) -> Result { let has_imaginary = x.has_imaginary(); let (real, imaginary, unit) = as_number_or_return!(x); if has_imaginary || real >= 1f64 || real <= -1f64 { @@ -469,17 +476,17 @@ pub mod funcs { 1f64 + real.clone(), imaginary.clone(), unit.clone(), - )); - let log2 = ln(KalkValue::Number(1f64 - real, -imaginary, unit)); + ))?; + let log2 = ln(KalkValue::Number(1f64 - real, -imaginary, unit))?; - log1.sub_without_unit(&log2) + log1.sub_without_unit(&log2)? .div_without_unit(&KalkValue::from(2f64)) } else { - KalkValue::Number(real.atanh(), float!(0), unit) + Ok(KalkValue::Number(real.atanh(), float!(0), unit)) } } - pub fn average(x: KalkValue) -> KalkValue { + pub fn average(x: KalkValue) -> Result { let values = as_vector_or_return!(x); let count = values.len() as i64; let mut sum_real = float!(0); @@ -493,119 +500,126 @@ pub mod funcs { KalkValue::Number(sum_real, sum_imaginary, None).div_without_unit(&KalkValue::from(count)) } - pub fn cbrt(x: KalkValue) -> KalkValue { + pub fn cbrt(x: KalkValue) -> Result { let (real, _, unit) = as_number_or_return!(x); - KalkValue::Number(real.cbrt(), float!(0), unit) + Ok(KalkValue::Number(real.cbrt(), float!(0), unit)) } - pub fn ceil(x: KalkValue) -> KalkValue { + pub fn ceil(x: KalkValue) -> Result { let (real, imaginary, unit) = as_number_or_return!(x); - KalkValue::Number(real.ceil(), imaginary.ceil(), unit) + Ok(KalkValue::Number(real.ceil(), imaginary.ceil(), unit)) } - pub fn cos(x: KalkValue) -> KalkValue { + pub fn cos(x: KalkValue) -> Result { let (real, imaginary, unit) = as_number_or_return!(x); - KalkValue::Number( + Ok(KalkValue::Number( real.clone().cos() * imaginary.clone().cosh(), -real.sin() * imaginary.sinh(), unit, - ) + )) } - pub fn cosh(x: KalkValue) -> KalkValue { + pub fn cosh(x: KalkValue) -> Result { let (real, imaginary, unit) = as_number_or_return!(x); - KalkValue::Number( + Ok(KalkValue::Number( real.clone().cosh() * imaginary.clone().cos(), real.sinh() * imaginary.sin(), unit, - ) + )) } - pub fn csc(x: KalkValue) -> KalkValue { - KalkValue::from(1f64).div_without_unit(&sin(x)) + pub fn csc(x: KalkValue) -> Result { + KalkValue::from(1f64).div_without_unit(&sin(x)?) } - pub fn csch(x: KalkValue) -> KalkValue { - KalkValue::from(1f64).div_without_unit(&sinh(x)) + pub fn csch(x: KalkValue) -> Result { + KalkValue::from(1f64).div_without_unit(&sinh(x)?) } - pub fn cot(x: KalkValue) -> KalkValue { + pub fn cot(x: KalkValue) -> Result { let (real, imaginary, unit) = as_number_or_return!(x); let a = real * 2f64; let b = imaginary * 2f64; - KalkValue::Number( + Ok(KalkValue::Number( -a.clone().sin() / (a.clone().cos() - b.clone().cosh()), b.clone().sinh() / (a.cos() - b.cosh()), unit, - ) + )) } - pub fn coth(x: KalkValue) -> KalkValue { + pub fn coth(x: KalkValue) -> Result { let (real, imaginary, unit) = as_number_or_return!(x); let a = real * 2f64; let b = imaginary * 2f64; - KalkValue::Number( + Ok(KalkValue::Number( -a.clone().sinh() / (b.clone().cos() - a.clone().cosh()), b.clone().sin() / (b.cos() - a.cosh()), unit, - ) + )) } - pub fn diag(x: KalkValue) -> KalkValue { + pub fn diag(x: KalkValue) -> Result { if let KalkValue::Vector(values) = x { let mut result = vec![vec![KalkValue::from(0f64); values.len()]; values.len()]; for (i, value) in values.iter().enumerate() { result[i][i] = value.clone(); } - KalkValue::Matrix(result) + Ok(KalkValue::Matrix(result)) } else { - KalkValue::nan() + Err(KalkError::UnexpectedType( + x.get_type_name(), + vec![String::from("vector")], + )) } } - pub fn exp(x: KalkValue) -> KalkValue { + pub fn exp(x: KalkValue) -> Result { let has_imaginary = x.has_imaginary(); let (real, imaginary, unit) = as_number_or_return!(x); if has_imaginary { // e^a*cos(b) + ie^a*sin(b) let exp_a = real.exp(); let b = imaginary; - KalkValue::Number(exp_a.clone() * b.clone().cos(), exp_a * b.sin(), unit) + Ok(KalkValue::Number( + exp_a.clone() * b.clone().cos(), + exp_a * b.sin(), + unit, + )) } else { - KalkValue::Number(real.exp(), float!(0), unit) + Ok(KalkValue::Number(real.exp(), float!(0), unit)) } } - pub fn floor(x: KalkValue) -> KalkValue { + pub fn floor(x: KalkValue) -> Result { let (real, imaginary, unit) = as_number_or_return!(x); - KalkValue::Number(real.floor(), imaginary.floor(), unit) + Ok(KalkValue::Number(real.floor(), imaginary.floor(), unit)) } - pub fn frac(x: KalkValue) -> KalkValue { + pub fn frac(x: KalkValue) -> Result { let (real, imaginary, unit) = as_number_or_return!(x); - KalkValue::Number(real.fract(), imaginary.fract(), unit) + Ok(KalkValue::Number(real.fract(), imaginary.fract(), unit)) } - pub fn gcd(x: KalkValue, y: KalkValue) -> KalkValue { + pub fn gcd(x: KalkValue, y: KalkValue) -> Result { let (real, imaginary, unit) = as_number_or_return!(x.clone()); let (real_rhs, imaginary_rhs, _) = as_number_or_return!(y.clone()); // Find the norm of a Gaussian integer - fn norm(x: KalkValue) -> KalkValue { + fn norm(x: KalkValue) -> Result { let (real, imaginary, unit) = as_number_or_return!(x); - KalkValue::Number( + Ok(KalkValue::Number( (real.clone() * real) + (imaginary.clone() * imaginary), float!(0), unit, - ) + )) } if x.has_imaginary() || y.has_imaginary() { @@ -615,8 +629,7 @@ pub mod funcs { || imaginary_rhs.fract() != 0f64 { // Not a Gaussian integer! - // TODO: throw an actual error instead of returning NaN - return KalkValue::from(f64::NAN); + return Err(KalkError::Expected(String::from("a gaussian integer"))); } // Partially derived from: @@ -626,7 +639,7 @@ pub mod funcs { let b; // Ensure a > b - if norm(x.clone()).values().0 < norm(y.clone()).values().0 { + if norm(x.clone())?.values().0 < norm(y.clone())?.values().0 { a = y; b = x; } else { @@ -636,13 +649,13 @@ pub mod funcs { let (b_real, b_imaginary, b_unit) = as_number_or_return!(b.clone()); let (c_real, c_imaginary, c_unit) = - as_number_or_return!(a.clone().div_without_unit(&b)); + as_number_or_return!(a.clone().div_without_unit(&b)?); if c_imaginary.clone().fract() == 0f64 { - KalkValue::Number(b_real.abs(), b_imaginary, b_unit) + Ok(KalkValue::Number(b_real.abs(), b_imaginary, b_unit)) } else { let rounded_c = KalkValue::Number(c_real.round(), c_imaginary.round(), c_unit); gcd( - a.sub_without_unit(&b.clone().mul_without_unit(&rounded_c)), + a.sub_without_unit(&b.clone().mul_without_unit(&rounded_c)?)?, b, ) } @@ -665,26 +678,28 @@ pub mod funcs { // Usually we'd need to return max(x, -x), but since we've handled negative // values above, that is unnecessary. - KalkValue::Number(x_a, float!(0), unit) + Ok(KalkValue::Number(x_a, float!(0), unit)) } } - pub fn im(x: KalkValue) -> KalkValue { + pub fn im(x: KalkValue) -> Result { let (_, imaginary, unit) = as_number_or_return!(x); - KalkValue::Number(imaginary, float!(0), unit) + Ok(KalkValue::Number(imaginary, float!(0), unit)) } - pub fn iverson(x: KalkValue) -> KalkValue { - KalkValue::from(if let KalkValue::Boolean(boolean_value) = x { - if boolean_value { - 1 + pub fn iverson(x: KalkValue) -> Result { + Ok(KalkValue::from( + if let KalkValue::Boolean(boolean_value) = x { + if boolean_value { + 1 + } else { + 0 + } } else { - 0 - } - } else { - 1 - }) + 1 + }, + )) } // ⎛ ⎞ @@ -692,89 +707,93 @@ pub mod funcs { // lcm(a, b) = ⎜ ───────── ⎟ × ⎜b⎜ // ⎜ gcd(a, b) ⎟ // ⎝ ⎠ - pub fn lcm(x: KalkValue, y: KalkValue) -> KalkValue { + pub fn lcm(x: KalkValue, y: KalkValue) -> Result { let (real, imaginary, unit) = as_number_or_return!(x.clone()); let (real_rhs, imaginary_rhs, unit_rhs) = as_number_or_return!(y.clone()); - let gcd = gcd(x, y); + let gcd = gcd(x, y)?; let absx = KalkValue::Number(real.abs(), imaginary, unit); let absy = KalkValue::Number(real_rhs.abs(), imaginary_rhs, unit_rhs); - absx.div_without_unit(&gcd).mul_without_unit(&absy) + absx.div_without_unit(&gcd)?.mul_without_unit(&absy) } - pub fn log(x: KalkValue) -> KalkValue { + pub fn log(x: KalkValue) -> Result { let (real, _, unit) = as_number_or_return!(x.clone()); if x.has_imaginary() || real < 0f64 { // ln(z) / ln(10) - ln(x).div_without_unit(&KalkValue::from(10f64.ln())) + ln(x)?.div_without_unit(&KalkValue::from(10f64.ln())) } else { - KalkValue::Number(real.log10(), float!(0), unit) + Ok(KalkValue::Number(real.log10(), float!(0), unit)) } } - pub fn logx(x: KalkValue, y: KalkValue) -> KalkValue { + pub fn logx(x: KalkValue, y: KalkValue) -> Result { let (real, _, unit) = as_number_or_return!(x.clone()); let (real_rhs, _, _) = as_number_or_return!(y.clone()); if x.has_imaginary() || y.has_imaginary() || real < 0f64 || real_rhs < 0f64 { // ln(z) / ln(n) - ln(x).div_without_unit(&ln(y)) + ln(x)?.div_without_unit(&ln(y)?) } else { - KalkValue::Number(real.log10() / real_rhs.log10(), float!(0), unit) + Ok(KalkValue::Number( + real.log10() / real_rhs.log10(), + float!(0), + unit, + )) } } - pub fn ln(x: KalkValue) -> KalkValue { + pub fn ln(x: KalkValue) -> Result { let (real, _, unit) = as_number_or_return!(x.clone()); if x.has_imaginary() || real < 0f64 { - let r = abs(x.clone()); + let r = abs(x.clone())?; // ln|z| + i * arg z - ln(r).add_without_unit(&multiply_with_i(arg(x))) + ln(r)?.add_without_unit(&multiply_with_i(arg(x)?)?) } else { - KalkValue::Number(real.ln(), float!(0), unit) + Ok(KalkValue::Number(real.ln(), float!(0), unit)) } } - pub fn length(x: KalkValue) -> KalkValue { - match x { + pub fn length(x: KalkValue) -> Result { + Ok(match x { KalkValue::Vector(values) => KalkValue::from(values.len() as f64), KalkValue::Matrix(rows) => KalkValue::from(rows.len() as f64), _ => KalkValue::from(0f64), - } + }) } - pub fn max(x: KalkValue) -> KalkValue { + pub fn max(x: KalkValue) -> Result { let values = as_vector_or_return!(x); let mut max = &values[0]; for value in &values { - if let KalkValue::Boolean(greater) = value.greater_than_without_unit(max) { + if let KalkValue::Boolean(greater) = value.greater_than_without_unit(max)? { if greater { max = value; } } } - max.clone() + Ok(max.clone()) } - pub fn min(x: KalkValue) -> KalkValue { + pub fn min(x: KalkValue) -> Result { let values = as_vector_or_return!(x); let mut min = &values[0]; for value in &values { - if let KalkValue::Boolean(less) = value.less_than_without_unit(min) { + if let KalkValue::Boolean(less) = value.less_than_without_unit(min)? { if less { min = value; } } } - min.clone() + Ok(min.clone()) } - pub fn nth_root(x: KalkValue, n: KalkValue) -> KalkValue { - x.pow_without_unit(&KalkValue::from(1f64).div_without_unit(&n)) + pub fn nth_root(x: KalkValue, n: KalkValue) -> Result { + x.pow_without_unit(&KalkValue::from(1f64).div_without_unit(&n)?) } - pub fn perms(x: KalkValue) -> KalkValue { - if let KalkValue::Vector(values) = sort(x) { + pub fn perms(x: KalkValue) -> Result { + if let KalkValue::Vector(values) = sort(x)? { let mut result: Vec> = vec![values]; // Permutations in lexographic order: https://www.baeldung.com/cs/array-generate-all-permutations @@ -795,19 +814,22 @@ pub mod funcs { // since the counter is of type usize, which // can't be negative. if i == 0 { - return KalkValue::Matrix(result); + return Ok(KalkValue::Matrix(result)); } } } if i == 0 { - return KalkValue::Matrix(result); + return Ok(KalkValue::Matrix(result)); } let pivot = if let KalkValue::Number(real, _, _) = &prev_values[i - 1] { real } else { - return KalkValue::nan(); + return Err(KalkError::UnexpectedType( + (&prev_values[i - 1]).get_type_name(), + vec![String::from("number")], + )); }; let mut j = prev_values.len() - 1; @@ -834,149 +856,161 @@ pub mod funcs { result.push(new_values); } } else { - KalkValue::nan() + Err(KalkError::UnexpectedType( + String::from("unknown"), + vec![String::from("vector")], + )) } } - pub fn prod(x: KalkValue) -> KalkValue { + pub fn prod(x: KalkValue) -> Result { let values = as_vector_or_return!(x); let mut prod = KalkValue::from(1f64); for value in values { - prod = prod.mul_without_unit(&value); + prod = prod.mul_without_unit(&value)?; } - prod + Ok(prod) } - pub fn re(x: KalkValue) -> KalkValue { + pub fn re(x: KalkValue) -> Result { let (real, _, unit) = as_number_or_return!(x); - KalkValue::Number(real, float!(0), unit) + Ok(KalkValue::Number(real, float!(0), unit)) } - pub fn round(x: KalkValue) -> KalkValue { + pub fn round(x: KalkValue) -> Result { let (real, imaginary, unit) = as_number_or_return!(x); - KalkValue::Number(real.round(), imaginary.round(), unit) + Ok(KalkValue::Number(real.round(), imaginary.round(), unit)) } - pub fn sec(x: KalkValue) -> KalkValue { - KalkValue::from(1f64).div_without_unit(&cos(x)) + pub fn sec(x: KalkValue) -> Result { + KalkValue::from(1f64).div_without_unit(&cos(x)?) } - pub fn sech(x: KalkValue) -> KalkValue { - KalkValue::from(1f64).div_without_unit(&cosh(x)) + pub fn sech(x: KalkValue) -> Result { + KalkValue::from(1f64).div_without_unit(&cosh(x)?) } - pub fn sin(x: KalkValue) -> KalkValue { + pub fn sin(x: KalkValue) -> Result { let (real, imaginary, unit) = as_number_or_return!(x); - KalkValue::Number( + Ok(KalkValue::Number( real.clone().sin() * imaginary.clone().cosh(), real.cos() * imaginary.sinh(), unit, - ) + )) } - pub fn sinh(x: KalkValue) -> KalkValue { + pub fn sinh(x: KalkValue) -> Result { let (real, imaginary, unit) = as_number_or_return!(x); - KalkValue::Number( + Ok(KalkValue::Number( real.clone().sinh() * imaginary.clone().cos(), real.cosh() * imaginary.sin(), unit, - ) + )) } - pub fn sgn(x: KalkValue) -> KalkValue { + pub fn sgn(x: KalkValue) -> Result { if x.has_imaginary() { - x.clone().div_without_unit(&abs(x)) + x.clone().div_without_unit(&abs(x)?) } else { let (real, _, unit) = as_number_or_return!(x); - KalkValue::Number(real.signum(), float!(0), unit) + Ok(KalkValue::Number(real.signum(), float!(0), unit)) } } - pub fn sort(x: KalkValue) -> KalkValue { + pub fn sort(x: KalkValue) -> Result { if let KalkValue::Vector(mut values) = x { values.sort_by(|a, b| { - if let KalkValue::Boolean(true) = a.eq_without_unit(b) { + if let KalkValue::Boolean(true) = + a.eq_without_unit(b).unwrap_or_else(|_| KalkValue::nan()) + { Ordering::Equal - } else if let KalkValue::Boolean(true) = a.greater_than_without_unit(b) { + } else if let KalkValue::Boolean(true) = a + .greater_than_without_unit(b) + .unwrap_or_else(|_| KalkValue::nan()) + { Ordering::Greater } else { Ordering::Less } }); - KalkValue::Vector(values) + Ok(KalkValue::Vector(values)) } else { - KalkValue::nan() + Err(KalkError::UnexpectedType( + x.get_type_name(), + vec![String::from("vector")], + )) } } - pub fn sqrt(x: KalkValue) -> KalkValue { + pub fn sqrt(x: KalkValue) -> Result { let (real, imaginary, unit) = as_number_or_return!(x.clone()); if x.has_imaginary() { - let (abs_real, _, abs_unit) = as_number_or_return!(abs(x)); + let abs = abs(x)?; + let (abs_real, _, abs_unit) = as_number_or_return!(abs); let r = abs_real; let a = real; let b = imaginary; // sqrt((|z| + a) / 2) + i * (b / |b|) * sqrt((|z| - a) / 2) - KalkValue::Number( + Ok(KalkValue::Number( ((r.clone() + a.clone()) / 2f64).sqrt(), (b.clone() / b.abs()) * ((r - a) / 2f64).sqrt(), abs_unit, - ) + )) } else if real < 0f64 { - KalkValue::Number(float!(0), real.abs().sqrt(), unit) + Ok(KalkValue::Number(float!(0), real.abs().sqrt(), unit)) } else { - KalkValue::Number(real.sqrt(), float!(0), unit) + Ok(KalkValue::Number(real.sqrt(), float!(0), unit)) } } - pub fn sum(x: KalkValue) -> KalkValue { + pub fn sum(x: KalkValue) -> Result { let values = as_vector_or_return!(x); let mut sum = KalkValue::from(0f64); for value in values { - sum = sum.add_without_unit(&value); + sum = sum.add_without_unit(&value)?; } - sum + Ok(sum) } - pub fn tan(x: KalkValue) -> KalkValue { + pub fn tan(x: KalkValue) -> Result { let has_imaginary = x.has_imaginary(); let (real, imaginary, unit) = as_number_or_return!(x); if has_imaginary { let a = real * 2f64; let b = imaginary * 2f64; - KalkValue::Number( + Ok(KalkValue::Number( a.clone().sin() / (a.clone().cos() + b.clone().cosh()), b.clone().sinh() / (a.cos() + b.cosh()), unit, - ) + )) } else { - KalkValue::Number(real.tan(), float!(0), unit) + Ok(KalkValue::Number(real.tan(), float!(0), unit)) } } - pub fn tanh(x: KalkValue) -> KalkValue { + pub fn tanh(x: KalkValue) -> Result { let has_imaginary = x.has_imaginary(); let (real, imaginary, unit) = as_number_or_return!(x); if has_imaginary { let a = real * 2f64; let b = imaginary * 2f64; - KalkValue::Number( + Ok(KalkValue::Number( a.clone().sinh() / (a.clone().cosh() + b.clone().cos()), b.clone().sin() / (a.cosh() + b.cos()), unit, - ) + )) } else { - KalkValue::Number(real.tanh(), float!(0), unit) + Ok(KalkValue::Number(real.tanh(), float!(0), unit)) } } #[allow(clippy::needless_range_loop)] - pub fn transpose(x: KalkValue) -> KalkValue { + pub fn transpose(x: KalkValue) -> Result { if let KalkValue::Matrix(rows) = x { let original_row_count = rows.len(); let original_column_count = rows.first().unwrap().len(); @@ -988,38 +1022,42 @@ pub mod funcs { } } - KalkValue::Matrix(result) + Ok(KalkValue::Matrix(result)) } else { - KalkValue::nan() + Err(KalkError::UnexpectedType( + x.get_type_name(), + vec![String::from("matrix")], + )) } } - pub fn trunc(x: KalkValue) -> KalkValue { + pub fn trunc(x: KalkValue) -> Result { let (real, imaginary, unit) = as_number_or_return!(x); - KalkValue::Number(real.trunc(), imaginary.trunc(), unit) + Ok(KalkValue::Number(real.trunc(), imaginary.trunc(), unit)) } - pub fn ncr(x: KalkValue, y: KalkValue) -> KalkValue { - factorial(x.clone()).div_without_unit( - &factorial(y.clone()).mul_without_unit(&factorial(x.sub_without_unit(&y))), + pub fn ncr(x: KalkValue, y: KalkValue) -> Result { + factorial(x.clone())?.div_without_unit( + &factorial(y.clone())?.mul_without_unit(&factorial(x.sub_without_unit(&y)?)?)?, ) } - pub fn npr(x: KalkValue, y: KalkValue) -> KalkValue { - factorial(x.clone()).div_without_unit(&factorial(x.sub_without_unit(&y))) + pub fn npr(x: KalkValue, y: KalkValue) -> Result { + factorial(x.clone())?.div_without_unit(&factorial(x.sub_without_unit(&y)?)?) } - fn multiply_with_i(z: KalkValue) -> KalkValue { + fn multiply_with_i(z: KalkValue) -> Result { let (real, imaginary, unit) = as_number_or_return!(z); // iz = i(a + bi) = -b + ai - KalkValue::Number(-imaginary, real, unit) + Ok(KalkValue::Number(-imaginary, real, unit)) } } #[cfg(test)] mod tests { use super::funcs::*; + use crate::errors::KalkError; use crate::float; use crate::prelude::KalkValue; use crate::test_helpers::cmp; @@ -1032,7 +1070,7 @@ mod tests { fn test_unary_funcs() { let in_out = vec![ ( - abs as fn(KalkValue) -> KalkValue, + abs as fn(KalkValue) -> Result, (3f64, 4f64), (5f64, 0f64), ), @@ -1042,7 +1080,8 @@ mod tests { ]; for (i, (func, input, expected_output)) in in_out.iter().enumerate() { - let actual_output = func(KalkValue::Number(float!(input.0), float!(input.1), None)); + let actual_output = + func(KalkValue::Number(float!(input.0), float!(input.1), None)).unwrap(); println!( "{} | expected: {}, {}", @@ -1063,7 +1102,7 @@ mod tests { fn test_binary_funcs() { let in_out = vec![ ( - gcd as fn(KalkValue, KalkValue) -> KalkValue, + gcd as fn(KalkValue, KalkValue) -> Result, ((12f64, 0f64), (18f64, 0f64)), (6f64, 0f64), ), @@ -1080,7 +1119,8 @@ mod tests { let actual_output = func( KalkValue::Number(float!(input.0 .0), float!(input.0 .1), None), KalkValue::Number(float!(input.1 .0), float!(input.1 .1), None), - ); + ) + .unwrap(); println!( "{} | expected: {}, {}", @@ -1110,7 +1150,7 @@ mod tests { ), ]; for (vec, expected_len) in vecs { - let permutations = if let KalkValue::Matrix(permutations) = perms(vec) { + let permutations = if let KalkValue::Matrix(permutations) = perms(vec).unwrap() { permutations } else { unreachable!() @@ -1143,17 +1183,17 @@ mod tests { } assert_eq!( - transpose(to_matrix(vec![vec![1, 2], vec![3, 4]])), + transpose(to_matrix(vec![vec![1, 2], vec![3, 4]])).unwrap(), to_matrix(vec![vec![1, 3], vec![2, 4]]) ); assert_eq!( - transpose(to_matrix(vec![vec![1, 2], vec![3, 4], vec![5, 6]])), + transpose(to_matrix(vec![vec![1, 2], vec![3, 4], vec![5, 6]])).unwrap(), to_matrix(vec![vec![1, 3, 5], vec![2, 4, 6]]) ); assert_eq!( - transpose(to_matrix(vec![vec![1, 2, 3], vec![4, 5, 6], vec![7, 8, 9]])), + transpose(to_matrix(vec![vec![1, 2, 3], vec![4, 5, 6], vec![7, 8, 9]])).unwrap(), to_matrix(vec![vec![1, 4, 7], vec![2, 5, 8], vec![3, 6, 9]]) ); } @@ -1164,7 +1204,7 @@ mod tests { // Auto-generated using kalk/scripts/generate_funcs_test_cases.py let in_out = vec![ ( - arg as fn(KalkValue) -> KalkValue, + arg as fn(KalkValue) -> Result, (0.3f64, 0f64), (0.0f64, 0.0f64), ), @@ -1387,7 +1427,8 @@ mod tests { ]; for (i, (func, input, expected_output)) in in_out.iter().enumerate() { - let actual_output = func(KalkValue::Number(float!(input.0), float!(input.1), None)); + let actual_output = + func(KalkValue::Number(float!(input.0), float!(input.1), None)).unwrap(); let expected_has_nan_or_inf = expected_output.0.is_nan() || expected_output.0.is_infinite() diff --git a/kalk/src/prelude/regular.rs b/kalk/src/prelude/regular.rs index 8800682..faca5e0 100644 --- a/kalk/src/prelude/regular.rs +++ b/kalk/src/prelude/regular.rs @@ -1,39 +1,40 @@ pub mod special_funcs { - use crate::{as_number_or_return, float, kalk_value::KalkValue}; + use crate::{as_number_or_return, errors::KalkError, float, kalk_value::KalkValue}; - pub fn factorial(x: KalkValue) -> KalkValue { + pub fn factorial(x: KalkValue) -> Result { let (real, _, unit) = as_number_or_return!(x); // Round it a bit, to prevent floating point errors. - KalkValue::Number( + Ok(KalkValue::Number( (super::funcs::precise_gamma(real + 1f64) * 10e6f64).round() / 10e6f64, float!(0), unit, - ) + )) } } pub(crate) mod funcs { + use crate::errors::KalkError; use crate::kalk_value::KalkValue; use crate::prelude::funcs::abs; use crate::{as_number_or_return, float}; - pub fn arg(x: KalkValue) -> KalkValue { + pub fn arg(x: KalkValue) -> Result { let (real, imaginary, unit) = as_number_or_return!(x); // i(ln|x| - ln(x)) - KalkValue::Number(imaginary.atan2(real), float!(0), unit) + Ok(KalkValue::Number(imaginary.atan2(real), float!(0), unit)) } - pub fn gamma(x: KalkValue) -> KalkValue { + pub fn gamma(x: KalkValue) -> Result { let (real, _, unit) = as_number_or_return!(x); // Round it a bit, to prevent floating point errors. - KalkValue::Number( + Ok(KalkValue::Number( (precise_gamma(real) * 10e6f64).round() / 10e6f64, float!(0), unit, - ) + )) } // Matthias Eiholzer - https://gitlab.com/matthiaseiholzer/mathru/-/tree/master @@ -59,59 +60,65 @@ pub(crate) mod funcs { 2f64.sqrt() * pi.sqrt() * t.powf(x - 0.5f64) * (-t).exp() * a } - pub fn bitcmp(x: KalkValue) -> KalkValue { + pub fn bitcmp(x: KalkValue) -> Result { let (real, _, _) = as_number_or_return!(x); - KalkValue::from(!(real.round() as i32)) + Ok(KalkValue::from(!(real.round() as i32))) } - pub fn bitand(x: KalkValue, y: KalkValue) -> KalkValue { + pub fn bitand(x: KalkValue, y: KalkValue) -> Result { let (real, _, _) = as_number_or_return!(x); let (real_rhs, _, _) = as_number_or_return!(y); - KalkValue::from(real.round() as i32 & real_rhs.round() as i32) + Ok(KalkValue::from( + real.round() as i32 & real_rhs.round() as i32, + )) } - pub fn bitor(x: KalkValue, y: KalkValue) -> KalkValue { + pub fn bitor(x: KalkValue, y: KalkValue) -> Result { let (real, _, _) = as_number_or_return!(x); let (real_rhs, _, _) = as_number_or_return!(y); - KalkValue::from(real.round() as i32 | real_rhs.round() as i32) + Ok(KalkValue::from( + real.round() as i32 | real_rhs.round() as i32, + )) } - pub fn bitxor(x: KalkValue, y: KalkValue) -> KalkValue { + pub fn bitxor(x: KalkValue, y: KalkValue) -> Result { let (real, _, _) = as_number_or_return!(x); let (real_rhs, _, _) = as_number_or_return!(y); - KalkValue::from(real.round() as i32 ^ real_rhs.round() as i32) + Ok(KalkValue::from( + real.round() as i32 ^ real_rhs.round() as i32, + )) } - pub fn bitshift(x: KalkValue, y: KalkValue) -> KalkValue { + pub fn bitshift(x: KalkValue, y: KalkValue) -> Result { let (real, _, _) = as_number_or_return!(x); let (real_rhs, _, _) = as_number_or_return!(y); let x = real.round() as i32; let y = real_rhs.round() as i32; if y < 0 { - KalkValue::from(x >> y.abs()) + Ok(KalkValue::from(x >> y.abs())) } else { - KalkValue::from(x << y) + Ok(KalkValue::from(x << y)) } } - pub fn hypot(x: KalkValue, y: KalkValue) -> KalkValue { - let (real, imaginary, unit) = as_number_or_return!(x.clone()); - let (real_rhs, imaginary_rhs, _) = as_number_or_return!(y.clone()); + pub fn hypot(x: KalkValue, y: KalkValue) -> Result { + let (real, _, unit) = as_number_or_return!(x.clone()); + let (real_rhs, _, _) = as_number_or_return!(y.clone()); if x.has_imaginary() || y.has_imaginary() { - let abs_x = abs(x); - let abs_y = abs(y); + let abs_x = abs(x)?; + let abs_y = abs(y)?; crate::prelude::funcs::sqrt( abs_x .clone() - .mul_without_unit(&abs_x) - .add_without_unit(&abs_y.clone().mul_without_unit(&abs_y)), + .mul_without_unit(&abs_x)? + .add_without_unit(&abs_y.clone().mul_without_unit(&abs_y)?)?, ) } else { - KalkValue::Number(real.hypot(real_rhs), float!(0), unit) + Ok(KalkValue::Number(real.hypot(real_rhs), float!(0), unit)) } } } diff --git a/kalk/src/prelude/with_rug.rs b/kalk/src/prelude/with_rug.rs index 32be9dd..5f90284 100644 --- a/kalk/src/prelude/with_rug.rs +++ b/kalk/src/prelude/with_rug.rs @@ -1,93 +1,97 @@ pub mod special_funcs { - use crate::{as_number_or_return, float, prelude::KalkValue}; + use crate::{as_number_or_return, errors::KalkError, float, prelude::KalkValue}; - pub fn factorial(x: KalkValue) -> KalkValue { + pub fn factorial(x: KalkValue) -> Result { let (real, _, unit) = as_number_or_return!(x); - KalkValue::Number((real + 1f64).gamma(), float!(0), unit) + Ok(KalkValue::Number((real + 1f64).gamma(), float!(0), unit)) } } pub(crate) mod funcs { + use crate::errors::KalkError; use crate::kalk_value::KalkValue; use crate::prelude::funcs::abs; use crate::{as_number_or_return, float}; - pub fn arg(x: KalkValue) -> KalkValue { + pub fn arg(x: KalkValue) -> Result { let (real, imaginary, unit) = as_number_or_return!(x); - KalkValue::Number(imaginary.atan2(&real), float!(0), unit) + Ok(KalkValue::Number(imaginary.atan2(&real), float!(0), unit)) } - pub fn gamma(x: KalkValue) -> KalkValue { + pub fn gamma(x: KalkValue) -> Result { let (real, _, unit) = as_number_or_return!(x); - KalkValue::Number(real.gamma(), float!(0), unit) + Ok(KalkValue::Number(real.gamma(), float!(0), unit)) } - pub fn bitcmp(x: KalkValue) -> KalkValue { + pub fn bitcmp(x: KalkValue) -> Result { let (real, _, _) = as_number_or_return!(x); - KalkValue::from(!real.to_i32_saturating().unwrap_or(i32::MAX)) + Ok(KalkValue::from( + !real.to_i32_saturating().unwrap_or(i32::MAX), + )) } - pub fn bitand(x: KalkValue, y: KalkValue) -> KalkValue { + pub fn bitand(x: KalkValue, y: KalkValue) -> Result { let (real, _, _) = as_number_or_return!(x); let (real_rhs, _, _) = as_number_or_return!(y); - KalkValue::from( + Ok(KalkValue::from( real.to_i32_saturating().unwrap_or(i32::MAX) & real_rhs.to_i32_saturating().unwrap_or(i32::MAX), - ) + )) } - pub fn bitor(x: KalkValue, y: KalkValue) -> KalkValue { + pub fn bitor(x: KalkValue, y: KalkValue) -> Result { let (real, _, _) = as_number_or_return!(x); let (real_rhs, _, _) = as_number_or_return!(y); - KalkValue::from( + Ok(KalkValue::from( real.to_i32_saturating().unwrap_or(i32::MAX) | real_rhs.to_i32_saturating().unwrap_or(i32::MAX), - ) + )) } - pub fn bitxor(x: KalkValue, y: KalkValue) -> KalkValue { + pub fn bitxor(x: KalkValue, y: KalkValue) -> Result { let (real, _, _) = as_number_or_return!(x); let (real_rhs, _, _) = as_number_or_return!(y); - KalkValue::from( + Ok(KalkValue::from( real.to_i32_saturating().unwrap_or(i32::MAX) ^ real_rhs.to_i32_saturating().unwrap_or(i32::MAX), - ) + )) } - pub fn bitshift(x: KalkValue, y: KalkValue) -> KalkValue { + pub fn bitshift(x: KalkValue, y: KalkValue) -> Result { let (real, _, _) = as_number_or_return!(x); let (real_rhs, _, _) = as_number_or_return!(y); let x = real.to_i32_saturating().unwrap_or(i32::MAX) as i32; let y = real_rhs.to_i32_saturating().unwrap_or(i32::MAX) as i32; if y < 0 { - KalkValue::from(x >> y.abs()) + Ok(KalkValue::from(x >> y.abs())) } else { - KalkValue::from(x << y) + Ok(KalkValue::from(x << y)) } } - pub fn hypot(x: KalkValue, y: KalkValue) -> KalkValue { - let (real, _, unit) = as_number_or_return!(x.clone()); - let (real_rhs, _, _) = as_number_or_return!(y.clone()); - if x.has_imaginary() || y.has_imaginary() { - let abs_x = abs(x); - let abs_y = abs(y); + pub fn hypot(x: KalkValue, y: KalkValue) -> Result { + let is_complex = x.has_imaginary() || y.has_imaginary(); + let (real, imaginary, unit) = as_number_or_return!(x); + let (real_rhs, imaginary_rhs, unit_rhs) = as_number_or_return!(y); + if is_complex { + let abs_x = abs(KalkValue::Number(real, imaginary, unit))?; + let abs_y = abs(KalkValue::Number(real_rhs, imaginary_rhs, unit_rhs))?; crate::prelude::funcs::sqrt( abs_x .clone() - .mul_without_unit(&abs_x) - .add_without_unit(&abs_y.clone().mul_without_unit(&abs_y)), + .mul_without_unit(&abs_x)? + .add_without_unit(&abs_y.clone().mul_without_unit(&abs_y)?)?, ) } else { - KalkValue::Number(real.hypot(&real_rhs), float!(0), unit) + Ok(KalkValue::Number(real.hypot(&real_rhs), float!(0), unit)) } } }