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::{
ast::{ConditionalPiece, Expr, Identifier, RangedVar, Stmt},
errors::KalkError,
inverter,
lexer::TokenKind,
parser::{self, KalkError},
prelude,
parser, prelude,
symbol_table::SymbolTable,
};

View File

@ -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)]

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::{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)
}

View File

@ -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;

View File

@ -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<KalkValue, KalkError> {
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<KalkValue, KalkError> {
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<KalkValue, KalkError> {
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<KalkValue, KalkError> {
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<KalkValue, KalkError> {
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<KalkValue, KalkError> {
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<KalkValue, KalkError> {
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<KalkValue, KalkError> {
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<KalkValue, KalkError> {
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<KalkValue, KalkError> {
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<KalkValue, KalkError> {
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<KalkValue, KalkError> {
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<KalkValue, KalkError> {
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<KalkValue, KalkError> {
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<KalkValue, KalkError> {
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<KalkValue, KalkError> {
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<KalkValue, KalkError> {
// 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<KalkValue> = 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<KalkValue, KalkError> {
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<KalkValue, KalkError> {
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<KalkValue, KalkError> {
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<KalkValue, KalkError> {
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<KalkValue, KalkError> {
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<KalkValue, KalkError> {
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<KalkValue, KalkError> {
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<KalkValue, KalkError>,
) -> Result<KalkValue, KalkError> {
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<KalkValue, KalkError>,
) -> Result<KalkValue, KalkError> {
// 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<ScientificNotation> for String {
}
}
impl std::iter::Sum<KalkValue> for KalkValue {
/*impl std::iter::Sum<KalkValue> for KalkValue {
fn sum<I>(iter: I) -> KalkValue
where
I: std::iter::Iterator<Item = KalkValue>,
@ -1151,7 +1266,7 @@ impl std::iter::Sum<KalkValue> for KalkValue {
sum
}
}
}*/
impl From<KalkValue> 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));
}

View File

@ -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;

View File

@ -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<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.
///
/// `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 {
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);
// 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<KalkValue, KalkError> {
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<KalkValue, KalkError> {
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<KalkValue, KalkError> {
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_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_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_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_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<KalkValue, KalkError> {
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))
}
}
}

View File

@ -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<KalkValue, KalkError> {
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<KalkValue, KalkError> {
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);
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);
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_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<KalkValue, KalkError> {
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<KalkValue, KalkError> {
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<KalkValue, KalkError> {
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<KalkValue, KalkError> {
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))
}
}
}