Added error handling to prelude functions and operations

This commit is contained in:
PaddiM8 2022-04-23 23:43:06 +02:00
parent 136036f3f0
commit 5ac3a12251
11 changed files with 738 additions and 553 deletions

View File

@ -1,9 +1,9 @@
use crate::{ use crate::{
ast::{ConditionalPiece, Expr, Identifier, RangedVar, Stmt}, ast::{ConditionalPiece, Expr, Identifier, RangedVar, Stmt},
errors::KalkError,
inverter, inverter,
lexer::TokenKind, lexer::TokenKind,
parser::{self, KalkError}, parser, prelude,
prelude,
symbol_table::SymbolTable, symbol_table::SymbolTable,
}; };

View File

@ -3,11 +3,11 @@ use crate::ast;
use crate::ast::Expr; use crate::ast::Expr;
use crate::ast::Identifier; use crate::ast::Identifier;
use crate::ast::Stmt; use crate::ast::Stmt;
use crate::errors::KalkError;
use crate::float; use crate::float;
use crate::interpreter; use crate::interpreter;
use crate::kalk_value::KalkValue; use crate::kalk_value::KalkValue;
use crate::lexer::TokenKind; use crate::lexer::TokenKind;
use crate::parser::KalkError;
pub fn derive_func( pub fn derive_func(
context: &mut interpreter::Context, context: &mut interpreter::Context,
@ -17,8 +17,8 @@ pub fn derive_func(
const H: f64 = 0.000001; const H: f64 = 0.000001;
let unit = argument.get_unit().cloned(); let unit = argument.get_unit().cloned();
let argument_with_h = ast::build_literal_ast(&argument.clone().add_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 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 new_identifier = Identifier::from_name_and_primes(&name.pure_name, name.prime_count - 1);
let f_x_h = interpreter::eval_fn_call_expr( let f_x_h = interpreter::eval_fn_call_expr(
@ -35,8 +35,8 @@ pub fn derive_func(
)?; )?;
Ok(f_x_h Ok(f_x_h
.sub_without_unit(&f_x) .sub_without_unit(&f_x)?
.div_without_unit(&(2f64 * H).into()) .div_without_unit(&(2f64 * H).into())?
.round_if_needed()) .round_if_needed())
} }
@ -98,11 +98,11 @@ fn simpsons_rule(
const N: i32 = 900; const N: i32 = 900;
let a = interpreter::eval_expr(context, a_expr, None)?; let a = interpreter::eval_expr(context, a_expr, None)?;
let b = interpreter::eval_expr(context, b_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 { for i in 0..=N {
let variable_value = a let variable_value = a
.clone() .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( context.symbol_table.set(Stmt::VarDecl(
Identifier::from_full_name(integration_variable), Identifier::from_full_name(integration_variable),
Box::new(crate::ast::build_literal_ast(&variable_value)), Box::new(crate::ast::build_literal_ast(&variable_value)),
@ -116,7 +116,7 @@ fn simpsons_rule(
// factor * f(x_n) // factor * f(x_n)
let (mul_real, mul_imaginary, _) = as_number_or_zero!( 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_real += mul_real;
result_imaginary += mul_imaginary; result_imaginary += mul_imaginary;
@ -133,11 +133,11 @@ fn simpsons_rule(
let result = KalkValue::Number(result_real, result_imaginary, None); let result = KalkValue::Number(result_real, result_imaginary, None);
let (h_real, h_imaginary, h_unit) = as_number_or_zero!(h); 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_real,
3f64 / 8f64 * h_imaginary, 3f64 / 8f64 * h_imaginary,
h_unit, h_unit,
))) ))
} }
#[cfg(test)] #[cfg(test)]

83
kalk/src/errors.rs Normal file
View File

@ -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<usize>),
InconsistentColumnWidths,
InvalidComprehension(String),
InvalidNumberLiteral(String),
InvalidOperator,
InvalidUnit,
TimedOut,
VariableReferencesItself,
PiecewiseConditionsAreFalse,
EvaluationError(String),
UnexpectedToken(TokenKind, TokenKind),
UnexpectedType(String, Vec<String>),
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::<Vec<String>>().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."),
}
}
}

View File

@ -1,9 +1,9 @@
use crate::ast::{Expr, Stmt}; use crate::ast::{Expr, Stmt};
use crate::ast::{Identifier, RangedVar}; use crate::ast::{Identifier, RangedVar};
use crate::calculation_result::CalculationResult; use crate::calculation_result::CalculationResult;
use crate::errors::KalkError;
use crate::kalk_value::KalkValue; use crate::kalk_value::KalkValue;
use crate::lexer::TokenKind; use crate::lexer::TokenKind;
use crate::parser::KalkError;
use crate::parser::DECL_UNIT; use crate::parser::DECL_UNIT;
use crate::symbol_table::SymbolTable; use crate::symbol_table::SymbolTable;
use crate::{as_number_or_zero, calculus}; use crate::{as_number_or_zero, calculus};
@ -163,7 +163,7 @@ fn eval_binary_expr(
let left = eval_expr(context, left_expr, None)?; let left = eval_expr(context, left_expr, None)?;
let mut right = eval_expr(context, right_expr, None)?; let mut right = eval_expr(context, right_expr, None)?;
if let Expr::Unary(TokenKind::Percent, _) = right_expr { 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 { if let TokenKind::Star = op {
return Ok(right); return Ok(right);
} }
@ -184,8 +184,8 @@ fn eval_binary_expr(
TokenKind::LessOrEquals => left.less_or_equals(context, right), TokenKind::LessOrEquals => left.less_or_equals(context, right),
TokenKind::And => left.and(&right), TokenKind::And => left.and(&right),
TokenKind::Or => left.or(&right), TokenKind::Or => left.or(&right),
_ => KalkValue::from(1f64), _ => Ok(KalkValue::from(1f64)),
}; }?;
if unit.is_some() { if unit.is_some() {
if let KalkValue::Number(real, imaginary, _) = result { if let KalkValue::Number(real, imaginary, _) = result {
@ -205,9 +205,9 @@ fn eval_unary_expr(
let num = eval_expr(context, expr, unit)?; let num = eval_expr(context, expr, unit)?;
match op { match op {
TokenKind::Minus => Ok(num.mul(context, KalkValue::from(-1f64))), TokenKind::Minus => num.mul(context, KalkValue::from(-1f64)),
TokenKind::Percent => Ok(num.mul(context, KalkValue::from(0.01f64))), TokenKind::Percent => num.mul(context, KalkValue::from(0.01f64)),
TokenKind::Exclamation => Ok(prelude::special_funcs::factorial(num)), TokenKind::Exclamation => prelude::special_funcs::factorial(num),
_ => Err(KalkError::InvalidOperator), _ => Err(KalkError::InvalidOperator),
} }
} }
@ -397,10 +397,8 @@ pub(crate) fn eval_fn_call_expr(
values.push(value); values.push(value);
} }
return Ok( return prelude::call_vector_func(&identifier.full_name, KalkValue::Vector(values))
prelude::call_vector_func(&identifier.full_name, KalkValue::Vector(values)) .unwrap_or_else(|| Ok(KalkValue::nan()));
.unwrap_or_else(KalkValue::nan),
);
} }
// Prelude // Prelude
@ -433,9 +431,7 @@ pub(crate) fn eval_fn_call_expr(
}; };
if let Some((result, _)) = prelude_func { if let Some((result, _)) = prelude_func {
// If the result is nan and only one argument was given, if result.is_err() && expressions.len() == 1 {
// it may be due to incompatible types.
if result.is_nan() && expressions.len() == 1 {
let x = eval_expr(context, &expressions[0], None)?; let x = eval_expr(context, &expressions[0], None)?;
// If a vector/matrix was given, call the function on every item // If a vector/matrix was given, call the function on every item
@ -450,7 +446,7 @@ pub(crate) fn eval_fn_call_expr(
value, value,
&context.angle_unit.clone(), &context.angle_unit.clone(),
) { ) {
new_values.push(result.0); new_values.push(result.0?);
} else { } else {
success = false; success = false;
break; break;
@ -472,7 +468,7 @@ pub(crate) fn eval_fn_call_expr(
value, value,
&context.angle_unit.clone(), &context.angle_unit.clone(),
) { ) {
new_row.push(result.0); new_row.push(result.0?);
} else { } else {
success = false; success = false;
break; break;
@ -488,8 +484,9 @@ pub(crate) fn eval_fn_call_expr(
} }
} }
return Ok(result); return result;
} }
// Symbol Table // Symbol Table
let stmt_definition = context.symbol_table.get_fn(&identifier.full_name).cloned(); 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)?; let eval = eval_expr(context, expression, None)?;
if sum_else_prod { if sum_else_prod {
sum = sum.add(context, eval); sum = sum.add(context, eval)?;
} else { } else {
sum = sum.mul(context, eval); sum = sum.mul(context, eval)?;
} }
} }
@ -829,7 +826,7 @@ mod tests {
} }
#[cfg(not(feature = "rug"))] #[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) Context::new(symbol_table, angle_unit, None)
} }

View File

@ -1,7 +1,7 @@
use crate::ast::Identifier; use crate::ast::Identifier;
use crate::ast::{Expr, Stmt}; use crate::ast::{Expr, Stmt};
use crate::errors::KalkError;
use crate::lexer::TokenKind; use crate::lexer::TokenKind;
use crate::parser::KalkError;
use crate::prelude; use crate::prelude;
use crate::symbol_table::SymbolTable; use crate::symbol_table::SymbolTable;
use lazy_static::lazy_static; use lazy_static::lazy_static;

View File

@ -14,6 +14,7 @@ pub use regular::*;
mod rounding; mod rounding;
use crate::ast::Expr; use crate::ast::Expr;
use crate::errors::KalkError;
use crate::radix; use crate::radix;
use wasm_bindgen::prelude::*; use wasm_bindgen::prelude::*;
@ -66,7 +67,10 @@ macro_rules! as_number_or_return {
unit, unit,
) )
} else { } 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) => {{ ($x:expr) => {{
if let KalkValue::Vector(values) = $x { if let KalkValue::Vector(values) = $x {
if values.len() == 0 { if values.len() == 0 {
return KalkValue::nan(); return Err(KalkError::Expected(String::from("a non-empty vector")));
} }
values values
} else { } 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) 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 { pub fn to_string_big(&self) -> String {
if let KalkValue::Number(real, imaginary, _) = self { if let KalkValue::Number(real, imaginary, _) = self {
if !self.has_imaginary() { if !self.has_imaginary() {
@ -532,7 +548,7 @@ impl KalkValue {
self, self,
context: &mut crate::interpreter::Context, context: &mut crate::interpreter::Context,
rhs: KalkValue, rhs: KalkValue,
) -> KalkValue { ) -> Result<KalkValue, KalkError> {
let right = calculate_unit(context, &self, rhs.clone()).unwrap_or(rhs); let right = calculate_unit(context, &self, rhs.clone()).unwrap_or(rhs);
self.add_without_unit(&right) self.add_without_unit(&right)
} }
@ -541,7 +557,7 @@ impl KalkValue {
self, self,
context: &mut crate::interpreter::Context, context: &mut crate::interpreter::Context,
rhs: KalkValue, rhs: KalkValue,
) -> KalkValue { ) -> Result<KalkValue, KalkError> {
let right = calculate_unit(context, &self, rhs.clone()).unwrap_or(rhs); let right = calculate_unit(context, &self, rhs.clone()).unwrap_or(rhs);
self.sub_without_unit(&right) self.sub_without_unit(&right)
} }
@ -550,7 +566,7 @@ impl KalkValue {
self, self,
context: &mut crate::interpreter::Context, context: &mut crate::interpreter::Context,
rhs: KalkValue, rhs: KalkValue,
) -> KalkValue { ) -> Result<KalkValue, KalkError> {
let right = calculate_unit(context, &self, rhs.clone()).unwrap_or(rhs); let right = calculate_unit(context, &self, rhs.clone()).unwrap_or(rhs);
self.mul_without_unit(&right) self.mul_without_unit(&right)
} }
@ -559,7 +575,7 @@ impl KalkValue {
self, self,
context: &mut crate::interpreter::Context, context: &mut crate::interpreter::Context,
rhs: KalkValue, rhs: KalkValue,
) -> KalkValue { ) -> Result<KalkValue, KalkError> {
let right = calculate_unit(context, &self, rhs.clone()).unwrap_or(rhs); let right = calculate_unit(context, &self, rhs.clone()).unwrap_or(rhs);
self.div_without_unit(&right) self.div_without_unit(&right)
} }
@ -568,7 +584,7 @@ impl KalkValue {
self, self,
context: &mut crate::interpreter::Context, context: &mut crate::interpreter::Context,
rhs: KalkValue, rhs: KalkValue,
) -> KalkValue { ) -> Result<KalkValue, KalkError> {
let right = calculate_unit(context, &self, rhs.clone()).unwrap_or(rhs); let right = calculate_unit(context, &self, rhs.clone()).unwrap_or(rhs);
self.pow_without_unit(&right) self.pow_without_unit(&right)
} }
@ -577,8 +593,8 @@ impl KalkValue {
self, self,
context: &mut crate::interpreter::Context, context: &mut crate::interpreter::Context,
rhs: KalkValue, rhs: KalkValue,
) -> KalkValue { ) -> Result<KalkValue, KalkError> {
if let KalkValue::Number(real, _, _) = &self { Ok(if let KalkValue::Number(real, _, _) = &self {
let right = calculate_unit(context, &self, rhs.clone()).unwrap_or(rhs); let right = calculate_unit(context, &self, rhs.clone()).unwrap_or(rhs);
if let KalkValue::Number(right_real, _, right_unit) = right { if let KalkValue::Number(right_real, _, right_unit) = right {
KalkValue::Number(real % right_real, float!(0f64), right_unit) KalkValue::Number(real % right_real, float!(0f64), right_unit)
@ -587,10 +603,14 @@ impl KalkValue {
} }
} else { } else {
self 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<KalkValue, KalkError> {
let right = calculate_unit(context, &self, rhs.clone()).unwrap_or(rhs); let right = calculate_unit(context, &self, rhs.clone()).unwrap_or(rhs);
self.eq_without_unit(&right) self.eq_without_unit(&right)
} }
@ -599,7 +619,7 @@ impl KalkValue {
self, self,
context: &mut crate::interpreter::Context, context: &mut crate::interpreter::Context,
rhs: KalkValue, rhs: KalkValue,
) -> KalkValue { ) -> Result<KalkValue, KalkError> {
let right = calculate_unit(context, &self, rhs.clone()).unwrap_or(rhs); let right = calculate_unit(context, &self, rhs.clone()).unwrap_or(rhs);
self.not_eq_without_unit(&right) self.not_eq_without_unit(&right)
} }
@ -608,7 +628,7 @@ impl KalkValue {
self, self,
context: &mut crate::interpreter::Context, context: &mut crate::interpreter::Context,
rhs: KalkValue, rhs: KalkValue,
) -> KalkValue { ) -> Result<KalkValue, KalkError> {
let right = calculate_unit(context, &self, rhs.clone()).unwrap_or(rhs); let right = calculate_unit(context, &self, rhs.clone()).unwrap_or(rhs);
self.greater_than_without_unit(&right) self.greater_than_without_unit(&right)
} }
@ -617,7 +637,7 @@ impl KalkValue {
self, self,
context: &mut crate::interpreter::Context, context: &mut crate::interpreter::Context,
rhs: KalkValue, rhs: KalkValue,
) -> KalkValue { ) -> Result<KalkValue, KalkError> {
let right = calculate_unit(context, &self, rhs.clone()).unwrap_or(rhs); let right = calculate_unit(context, &self, rhs.clone()).unwrap_or(rhs);
self.less_than_without_unit(&right) self.less_than_without_unit(&right)
} }
@ -626,13 +646,13 @@ impl KalkValue {
self, self,
context: &mut crate::interpreter::Context, context: &mut crate::interpreter::Context,
rhs: KalkValue, rhs: KalkValue,
) -> KalkValue { ) -> Result<KalkValue, KalkError> {
let right = calculate_unit(context, &self, rhs.clone()).unwrap_or(rhs); let right = calculate_unit(context, &self, rhs.clone()).unwrap_or(rhs);
if let (KalkValue::Boolean(greater), KalkValue::Boolean(equal)) = ( if let (KalkValue::Boolean(greater), KalkValue::Boolean(equal)) = (
self.greater_than_without_unit(&right), self.greater_than_without_unit(&right)?,
self.eq_without_unit(&right), self.eq_without_unit(&right)?,
) { ) {
KalkValue::Boolean(greater || equal) Ok(KalkValue::Boolean(greater || equal))
} else { } else {
unreachable!() unreachable!()
} }
@ -642,69 +662,93 @@ impl KalkValue {
self, self,
context: &mut crate::interpreter::Context, context: &mut crate::interpreter::Context,
rhs: KalkValue, rhs: KalkValue,
) -> KalkValue { ) -> Result<KalkValue, KalkError> {
let right = calculate_unit(context, &self, rhs.clone()).unwrap_or(rhs); let right = calculate_unit(context, &self, rhs.clone()).unwrap_or(rhs);
if let (KalkValue::Boolean(less), KalkValue::Boolean(equal)) = ( if let (KalkValue::Boolean(less), KalkValue::Boolean(equal)) = (
self.less_than_without_unit(&right), self.less_than_without_unit(&right)?,
self.eq_without_unit(&right), self.eq_without_unit(&right)?,
) { ) {
KalkValue::Boolean(less || equal) Ok(KalkValue::Boolean(less || equal))
} else { } else {
unreachable!() unreachable!()
} }
} }
pub(crate) fn and(self, rhs: &KalkValue) -> KalkValue { pub(crate) fn and(self, rhs: &KalkValue) -> Result<KalkValue, KalkError> {
match (self, rhs) { match (self, rhs) {
(KalkValue::Boolean(boolean), KalkValue::Boolean(boolean_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<KalkValue, KalkError> {
match (self, rhs) { match (self, rhs) {
(KalkValue::Boolean(boolean), KalkValue::Boolean(boolean_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<KalkValue, KalkError> {
match (self.clone(), rhs) { match (self.clone(), rhs) {
( (
KalkValue::Number(real, imaginary, _), KalkValue::Number(real, imaginary, _),
KalkValue::Number(real_rhs, imaginary_rhs, unit), 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(_)) => { (KalkValue::Matrix(_), _) | (_, KalkValue::Matrix(_)) => {
calculate_matrix(self, rhs, &KalkValue::add_without_unit) calculate_matrix(self, rhs, &KalkValue::add_without_unit)
} }
(KalkValue::Vector(_), _) | (_, KalkValue::Vector(_)) => { (KalkValue::Vector(_), _) | (_, KalkValue::Vector(_)) => {
calculate_vector(self, rhs, &KalkValue::add_without_unit) 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<KalkValue, KalkError> {
match (self.clone(), rhs) { match (self.clone(), rhs) {
( (
KalkValue::Number(real, imaginary, _), KalkValue::Number(real, imaginary, _),
KalkValue::Number(real_rhs, imaginary_rhs, unit), 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(_)) => { (KalkValue::Matrix(_), _) | (_, KalkValue::Matrix(_)) => {
calculate_matrix(self, rhs, &KalkValue::sub_without_unit) calculate_matrix(self, rhs, &KalkValue::sub_without_unit)
} }
(KalkValue::Vector(_), _) | (_, KalkValue::Vector(_)) => { (KalkValue::Vector(_), _) | (_, KalkValue::Vector(_)) => {
calculate_vector(self, rhs, &KalkValue::sub_without_unit) 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<KalkValue, KalkError> {
// Make sure matrix is always first to avoid having to match // Make sure matrix is always first to avoid having to match
// different orders in the next match expression. // different orders in the next match expression.
let (lhs, rhs) = match (&self, rhs) { let (lhs, rhs) = match (&self, rhs) {
@ -717,42 +761,38 @@ impl KalkValue {
( (
KalkValue::Number(real, imaginary, _), KalkValue::Number(real, imaginary, _),
KalkValue::Number(real_rhs, imaginary_rhs, unit), KalkValue::Number(real_rhs, imaginary_rhs, unit),
) => KalkValue::Number( ) => Ok(KalkValue::Number(
// (a + bi)(c + di) = ac + adi + bci + bdi² // (a + bi)(c + di) = ac + adi + bci + bdi²
real.clone() * real_rhs - imaginary.clone() * imaginary_rhs, real.clone() * real_rhs - imaginary.clone() * imaginary_rhs,
real.clone() * imaginary_rhs + imaginary * real_rhs, real.clone() * imaginary_rhs + imaginary * real_rhs,
unit.clone(), unit.clone(),
), )),
(KalkValue::Matrix(rows), KalkValue::Number(_, _, _)) => KalkValue::Matrix( (KalkValue::Matrix(_), KalkValue::Number(_, _, _)) => {
rows.iter() calculate_matrix(lhs.clone(), rhs, &KalkValue::mul_without_unit)
.map(|row| { }
row.iter()
.map(|x| x.clone().mul_without_unit(rhs))
.collect()
})
.collect(),
),
(KalkValue::Matrix(rows), KalkValue::Vector(values_rhs)) => { (KalkValue::Matrix(rows), KalkValue::Vector(values_rhs)) => {
if rows.first().unwrap().len() != values_rhs.len() { if rows.first().unwrap().len() != values_rhs.len() {
return KalkValue::nan(); return Err(KalkError::IncompatibleVectorsMatrixes);
} }
let mut new_values: Vec<KalkValue> = Vec::new(); let mut new_values: Vec<KalkValue> = Vec::new();
for row in rows { for row in rows {
new_values.push( let mut sum = KalkValue::from(0);
row.iter() for (x, y) in row.iter().zip(values_rhs) {
.zip(values_rhs) sum = sum
.map(|(x, y)| x.clone().mul_without_unit(y)) .clone()
.sum(), .add_without_unit(&x.clone().mul_without_unit(y)?)?;
)
} }
KalkValue::Vector(new_values) new_values.push(sum);
}
Ok(KalkValue::Vector(new_values))
} }
(KalkValue::Matrix(rows), KalkValue::Matrix(rows_rhs)) => { (KalkValue::Matrix(rows), KalkValue::Matrix(rows_rhs)) => {
let lhs_columns = rows.first().unwrap(); let lhs_columns = rows.first().unwrap();
if lhs_columns.len() != rows_rhs.len() { if lhs_columns.len() != rows_rhs.len() {
return KalkValue::nan(); return Err(KalkError::IncompatibleVectorsMatrixes);
} }
let rhs_columns = rows_rhs.first().unwrap(); let rhs_columns = rows_rhs.first().unwrap();
@ -767,55 +807,62 @@ impl KalkValue {
// For every value in the current lhs row // For every value in the current lhs row
for (k, value) in rows[i].iter().enumerate() { for (k, value) in rows[i].iter().enumerate() {
let value_rhs = &rows_rhs[k][j]; 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; 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)) => { (KalkValue::Vector(values), KalkValue::Vector(values_rhs)) => {
if values.len() != values_rhs.len() { if values.len() != values_rhs.len() {
return KalkValue::nan(); return Err(KalkError::IncompatibleVectorsMatrixes);
} }
let mut sum = KalkValue::from(0f64); let mut sum = KalkValue::from(0f64);
for (value, value_rhs) in values.iter().zip(values_rhs) { 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<KalkValue, KalkError> {
match (self.clone(), rhs.clone()) { match (self.clone(), rhs.clone()) {
(KalkValue::Number(real, _, _), KalkValue::Number(real_rhs, _, unit)) => { (KalkValue::Number(real, _, _), KalkValue::Number(real_rhs, _, unit)) => {
// Avoid unecessary calculations // Avoid unecessary calculations
if !self.has_imaginary() && !rhs.has_imaginary() { 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 { } else {
// Multiply both the numerator and denominator // Multiply both the numerator and denominator
// with the conjugate of the denominator, and divide. // with the conjugate of the denominator, and divide.
let conjugate = rhs.get_conjugate(); let conjugate = rhs.get_conjugate()?;
let (numerator, numerator_imaginary) = let (numerator, numerator_imaginary) =
self.mul_without_unit(&conjugate).values(); self.mul_without_unit(&conjugate)?.values();
let (denominator, _) = rhs.clone().mul_without_unit(&conjugate).values(); let (denominator, _) = rhs.clone().mul_without_unit(&conjugate)?.values();
KalkValue::Number( Ok(KalkValue::Number(
numerator / denominator.clone(), numerator / denominator.clone(),
numerator_imaginary / denominator, numerator_imaginary / denominator,
unit, unit,
) ))
} }
} }
(KalkValue::Matrix(_), _) | (_, KalkValue::Matrix(_)) => { (KalkValue::Matrix(_), _) | (_, KalkValue::Matrix(_)) => {
@ -824,11 +871,15 @@ impl KalkValue {
(KalkValue::Vector(_), _) | (_, KalkValue::Vector(_)) => { (KalkValue::Vector(_), _) | (_, KalkValue::Vector(_)) => {
calculate_vector(self, rhs, &KalkValue::div_without_unit) 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<KalkValue, KalkError> {
match (self.clone(), rhs) { match (self.clone(), rhs) {
( (
KalkValue::Number(real, imaginary, _), KalkValue::Number(real, imaginary, _),
@ -842,75 +893,92 @@ impl KalkValue {
let b = imaginary; let b = imaginary;
let c = real_rhs; let c = real_rhs;
let d = imaginary_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 raised = a.clone() * a + b.clone() * b;
let exp = let exp =
pow(raised.clone(), c.clone() / 2f64) * (-d.clone() * arg.clone()).exp(); pow(raised.clone(), c.clone() / 2f64) * (-d.clone() * arg.clone()).exp();
let polar = c * arg + d.clone() / 2f64 * raised.ln(); let polar = c * arg + d.clone() / 2f64 * raised.ln();
KalkValue::Number( Ok(KalkValue::Number(
polar.clone().cos() * exp.clone(), polar.clone().cos() * exp.clone(),
polar.sin() * exp, polar.sin() * exp,
unit.clone(), unit.clone(),
) ))
} else { } 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, _)) => { (KalkValue::Matrix(rows), KalkValue::Number(real, _, _)) => {
if real < &0f64 if real < &0f64 || real.clone().fract() > 0.000001f64 {
|| real.clone().fract() > 0.000001f64 return Err(KalkError::Expected(String::from("a positive integer")));
|| !(imaginary == &0f64 || imaginary == &-0f64) }
|| rows.len() != rows.first().unwrap().len()
{ if rhs.has_imaginary() {
return KalkValue::nan(); return Err(KalkError::ExpectedReal);
}
if rows.len() != rows.first().unwrap().len() {
return Err(KalkError::Expected(String::from("a square matrix")));
} }
if real == &0f64 { if real == &0f64 {
return KalkValue::from(1f64); return Ok(KalkValue::from(1f64));
} }
let mut result = KalkValue::from(1f64); let mut result = KalkValue::from(1f64);
for _ in 0..primitive!(real) as i32 { 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(_)) => { (KalkValue::Vector(_), _) | (_, KalkValue::Vector(_)) => {
calculate_vector(self, rhs, &KalkValue::pow_without_unit) 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<KalkValue, KalkError> {
match (self, rhs) { match (self, rhs) {
( (
KalkValue::Number(real, imaginary, _), KalkValue::Number(real, imaginary, _),
KalkValue::Number(real_rhs, imaginary_rhs, _), KalkValue::Number(real_rhs, imaginary_rhs, _),
) => KalkValue::Boolean( ) => Ok(KalkValue::Boolean(
(real.clone() - real_rhs.clone()).abs() < ACCEPTABLE_COMPARISON_MARGIN (real.clone() - real_rhs.clone()).abs() < ACCEPTABLE_COMPARISON_MARGIN
&& (imaginary.clone() - imaginary_rhs.clone()).abs() && (imaginary.clone() - imaginary_rhs.clone()).abs()
< ACCEPTABLE_COMPARISON_MARGIN, < ACCEPTABLE_COMPARISON_MARGIN,
), )),
(KalkValue::Boolean(boolean), KalkValue::Boolean(boolean_rhs)) => { (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)) => { (KalkValue::Matrix(rows), KalkValue::Matrix(rows_rhs)) => {
let mut matrices_are_equal = true; let mut matrices_are_equal = true;
for (row, row_rhs) in rows.iter().zip(rows_rhs) { for (row, row_rhs) in rows.iter().zip(rows_rhs) {
for (value, value_rhs) in row.iter().zip(row_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 { if !are_equal {
matrices_are_equal = false; 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)) => { (KalkValue::Vector(values), KalkValue::Vector(values_rhs)) => {
let mut vecs_are_equal = true; let mut vecs_are_equal = true;
for (value, value_rhs) in values.iter().zip(values_rhs) { 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 { if !are_equal {
vecs_are_equal = false; 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<KalkValue, KalkError> {
match (self, rhs) { match (self, rhs) {
( (
KalkValue::Number(real, imaginary, _), KalkValue::Number(real, imaginary, _),
KalkValue::Number(real_rhs, imaginary_rhs, _), KalkValue::Number(real_rhs, imaginary_rhs, _),
) => KalkValue::Boolean( ) => Ok(KalkValue::Boolean(
(real.clone() - real_rhs.clone()).abs() > ACCEPTABLE_COMPARISON_MARGIN (real.clone() - real_rhs.clone()).abs() > ACCEPTABLE_COMPARISON_MARGIN
|| (imaginary.clone() - imaginary_rhs.clone()).abs() || (imaginary.clone() - imaginary_rhs.clone()).abs()
> ACCEPTABLE_COMPARISON_MARGIN, > ACCEPTABLE_COMPARISON_MARGIN,
), )),
(KalkValue::Boolean(boolean), KalkValue::Boolean(boolean_rhs)) => { (KalkValue::Boolean(boolean), KalkValue::Boolean(boolean_rhs)) => {
KalkValue::Boolean(boolean != boolean_rhs) Ok(KalkValue::Boolean(boolean != boolean_rhs))
} }
(KalkValue::Vector(_), KalkValue::Vector(_)) (KalkValue::Vector(_), KalkValue::Vector(_))
| (KalkValue::Matrix(_), KalkValue::Matrix(_)) => { | (KalkValue::Matrix(_), KalkValue::Matrix(_)) => {
if let KalkValue::Boolean(boolean) = self.eq_without_unit(rhs) { if let KalkValue::Boolean(boolean) = self.eq_without_unit(rhs)? {
KalkValue::Boolean(!boolean) Ok(KalkValue::Boolean(!boolean))
} else { } 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<KalkValue, KalkError> {
if self.has_imaginary() || rhs.has_imaginary() { if self.has_imaginary() || rhs.has_imaginary() {
return KalkValue::nan(); return Err(KalkError::ExpectedReal);
} }
match (self, rhs) { match (self, rhs) {
(KalkValue::Number(real, _, _), KalkValue::Number(real_rhs, _, _)) => { (KalkValue::Number(real, _, _), KalkValue::Number(real_rhs, _, _)) => Ok(
KalkValue::Boolean(real.clone() - real_rhs.clone() > ACCEPTABLE_COMPARISON_MARGIN) KalkValue::Boolean(real.clone() - real_rhs.clone() > ACCEPTABLE_COMPARISON_MARGIN),
} ),
_ => KalkValue::nan(), _ => 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<KalkValue, KalkError> {
if self.has_imaginary() || rhs.has_imaginary() { if self.has_imaginary() || rhs.has_imaginary() {
return KalkValue::nan(); return Err(KalkError::ExpectedReal);
} }
match (self, rhs) { match (self, rhs) {
(KalkValue::Number(real, _, _), KalkValue::Number(real_rhs, _, _)) => { (KalkValue::Number(real, _, _), KalkValue::Number(real_rhs, _, _)) => Ok(
KalkValue::Boolean(real.clone() - real_rhs.clone() < -ACCEPTABLE_COMPARISON_MARGIN) KalkValue::Boolean(real.clone() - real_rhs.clone() < -ACCEPTABLE_COMPARISON_MARGIN),
} ),
_ => KalkValue::nan(), _ => 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<KalkValue, KalkError> {
match self { match self {
KalkValue::Number(real, imaginary, unit) => { KalkValue::Number(real, imaginary, unit) => Ok(KalkValue::Number(
KalkValue::Number(real.clone(), imaginary.clone() * (-1f64), unit.clone()) real.clone(),
} imaginary.clone() * (-1f64),
_ => KalkValue::nan(), 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( fn calculate_vector(
x: KalkValue, x: KalkValue,
y: &KalkValue, y: &KalkValue,
action: &dyn Fn(KalkValue, &KalkValue) -> KalkValue, action: &dyn Fn(KalkValue, &KalkValue) -> Result<KalkValue, KalkError>,
) -> KalkValue { ) -> Result<KalkValue, KalkError> {
match (x, y) { match (x, y) {
(KalkValue::Vector(values), KalkValue::Number(_, _, _)) => { (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::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)) => { (KalkValue::Vector(values), KalkValue::Vector(values_rhs)) => {
if values.len() == values_rhs.len() { if values.len() != values_rhs.len() {
KalkValue::Vector( return Err(KalkError::IncompatibleVectorsMatrixes);
values
.iter()
.zip(values_rhs)
.map(|(x, y)| action(x.clone(), y))
.collect(),
)
} else {
KalkValue::nan()
} }
let mut new_values = Vec::new();
for (value, value_rhs) in values.iter().zip(values_rhs) {
new_values.push(action(value.clone(), value_rhs)?);
} }
_ => KalkValue::nan(),
Ok(KalkValue::Vector(new_values))
}
(x, y) => Err(KalkError::IncompatibleTypesForOperation(
String::from("vector operation"),
x.get_type_name(),
y.get_type_name(),
)),
} }
} }
fn calculate_matrix( fn calculate_matrix(
x: KalkValue, x: KalkValue,
y: &KalkValue, y: &KalkValue,
action: &dyn Fn(KalkValue, &KalkValue) -> KalkValue, action: &dyn Fn(KalkValue, &KalkValue) -> Result<KalkValue, KalkError>,
) -> KalkValue { ) -> Result<KalkValue, KalkError> {
// Make sure matrix is always first to avoid having to match // Make sure matrix is always first to avoid having to match
// different orders in the next match expression. // different orders in the next match expression.
let (x, y) = match (&x, y) { let (x, y) = match (&x, y) {
@ -1053,14 +1158,20 @@ fn calculate_matrix(
}; };
match (x, y) { match (x, y) {
(KalkValue::Matrix(rows), KalkValue::Number(_, _, _)) => KalkValue::Matrix( (KalkValue::Matrix(rows), KalkValue::Number(_, _, _)) => {
rows.iter() let mut new_rows = Vec::new();
.map(|row| row.iter().map(|x| action(x.clone(), y)).collect()) for row in rows {
.collect(), 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)) => { (KalkValue::Matrix(rows), KalkValue::Vector(values_rhs)) => {
if rows.len() != values_rhs.len() { if rows.len() != values_rhs.len() {
return KalkValue::nan(); return Err(KalkError::IncompatibleVectorsMatrixes);
} }
let mut new_rows = Vec::new(); let mut new_rows = Vec::new();
@ -1070,17 +1181,17 @@ fn calculate_matrix(
new_rows new_rows
.last_mut() .last_mut()
.unwrap() .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)) => { (KalkValue::Matrix(rows), KalkValue::Matrix(rows_rhs)) => {
if rows.len() != rows_rhs.len() if rows.len() != rows_rhs.len()
|| rows.first().unwrap().len() != rows_rhs.first().unwrap().len() || rows.first().unwrap().len() != rows_rhs.first().unwrap().len()
{ {
return KalkValue::nan(); return Err(KalkError::IncompatibleVectorsMatrixes);
} }
let mut new_rows = Vec::new(); let mut new_rows = Vec::new();
@ -1090,13 +1201,17 @@ fn calculate_matrix(
new_rows new_rows
.last_mut() .last_mut()
.unwrap() .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<ScientificNotation> for String {
} }
} }
impl std::iter::Sum<KalkValue> for KalkValue { /*impl std::iter::Sum<KalkValue> for KalkValue {
fn sum<I>(iter: I) -> KalkValue fn sum<I>(iter: I) -> KalkValue
where where
I: std::iter::Iterator<Item = KalkValue>, I: std::iter::Iterator<Item = KalkValue>,
@ -1151,7 +1266,7 @@ impl std::iter::Sum<KalkValue> for KalkValue {
sum sum
} }
} }*/
impl From<KalkValue> for String { impl From<KalkValue> for String {
fn from(val: KalkValue) -> Self { fn from(val: KalkValue) -> Self {
@ -1211,7 +1326,8 @@ mod tests {
for (a, b, expected_result) in in_out { for (a, b, expected_result) in in_out {
let actual_result = KalkValue::Number(float!(a.0), float!(a.1), None) 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.to_f64(), expected_result.0);
assert_eq!(actual_result.imaginary_to_f64(), expected_result.1); assert_eq!(actual_result.imaginary_to_f64(), expected_result.1);
} }
@ -1228,7 +1344,8 @@ mod tests {
for (a, b, expected_result) in in_out { for (a, b, expected_result) in in_out {
let actual_result = KalkValue::Number(float!(a.0), float!(a.1), None) 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.to_f64(), expected_result.0);
assert_eq!(actual_result.imaginary_to_f64(), expected_result.1); assert_eq!(actual_result.imaginary_to_f64(), expected_result.1);
} }
@ -1245,7 +1362,8 @@ mod tests {
for (a, b, expected_result) in in_out { for (a, b, expected_result) in in_out {
let actual_result = KalkValue::Number(float!(a.0), float!(a.1), None) 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.to_f64(), expected_result.0);
assert_eq!(actual_result.imaginary_to_f64(), expected_result.1); assert_eq!(actual_result.imaginary_to_f64(), expected_result.1);
} }
@ -1261,7 +1379,8 @@ mod tests {
for (a, b, expected_result) in in_out { for (a, b, expected_result) in in_out {
let actual_result = KalkValue::Number(float!(a.0), float!(a.1), None) 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.to_f64(), expected_result.0);
assert_eq!(actual_result.imaginary_to_f64(), expected_result.1); assert_eq!(actual_result.imaginary_to_f64(), expected_result.1);
} }
@ -1289,7 +1408,8 @@ mod tests {
for (a, b, expected_result) in in_out { for (a, b, expected_result) in in_out {
let actual_result = KalkValue::Number(float!(a.0), float!(a.1), None) 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.to_f64(), expected_result.0));
assert!(cmp(actual_result.imaginary_to_f64(), expected_result.1)); assert!(cmp(actual_result.imaginary_to_f64(), expected_result.1));
} }

View File

@ -1,9 +1,11 @@
#![allow(clippy::unused_unit)] #![allow(clippy::unused_unit)]
#![allow(clippy::float_cmp)] #![allow(clippy::float_cmp)]
#![allow(clippy::clone_on_copy)] // the float type needs explicit cloning if the rug feature is enabled
mod analysis; mod analysis;
pub mod ast; pub mod ast;
pub mod calculation_result; pub mod calculation_result;
mod calculus; mod calculus;
mod errors;
mod integration_testing; mod integration_testing;
mod interpreter; mod interpreter;
mod inverter; mod inverter;

View File

@ -3,6 +3,7 @@ use std::cell::Cell;
use crate::analysis; use crate::analysis;
use crate::ast::Identifier; use crate::ast::Identifier;
use crate::calculation_result::CalculationResult; use crate::calculation_result::CalculationResult;
use crate::errors::KalkError;
use crate::{ use crate::{
ast::{Expr, Stmt}, ast::{Expr, Stmt},
interpreter, 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<usize>),
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::<Vec<String>>().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. /// Evaluate expressions/declarations and return the answer.
/// ///
/// `None` will be returned if the last statement is a declaration. /// `None` will be returned if the last statement is a declaration.

File diff suppressed because it is too large Load Diff

View File

@ -1,39 +1,40 @@
pub mod special_funcs { 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<KalkValue, KalkError> {
let (real, _, unit) = as_number_or_return!(x); let (real, _, unit) = as_number_or_return!(x);
// Round it a bit, to prevent floating point errors. // Round it a bit, to prevent floating point errors.
KalkValue::Number( Ok(KalkValue::Number(
(super::funcs::precise_gamma(real + 1f64) * 10e6f64).round() / 10e6f64, (super::funcs::precise_gamma(real + 1f64) * 10e6f64).round() / 10e6f64,
float!(0), float!(0),
unit, unit,
) ))
} }
} }
pub(crate) mod funcs { pub(crate) mod funcs {
use crate::errors::KalkError;
use crate::kalk_value::KalkValue; use crate::kalk_value::KalkValue;
use crate::prelude::funcs::abs; use crate::prelude::funcs::abs;
use crate::{as_number_or_return, float}; use crate::{as_number_or_return, float};
pub fn arg(x: KalkValue) -> KalkValue { pub fn arg(x: KalkValue) -> Result<KalkValue, KalkError> {
let (real, imaginary, unit) = as_number_or_return!(x); let (real, imaginary, unit) = as_number_or_return!(x);
// i(ln|x| - ln(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<KalkValue, KalkError> {
let (real, _, unit) = as_number_or_return!(x); let (real, _, unit) = as_number_or_return!(x);
// Round it a bit, to prevent floating point errors. // Round it a bit, to prevent floating point errors.
KalkValue::Number( Ok(KalkValue::Number(
(precise_gamma(real) * 10e6f64).round() / 10e6f64, (precise_gamma(real) * 10e6f64).round() / 10e6f64,
float!(0), float!(0),
unit, unit,
) ))
} }
// Matthias Eiholzer - https://gitlab.com/matthiaseiholzer/mathru/-/tree/master // 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 2f64.sqrt() * pi.sqrt() * t.powf(x - 0.5f64) * (-t).exp() * a
} }
pub fn bitcmp(x: KalkValue) -> KalkValue { pub fn bitcmp(x: KalkValue) -> Result<KalkValue, KalkError> {
let (real, _, _) = as_number_or_return!(x); 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<KalkValue, KalkError> {
let (real, _, _) = as_number_or_return!(x); let (real, _, _) = as_number_or_return!(x);
let (real_rhs, _, _) = as_number_or_return!(y); 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<KalkValue, KalkError> {
let (real, _, _) = as_number_or_return!(x); let (real, _, _) = as_number_or_return!(x);
let (real_rhs, _, _) = as_number_or_return!(y); 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<KalkValue, KalkError> {
let (real, _, _) = as_number_or_return!(x); let (real, _, _) = as_number_or_return!(x);
let (real_rhs, _, _) = as_number_or_return!(y); 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<KalkValue, KalkError> {
let (real, _, _) = as_number_or_return!(x); let (real, _, _) = as_number_or_return!(x);
let (real_rhs, _, _) = as_number_or_return!(y); let (real_rhs, _, _) = as_number_or_return!(y);
let x = real.round() as i32; let x = real.round() as i32;
let y = real_rhs.round() as i32; let y = real_rhs.round() as i32;
if y < 0 { if y < 0 {
KalkValue::from(x >> y.abs()) Ok(KalkValue::from(x >> y.abs()))
} else { } else {
KalkValue::from(x << y) Ok(KalkValue::from(x << y))
} }
} }
pub fn hypot(x: KalkValue, y: KalkValue) -> KalkValue { pub fn hypot(x: KalkValue, y: KalkValue) -> Result<KalkValue, KalkError> {
let (real, imaginary, unit) = as_number_or_return!(x.clone()); let (real, _, unit) = as_number_or_return!(x.clone());
let (real_rhs, imaginary_rhs, _) = as_number_or_return!(y.clone()); let (real_rhs, _, _) = as_number_or_return!(y.clone());
if x.has_imaginary() || y.has_imaginary() { if x.has_imaginary() || y.has_imaginary() {
let abs_x = abs(x); let abs_x = abs(x)?;
let abs_y = abs(y); let abs_y = abs(y)?;
crate::prelude::funcs::sqrt( crate::prelude::funcs::sqrt(
abs_x abs_x
.clone() .clone()
.mul_without_unit(&abs_x) .mul_without_unit(&abs_x)?
.add_without_unit(&abs_y.clone().mul_without_unit(&abs_y)), .add_without_unit(&abs_y.clone().mul_without_unit(&abs_y)?)?,
) )
} else { } else {
KalkValue::Number(real.hypot(real_rhs), float!(0), unit) Ok(KalkValue::Number(real.hypot(real_rhs), float!(0), unit))
} }
} }
} }

View File

@ -1,93 +1,97 @@
pub mod special_funcs { 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<KalkValue, KalkError> {
let (real, _, unit) = as_number_or_return!(x); 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 { pub(crate) mod funcs {
use crate::errors::KalkError;
use crate::kalk_value::KalkValue; use crate::kalk_value::KalkValue;
use crate::prelude::funcs::abs; use crate::prelude::funcs::abs;
use crate::{as_number_or_return, float}; use crate::{as_number_or_return, float};
pub fn arg(x: KalkValue) -> KalkValue { pub fn arg(x: KalkValue) -> Result<KalkValue, KalkError> {
let (real, imaginary, unit) = as_number_or_return!(x); 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<KalkValue, KalkError> {
let (real, _, unit) = as_number_or_return!(x); 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<KalkValue, KalkError> {
let (real, _, _) = as_number_or_return!(x); 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<KalkValue, KalkError> {
let (real, _, _) = as_number_or_return!(x); let (real, _, _) = as_number_or_return!(x);
let (real_rhs, _, _) = as_number_or_return!(y); let (real_rhs, _, _) = as_number_or_return!(y);
KalkValue::from( Ok(KalkValue::from(
real.to_i32_saturating().unwrap_or(i32::MAX) real.to_i32_saturating().unwrap_or(i32::MAX)
& real_rhs.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<KalkValue, KalkError> {
let (real, _, _) = as_number_or_return!(x); let (real, _, _) = as_number_or_return!(x);
let (real_rhs, _, _) = as_number_or_return!(y); let (real_rhs, _, _) = as_number_or_return!(y);
KalkValue::from( Ok(KalkValue::from(
real.to_i32_saturating().unwrap_or(i32::MAX) real.to_i32_saturating().unwrap_or(i32::MAX)
| real_rhs.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<KalkValue, KalkError> {
let (real, _, _) = as_number_or_return!(x); let (real, _, _) = as_number_or_return!(x);
let (real_rhs, _, _) = as_number_or_return!(y); let (real_rhs, _, _) = as_number_or_return!(y);
KalkValue::from( Ok(KalkValue::from(
real.to_i32_saturating().unwrap_or(i32::MAX) real.to_i32_saturating().unwrap_or(i32::MAX)
^ real_rhs.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<KalkValue, KalkError> {
let (real, _, _) = as_number_or_return!(x); let (real, _, _) = as_number_or_return!(x);
let (real_rhs, _, _) = as_number_or_return!(y); let (real_rhs, _, _) = as_number_or_return!(y);
let x = real.to_i32_saturating().unwrap_or(i32::MAX) as i32; 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; let y = real_rhs.to_i32_saturating().unwrap_or(i32::MAX) as i32;
if y < 0 { if y < 0 {
KalkValue::from(x >> y.abs()) Ok(KalkValue::from(x >> y.abs()))
} else { } else {
KalkValue::from(x << y) Ok(KalkValue::from(x << y))
} }
} }
pub fn hypot(x: KalkValue, y: KalkValue) -> KalkValue { pub fn hypot(x: KalkValue, y: KalkValue) -> Result<KalkValue, KalkError> {
let (real, _, unit) = as_number_or_return!(x.clone()); let is_complex = x.has_imaginary() || y.has_imaginary();
let (real_rhs, _, _) = as_number_or_return!(y.clone()); let (real, imaginary, unit) = as_number_or_return!(x);
if x.has_imaginary() || y.has_imaginary() { let (real_rhs, imaginary_rhs, unit_rhs) = as_number_or_return!(y);
let abs_x = abs(x); if is_complex {
let abs_y = abs(y); 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( crate::prelude::funcs::sqrt(
abs_x abs_x
.clone() .clone()
.mul_without_unit(&abs_x) .mul_without_unit(&abs_x)?
.add_without_unit(&abs_y.clone().mul_without_unit(&abs_y)), .add_without_unit(&abs_y.clone().mul_without_unit(&abs_y)?)?,
) )
} else { } else {
KalkValue::Number(real.hypot(&real_rhs), float!(0), unit) Ok(KalkValue::Number(real.hypot(&real_rhs), float!(0), unit))
} }
} }
} }