mirror of
https://github.com/PaddiM8/kalker.git
synced 2024-12-12 17:40:52 +01:00
Changed KalkNum to an enum, KalkValue, creating a better type system
This commit is contained in:
parent
83f20e035c
commit
92521f842b
@ -68,19 +68,19 @@ impl Identifier {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn build_literal_ast(kalk_num: &crate::kalk_num::KalkNum) -> Expr {
|
||||
if kalk_num.has_imaginary() {
|
||||
pub fn build_literal_ast(kalk_value: &crate::kalk_value::KalkValue) -> Expr {
|
||||
if kalk_value.has_imaginary() {
|
||||
Expr::Binary(
|
||||
Box::new(Expr::Literal(kalk_num.to_f64())),
|
||||
Box::new(Expr::Literal(kalk_value.to_f64())),
|
||||
TokenKind::Plus,
|
||||
Box::new(Expr::Binary(
|
||||
Box::new(Expr::Literal(kalk_num.imaginary_to_f64())),
|
||||
Box::new(Expr::Literal(kalk_value.imaginary_to_f64())),
|
||||
TokenKind::Star,
|
||||
Box::new(Expr::Var(Identifier::from_full_name("i"))),
|
||||
)),
|
||||
)
|
||||
} else {
|
||||
Expr::Literal(kalk_num.to_f64())
|
||||
Expr::Literal(kalk_value.to_f64())
|
||||
}
|
||||
}
|
||||
|
||||
|
73
kalk/src/calculation_result.rs
Normal file
73
kalk/src/calculation_result.rs
Normal file
@ -0,0 +1,73 @@
|
||||
use wasm_bindgen::prelude::wasm_bindgen;
|
||||
|
||||
use crate::kalk_value::{ComplexNumberType, KalkValue, ScientificNotation};
|
||||
|
||||
#[wasm_bindgen]
|
||||
pub struct CalculationResult {
|
||||
value: KalkValue,
|
||||
radix: u8,
|
||||
}
|
||||
|
||||
// Wraps around KalkValue since enums don't work
|
||||
// with the javascript bindings.
|
||||
#[wasm_bindgen]
|
||||
impl CalculationResult {
|
||||
pub(crate) fn new(value: KalkValue, radix: u8) -> Self {
|
||||
CalculationResult { value, radix }
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
pub(crate) fn get_value(self) -> KalkValue {
|
||||
self.value
|
||||
}
|
||||
|
||||
#[wasm_bindgen(js_name = toString)]
|
||||
pub fn to_string(&self) -> String {
|
||||
self.value.to_string()
|
||||
}
|
||||
|
||||
#[wasm_bindgen(js_name = toStringBig)]
|
||||
pub fn to_string_big(&self) -> String {
|
||||
self.value.to_string_big()
|
||||
}
|
||||
|
||||
#[wasm_bindgen(js_name = toPrettyString)]
|
||||
pub fn to_string_pretty(&self) -> String {
|
||||
if self.radix == 10 {
|
||||
self.value.to_string_pretty_radix(10)
|
||||
} else {
|
||||
format!(
|
||||
"{}\n{}",
|
||||
self.value.to_string_pretty_radix(10),
|
||||
self.value.to_string_pretty_radix(self.radix),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
#[wasm_bindgen(js_name = getValue)]
|
||||
pub fn to_f64(&self) -> f64 {
|
||||
self.value.to_f64()
|
||||
}
|
||||
|
||||
#[wasm_bindgen(js_name = getImaginaryValue)]
|
||||
pub fn imaginary_to_f64(&self) -> f64 {
|
||||
self.value.imaginary_to_f64()
|
||||
}
|
||||
|
||||
pub(crate) fn set_radix(&mut self, radix: u8) {
|
||||
self.radix = radix;
|
||||
}
|
||||
|
||||
#[wasm_bindgen(js_name = toScientificNotation)]
|
||||
pub fn to_scientific_notation_js(
|
||||
&self,
|
||||
complex_number_type: ComplexNumberType,
|
||||
) -> ScientificNotation {
|
||||
self.value.to_scientific_notation(complex_number_type)
|
||||
}
|
||||
|
||||
#[wasm_bindgen(js_name = estimate)]
|
||||
pub fn estimate_js(&self) -> Option<String> {
|
||||
self.value.estimate()
|
||||
}
|
||||
}
|
@ -1,19 +1,21 @@
|
||||
use crate::as_number_or_zero;
|
||||
use crate::ast;
|
||||
use crate::ast::Expr;
|
||||
use crate::ast::Identifier;
|
||||
use crate::ast::Stmt;
|
||||
use crate::float;
|
||||
use crate::interpreter;
|
||||
use crate::kalk_num::KalkNum;
|
||||
use crate::kalk_value::KalkValue;
|
||||
use crate::lexer::TokenKind;
|
||||
use crate::parser::CalcError;
|
||||
|
||||
pub fn derive_func(
|
||||
context: &mut interpreter::Context,
|
||||
name: &Identifier,
|
||||
argument: KalkNum,
|
||||
) -> Result<KalkNum, CalcError> {
|
||||
argument: KalkValue,
|
||||
) -> Result<KalkValue, CalcError> {
|
||||
const H: f64 = 0.000001;
|
||||
let unit = &argument.unit.to_string();
|
||||
let unit = &argument.get_unit();
|
||||
|
||||
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()));
|
||||
@ -34,7 +36,7 @@ pub fn integrate_with_unknown_variable(
|
||||
a: &Expr,
|
||||
b: &Expr,
|
||||
expr: &Expr,
|
||||
) -> Result<KalkNum, CalcError> {
|
||||
) -> Result<KalkValue, CalcError> {
|
||||
let mut integration_variable: Option<&str> = None;
|
||||
|
||||
// integral(a, b, expr dx)
|
||||
@ -66,7 +68,7 @@ pub fn integrate(
|
||||
b: &Expr,
|
||||
expr: &Expr,
|
||||
integration_variable: &str,
|
||||
) -> Result<KalkNum, CalcError> {
|
||||
) -> Result<KalkValue, CalcError> {
|
||||
Ok(simpsons_rule(context, a, b, expr, integration_variable)?.round_if_needed())
|
||||
}
|
||||
|
||||
@ -77,8 +79,9 @@ fn simpsons_rule(
|
||||
b_expr: &Expr,
|
||||
expr: &Expr,
|
||||
integration_variable: &str,
|
||||
) -> Result<KalkNum, CalcError> {
|
||||
let mut result = KalkNum::default();
|
||||
) -> Result<KalkValue, CalcError> {
|
||||
let mut result_real = float!(0);
|
||||
let mut result_imaginary = float!(0);
|
||||
let original_variable_value = context
|
||||
.symbol_table
|
||||
.get_and_remove_var(integration_variable);
|
||||
@ -86,26 +89,27 @@ fn simpsons_rule(
|
||||
const N: i32 = 900;
|
||||
let a = interpreter::eval_expr(context, a_expr, "")?;
|
||||
let b = interpreter::eval_expr(context, b_expr, "")?;
|
||||
let h = (b.sub_without_unit(a.clone())).div_without_unit(KalkNum::from(N));
|
||||
let h = (b.sub_without_unit(a.clone())).div_without_unit(KalkValue::from(N));
|
||||
for i in 0..=N {
|
||||
let variable_value = a
|
||||
.clone()
|
||||
.add_without_unit(KalkNum::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)),
|
||||
));
|
||||
|
||||
let factor = KalkNum::from(match i {
|
||||
let factor = KalkValue::from(match i {
|
||||
0 | N => 1,
|
||||
_ if i % 3 == 0 => 2,
|
||||
_ => 3,
|
||||
});
|
||||
|
||||
// factor * f(x_n)
|
||||
let mul = factor.mul_without_unit(interpreter::eval_expr(context, expr, "")?);
|
||||
result.value += mul.value;
|
||||
result.imaginary_value += mul.imaginary_value;
|
||||
let (mul_real, mul_imaginary, _) =
|
||||
as_number_or_zero!(factor.mul_without_unit(interpreter::eval_expr(context, expr, "")?));
|
||||
result_real += mul_real;
|
||||
result_imaginary += mul_imaginary;
|
||||
}
|
||||
|
||||
if let Some(value) = original_variable_value {
|
||||
@ -116,10 +120,13 @@ fn simpsons_rule(
|
||||
.get_and_remove_var(integration_variable);
|
||||
}
|
||||
|
||||
Ok(result.mul_without_unit(KalkNum::new_with_imaginary(
|
||||
3f64 / 8f64 * h.value,
|
||||
&h.unit,
|
||||
3f64 / 8f64 * h.imaginary_value,
|
||||
let result = KalkValue::Number(result_real, result_imaginary, String::new());
|
||||
let (h_real, h_imaginary, h_unit) = as_number_or_zero!(h);
|
||||
|
||||
Ok(result.mul_without_unit(KalkValue::Number(
|
||||
3f64 / 8f64 * h_real,
|
||||
3f64 / 8f64 * h_imaginary,
|
||||
h_unit,
|
||||
)))
|
||||
}
|
||||
|
||||
@ -128,8 +135,9 @@ mod tests {
|
||||
use crate::ast;
|
||||
use crate::calculus::Identifier;
|
||||
use crate::calculus::Stmt;
|
||||
use crate::float;
|
||||
use crate::interpreter;
|
||||
use crate::kalk_num::KalkNum;
|
||||
use crate::kalk_value::KalkValue;
|
||||
use crate::lexer::TokenKind::*;
|
||||
use crate::symbol_table::SymbolTable;
|
||||
use crate::test_helpers::*;
|
||||
@ -210,7 +218,7 @@ mod tests {
|
||||
let result = super::derive_func(
|
||||
&mut context,
|
||||
&Identifier::from_full_name("f'"),
|
||||
KalkNum::new_with_imaginary(KalkNum::from(2f64).value, "", KalkNum::from(3f64).value),
|
||||
KalkValue::Number(float!(2f64), float!(3f64), String::new()),
|
||||
)
|
||||
.unwrap();
|
||||
assert!(cmp(result.to_f64(), -4.5f64) || cmp(result.to_f64(), -4.499999f64));
|
||||
@ -255,10 +263,10 @@ mod tests {
|
||||
let result = super::integrate(
|
||||
&mut context,
|
||||
&*literal(2f64),
|
||||
&ast::build_literal_ast(&KalkNum::new_with_imaginary(
|
||||
KalkNum::from(3f64).value,
|
||||
"",
|
||||
KalkNum::from(4f64).value,
|
||||
&ast::build_literal_ast(&KalkValue::Number(
|
||||
float!(3f64),
|
||||
float!(4f64),
|
||||
String::new(),
|
||||
)),
|
||||
&*binary(var("x"), Star, var("i")),
|
||||
"x",
|
||||
|
@ -2,9 +2,9 @@
|
||||
mod tests {
|
||||
use std::{fs::File, io::Read, path::PathBuf};
|
||||
|
||||
use crate::kalk_num::KalkNum;
|
||||
use crate::kalk_value::KalkValue;
|
||||
|
||||
fn eval_file(name: &str) -> KalkNum {
|
||||
fn eval_file(name: &str) -> KalkValue {
|
||||
let mut path = PathBuf::new();
|
||||
path.push(env!("CARGO_MANIFEST_DIR"));
|
||||
path.push("..");
|
||||
@ -19,9 +19,17 @@ mod tests {
|
||||
.unwrap();
|
||||
let mut context = crate::parser::Context::new();
|
||||
|
||||
crate::parser::eval(&mut context, &file_content, 58)
|
||||
#[cfg(feature = "rug")]
|
||||
return crate::parser::eval(&mut context, &file_content, 58)
|
||||
.unwrap()
|
||||
.unwrap()
|
||||
.get_value();
|
||||
|
||||
#[cfg(not(feature = "rug"))]
|
||||
crate::parser::eval(&mut context, &file_content)
|
||||
.unwrap()
|
||||
.unwrap()
|
||||
.get_value()
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
@ -1,12 +1,13 @@
|
||||
use crate::ast::Identifier;
|
||||
use crate::ast::{Expr, Stmt};
|
||||
use crate::calculus;
|
||||
use crate::kalk_num::KalkNum;
|
||||
use crate::calculation_result::CalculationResult;
|
||||
use crate::kalk_value::KalkValue;
|
||||
use crate::lexer::TokenKind;
|
||||
use crate::parser::CalcError;
|
||||
use crate::parser::DECL_UNIT;
|
||||
use crate::prelude;
|
||||
use crate::symbol_table::SymbolTable;
|
||||
use crate::{as_number_or_zero, calculus};
|
||||
use crate::{float, prelude};
|
||||
|
||||
pub struct Context<'a> {
|
||||
pub symbol_table: &'a mut SymbolTable,
|
||||
@ -40,16 +41,19 @@ impl<'a> Context<'a> {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn interpret(&mut self, statements: Vec<Stmt>) -> Result<Option<KalkNum>, CalcError> {
|
||||
pub fn interpret(
|
||||
&mut self,
|
||||
statements: Vec<Stmt>,
|
||||
) -> Result<Option<CalculationResult>, CalcError> {
|
||||
for (i, stmt) in statements.iter().enumerate() {
|
||||
let num = eval_stmt(self, stmt)?;
|
||||
|
||||
// Insert the last value into the `ans` variable.
|
||||
self.symbol_table.set(if (&num.unit).len() > 0 {
|
||||
self.symbol_table.set(if num.has_unit() {
|
||||
Stmt::VarDecl(
|
||||
Identifier::from_full_name("ans"),
|
||||
Box::new(Expr::Unit(
|
||||
num.unit.clone(),
|
||||
num.get_unit().clone(),
|
||||
Box::new(crate::ast::build_literal_ast(&num)),
|
||||
)),
|
||||
)
|
||||
@ -62,7 +66,7 @@ impl<'a> Context<'a> {
|
||||
|
||||
if i == statements.len() - 1 {
|
||||
if let Stmt::Expr(_) = stmt {
|
||||
return Ok(Some(num));
|
||||
return Ok(Some(CalculationResult::new(num, 10)));
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -71,7 +75,7 @@ impl<'a> Context<'a> {
|
||||
}
|
||||
}
|
||||
|
||||
fn eval_stmt(context: &mut Context, stmt: &Stmt) -> Result<KalkNum, CalcError> {
|
||||
fn eval_stmt(context: &mut Context, stmt: &Stmt) -> Result<KalkValue, CalcError> {
|
||||
match stmt {
|
||||
Stmt::VarDecl(_, _) => eval_var_decl_stmt(context, stmt),
|
||||
Stmt::FnDecl(_, _, _) => eval_fn_decl_stmt(),
|
||||
@ -80,20 +84,20 @@ fn eval_stmt(context: &mut Context, stmt: &Stmt) -> Result<KalkNum, CalcError> {
|
||||
}
|
||||
}
|
||||
|
||||
fn eval_var_decl_stmt(context: &mut Context, stmt: &Stmt) -> Result<KalkNum, CalcError> {
|
||||
fn eval_var_decl_stmt(context: &mut Context, stmt: &Stmt) -> Result<KalkValue, CalcError> {
|
||||
context.symbol_table.insert(stmt.clone());
|
||||
Ok(KalkNum::from(1))
|
||||
Ok(KalkValue::from(1))
|
||||
}
|
||||
|
||||
fn eval_fn_decl_stmt() -> Result<KalkNum, CalcError> {
|
||||
Ok(KalkNum::from(1)) // Nothing needs to happen here, since the parser will already have added the FnDecl's to the symbol table.
|
||||
fn eval_fn_decl_stmt() -> Result<KalkValue, CalcError> {
|
||||
Ok(KalkValue::from(1)) // Nothing needs to happen here, since the parser will already have added the FnDecl's to the symbol table.
|
||||
}
|
||||
|
||||
fn eval_unit_decl_stmt() -> Result<KalkNum, CalcError> {
|
||||
Ok(KalkNum::from(1))
|
||||
fn eval_unit_decl_stmt() -> Result<KalkValue, CalcError> {
|
||||
Ok(KalkValue::from(1))
|
||||
}
|
||||
|
||||
fn eval_expr_stmt(context: &mut Context, expr: &Expr) -> Result<KalkNum, CalcError> {
|
||||
fn eval_expr_stmt(context: &mut Context, expr: &Expr) -> Result<KalkValue, CalcError> {
|
||||
eval_expr(context, &expr, "")
|
||||
}
|
||||
|
||||
@ -101,7 +105,7 @@ pub(crate) fn eval_expr(
|
||||
context: &mut Context,
|
||||
expr: &Expr,
|
||||
unit: &str,
|
||||
) -> Result<KalkNum, CalcError> {
|
||||
) -> Result<KalkValue, CalcError> {
|
||||
#[cfg(not(target_arch = "wasm32"))]
|
||||
if let (Ok(elapsed), Some(timeout)) = (context.start_time.elapsed(), context.timeout) {
|
||||
if elapsed.as_millis() >= timeout {
|
||||
@ -129,12 +133,12 @@ fn eval_binary_expr(
|
||||
op: &TokenKind,
|
||||
right_expr: &Expr,
|
||||
unit: &str,
|
||||
) -> Result<KalkNum, CalcError> {
|
||||
) -> Result<KalkValue, CalcError> {
|
||||
if let TokenKind::ToKeyword = op {
|
||||
// TODO: When the unit conversion function takes a Float instead of Expr,
|
||||
// move this to the match statement further down.
|
||||
if let Expr::Var(right_unit) = right_expr {
|
||||
let left_unit = eval_expr(context, left_expr, "")?.unit;
|
||||
let left_unit = eval_expr(context, left_expr, "")?.get_unit();
|
||||
return convert_unit(context, left_expr, &left_unit, &right_unit.full_name);
|
||||
// TODO: Avoid evaluating this twice.
|
||||
}
|
||||
@ -149,7 +153,7 @@ fn eval_binary_expr(
|
||||
}
|
||||
}
|
||||
|
||||
let mut result = match op {
|
||||
let result = match op {
|
||||
TokenKind::Plus => left.add(context, right),
|
||||
TokenKind::Minus => left.sub(context, right),
|
||||
TokenKind::Star => left.mul(context, right),
|
||||
@ -162,11 +166,13 @@ fn eval_binary_expr(
|
||||
TokenKind::LessThan => left.less_than(context, right),
|
||||
TokenKind::GreaterOrEquals => left.greater_or_equals(context, right),
|
||||
TokenKind::LessOrEquals => left.less_or_equals(context, right),
|
||||
_ => KalkNum::from(1),
|
||||
_ => KalkValue::from(1),
|
||||
};
|
||||
|
||||
if unit.len() > 0 {
|
||||
result.unit = unit.to_string();
|
||||
if let KalkValue::Number(real, imaginary, _) = result {
|
||||
return Ok(KalkValue::Number(real, imaginary, unit.to_string()));
|
||||
}
|
||||
};
|
||||
|
||||
Ok(result)
|
||||
@ -177,12 +183,12 @@ fn eval_unary_expr(
|
||||
op: &TokenKind,
|
||||
expr: &Expr,
|
||||
unit: &str,
|
||||
) -> Result<KalkNum, CalcError> {
|
||||
) -> Result<KalkValue, CalcError> {
|
||||
let num = eval_expr(context, &expr, unit)?;
|
||||
|
||||
match op {
|
||||
TokenKind::Minus => Ok(num.mul(context, KalkNum::from(-1f64))),
|
||||
TokenKind::Percent => Ok(num.mul(context, KalkNum::from(0.01f64))),
|
||||
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)),
|
||||
_ => Err(CalcError::InvalidOperator),
|
||||
}
|
||||
@ -192,7 +198,7 @@ fn eval_unit_expr(
|
||||
context: &mut Context,
|
||||
identifier: &str,
|
||||
expr: &Expr,
|
||||
) -> Result<KalkNum, CalcError> {
|
||||
) -> Result<KalkValue, CalcError> {
|
||||
let angle_unit = &context.angle_unit.clone();
|
||||
if (identifier == "rad" || identifier == "deg") && angle_unit != identifier {
|
||||
return convert_unit(context, expr, identifier, angle_unit);
|
||||
@ -206,7 +212,7 @@ pub fn convert_unit(
|
||||
expr: &Expr,
|
||||
from_unit: &str,
|
||||
to_unit: &str,
|
||||
) -> Result<KalkNum, CalcError> {
|
||||
) -> Result<KalkValue, CalcError> {
|
||||
if let Some(Stmt::UnitDecl(_, _, unit_def)) =
|
||||
context.symbol_table.get_unit(to_unit, from_unit).cloned()
|
||||
{
|
||||
@ -215,12 +221,8 @@ pub fn convert_unit(
|
||||
Box::new(expr.clone()),
|
||||
));
|
||||
|
||||
let num = eval_expr(context, &unit_def, "")?;
|
||||
Ok(KalkNum::new_with_imaginary(
|
||||
num.value,
|
||||
to_unit.into(),
|
||||
num.imaginary_value,
|
||||
))
|
||||
let (real, imaginary, _) = as_number_or_zero!(eval_expr(context, &unit_def, "")?);
|
||||
Ok(KalkValue::Number(real, imaginary, to_unit.into()))
|
||||
} else {
|
||||
Err(CalcError::InvalidUnit)
|
||||
}
|
||||
@ -230,7 +232,7 @@ fn eval_var_expr(
|
||||
context: &mut Context,
|
||||
identifier: &Identifier,
|
||||
unit: &str,
|
||||
) -> Result<KalkNum, CalcError> {
|
||||
) -> Result<KalkValue, CalcError> {
|
||||
// If there is a constant with this name, return a literal expression with its value
|
||||
if let Some(value) = prelude::CONSTANTS.get(identifier.full_name.as_ref() as &str) {
|
||||
return eval_expr(context, &Expr::Literal(*value), unit);
|
||||
@ -238,7 +240,7 @@ fn eval_var_expr(
|
||||
|
||||
if identifier.full_name == "n" {
|
||||
if let Some(value) = context.sum_n_value {
|
||||
return Ok(KalkNum::from(value));
|
||||
return Ok(KalkValue::from(value));
|
||||
}
|
||||
}
|
||||
|
||||
@ -254,17 +256,33 @@ fn eval_var_expr(
|
||||
}
|
||||
|
||||
#[allow(unused_variables)]
|
||||
fn eval_literal_expr(context: &mut Context, value: f64, unit: &str) -> Result<KalkNum, CalcError> {
|
||||
let mut num: KalkNum = value.into();
|
||||
num.unit = unit.into();
|
||||
#[cfg(feature = "rug")]
|
||||
fn eval_literal_expr(
|
||||
context: &mut Context,
|
||||
value: f64,
|
||||
unit: &str,
|
||||
) -> Result<KalkValue, CalcError> {
|
||||
let mut float = float!(value);
|
||||
float.set_prec(context.precision);
|
||||
|
||||
#[cfg(feature = "rug")]
|
||||
num.value.set_prec(context.precision);
|
||||
|
||||
Ok(num)
|
||||
Ok(KalkValue::Number(float, float!(0), unit.to_string()))
|
||||
}
|
||||
|
||||
fn eval_group_expr(context: &mut Context, expr: &Expr, unit: &str) -> Result<KalkNum, CalcError> {
|
||||
#[allow(unused_variables)]
|
||||
#[cfg(not(feature = "rug"))]
|
||||
fn eval_literal_expr(
|
||||
context: &mut Context,
|
||||
value: f64,
|
||||
unit: &str,
|
||||
) -> Result<KalkValue, CalcError> {
|
||||
Ok(KalkValue::Number(
|
||||
float!(value),
|
||||
float!(0),
|
||||
unit.to_string(),
|
||||
))
|
||||
}
|
||||
|
||||
fn eval_group_expr(context: &mut Context, expr: &Expr, unit: &str) -> Result<KalkValue, CalcError> {
|
||||
eval_expr(context, expr, unit)
|
||||
}
|
||||
|
||||
@ -273,7 +291,7 @@ pub(crate) fn eval_fn_call_expr(
|
||||
identifier: &Identifier,
|
||||
expressions: &[Expr],
|
||||
unit: &str,
|
||||
) -> Result<KalkNum, CalcError> {
|
||||
) -> Result<KalkValue, CalcError> {
|
||||
// Prelude
|
||||
let prelude_func = match expressions.len() {
|
||||
1 => {
|
||||
@ -327,27 +345,26 @@ pub(crate) fn eval_fn_call_expr(
|
||||
_ => unreachable!(),
|
||||
};
|
||||
let mut sum = if sum_else_prod {
|
||||
KalkNum::default()
|
||||
KalkValue::from(0f64)
|
||||
} else {
|
||||
KalkNum::from(1f64)
|
||||
KalkValue::from(1f64)
|
||||
};
|
||||
|
||||
for n in start..=end {
|
||||
context.sum_n_value = Some(n);
|
||||
let eval = eval_expr(context, &expressions[2], "")?;
|
||||
if sum_else_prod {
|
||||
sum.value += eval.value;
|
||||
sum.imaginary_value += eval.imaginary_value;
|
||||
sum = sum.add(context, eval);
|
||||
} else {
|
||||
sum.value *= eval.value;
|
||||
sum.imaginary_value *= eval.imaginary_value;
|
||||
sum = sum.mul(context, eval);
|
||||
}
|
||||
}
|
||||
|
||||
context.sum_n_value = None;
|
||||
sum.unit = unit.into();
|
||||
let (sum_real, sum_imaginary, _) = as_number_or_zero!(sum);
|
||||
|
||||
return Ok(sum);
|
||||
// Set the unit as well
|
||||
return Ok(KalkValue::Number(sum_real, sum_imaginary, unit.to_string()));
|
||||
}
|
||||
"integrate" => {
|
||||
return match expressions.len() {
|
||||
@ -447,9 +464,9 @@ fn eval_piecewise(
|
||||
context: &mut Context,
|
||||
pieces: &Vec<crate::ast::ConditionalPiece>,
|
||||
unit: &str,
|
||||
) -> Result<KalkNum, CalcError> {
|
||||
) -> Result<KalkValue, CalcError> {
|
||||
for piece in pieces {
|
||||
if let Some(condition_is_true) = eval_expr(context, &piece.condition, unit)?.boolean_value {
|
||||
if let KalkValue::Boolean(condition_is_true) = eval_expr(context, &piece.condition, unit)? {
|
||||
if condition_is_true {
|
||||
return Ok(eval_expr(context, &piece.expr, unit)?);
|
||||
}
|
||||
@ -491,7 +508,7 @@ mod tests {
|
||||
);
|
||||
}
|
||||
|
||||
fn interpret_with_unit(stmt: Stmt) -> Result<Option<KalkNum>, CalcError> {
|
||||
fn interpret_with_unit(stmt: Stmt) -> Result<Option<CalculationResult>, CalcError> {
|
||||
let mut symbol_table = SymbolTable::new();
|
||||
symbol_table
|
||||
.insert(DEG_RAD_UNIT.clone())
|
||||
@ -500,9 +517,9 @@ mod tests {
|
||||
context(&mut symbol_table, "rad").interpret(vec![stmt])
|
||||
}
|
||||
|
||||
fn interpret(stmt: Stmt) -> Result<Option<KalkNum>, CalcError> {
|
||||
fn interpret(stmt: Stmt) -> Result<Option<KalkValue>, CalcError> {
|
||||
if let Some(result) = interpret_with_unit(stmt)? {
|
||||
Ok(Some(result))
|
||||
Ok(Some(result.get_value()))
|
||||
} else {
|
||||
Ok(None)
|
||||
}
|
||||
@ -518,10 +535,18 @@ mod tests {
|
||||
Context::new(symbol_table, angle_unit, None)
|
||||
}
|
||||
|
||||
fn cmp(x: KalkNum, y: f64) -> bool {
|
||||
fn cmp(x: KalkValue, y: f64) -> bool {
|
||||
(x.to_f64() - y).abs() < 0.0001
|
||||
}
|
||||
|
||||
fn bool(x: &KalkValue) -> bool {
|
||||
if let KalkValue::Boolean(boolean_value) = x {
|
||||
*boolean_value
|
||||
} else {
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_literal() {
|
||||
let stmt = Stmt::Expr(literal(1f64));
|
||||
@ -550,40 +575,28 @@ mod tests {
|
||||
assert_eq!(interpret(pow).unwrap().unwrap().to_f64(), 8f64);
|
||||
|
||||
let result = interpret(equals).unwrap().unwrap();
|
||||
assert_eq!(
|
||||
(result.to_f64(), result.boolean_value.unwrap()),
|
||||
(0f64, false)
|
||||
);
|
||||
assert_eq!(bool(&result), false);
|
||||
assert!(result.to_f64().is_nan());
|
||||
|
||||
let result = interpret(not_equals).unwrap().unwrap();
|
||||
assert_eq!(
|
||||
(result.to_f64(), result.boolean_value.unwrap()),
|
||||
(0f64, true)
|
||||
);
|
||||
assert_eq!(bool(&result), true);
|
||||
assert!(result.to_f64().is_nan());
|
||||
|
||||
let result = interpret(greater_than).unwrap().unwrap();
|
||||
assert_eq!(
|
||||
(result.to_f64(), result.boolean_value.unwrap()),
|
||||
(0f64, false)
|
||||
);
|
||||
assert_eq!(bool(&result), false);
|
||||
assert!(result.to_f64().is_nan());
|
||||
|
||||
let result = interpret(less_than).unwrap().unwrap();
|
||||
assert_eq!(
|
||||
(result.to_f64(), result.boolean_value.unwrap()),
|
||||
(0f64, true)
|
||||
);
|
||||
assert_eq!(bool(&result), true);
|
||||
assert!(result.to_f64().is_nan());
|
||||
|
||||
let result = interpret(greater_or_equals).unwrap().unwrap();
|
||||
assert_eq!(
|
||||
(result.to_f64(), result.boolean_value.unwrap()),
|
||||
(0f64, false)
|
||||
);
|
||||
assert_eq!(bool(&result), false);
|
||||
assert!(result.to_f64().is_nan());
|
||||
|
||||
let result = interpret(less_or_equals).unwrap().unwrap();
|
||||
assert_eq!(
|
||||
(result.to_f64(), result.boolean_value.unwrap()),
|
||||
(0f64, true)
|
||||
);
|
||||
assert_eq!(bool(&result), true);
|
||||
assert!(result.to_f64().is_nan());
|
||||
}
|
||||
|
||||
#[test]
|
||||
@ -630,11 +643,16 @@ mod tests {
|
||||
rad_context
|
||||
.interpret(vec![implicit.clone()])
|
||||
.unwrap()
|
||||
.unwrap(),
|
||||
.unwrap()
|
||||
.get_value(),
|
||||
0.84147098
|
||||
));
|
||||
assert!(cmp(
|
||||
deg_context.interpret(vec![implicit]).unwrap().unwrap(),
|
||||
deg_context
|
||||
.interpret(vec![implicit])
|
||||
.unwrap()
|
||||
.unwrap()
|
||||
.get_value(),
|
||||
0.01745240
|
||||
));
|
||||
}
|
||||
|
@ -1,177 +0,0 @@
|
||||
use crate::kalk_num::*;
|
||||
use wasm_bindgen::prelude::*;
|
||||
|
||||
#[wasm_bindgen]
|
||||
#[derive(PartialEq, Debug, Clone, Default)]
|
||||
pub struct KalkNum {
|
||||
pub(crate) value: f64,
|
||||
pub(crate) unit: String,
|
||||
pub(crate) imaginary_value: f64,
|
||||
pub(crate) boolean_value: Option<bool>,
|
||||
pub(crate) other_radix: Option<u8>,
|
||||
}
|
||||
|
||||
#[wasm_bindgen]
|
||||
impl KalkNum {
|
||||
pub fn new(value: f64, unit: &str) -> Self {
|
||||
Self {
|
||||
value,
|
||||
unit: unit.to_string(),
|
||||
imaginary_value: 0f64,
|
||||
boolean_value: None,
|
||||
other_radix: None,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn new_with_imaginary(value: f64, unit: &str, imaginary_value: f64) -> Self {
|
||||
Self {
|
||||
value,
|
||||
unit: unit.to_string(),
|
||||
imaginary_value: if imaginary_value == -0f64 {
|
||||
0f64
|
||||
} else {
|
||||
imaginary_value
|
||||
},
|
||||
boolean_value: None,
|
||||
other_radix: None,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn from_imaginary(value: f64) -> Self {
|
||||
Self {
|
||||
value: 0f64,
|
||||
unit: String::new(),
|
||||
imaginary_value: value,
|
||||
boolean_value: None,
|
||||
other_radix: None,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn from_bool(value: bool) -> Self {
|
||||
Self {
|
||||
value: 0f64,
|
||||
unit: String::new(),
|
||||
imaginary_value: 0f64,
|
||||
boolean_value: Some(value),
|
||||
other_radix: None,
|
||||
}
|
||||
}
|
||||
|
||||
#[wasm_bindgen(js_name = getValue)]
|
||||
pub fn to_f64(&self) -> f64 {
|
||||
self.value
|
||||
}
|
||||
|
||||
#[wasm_bindgen(js_name = getImaginaryValue)]
|
||||
pub fn imaginary_to_f64(&self) -> f64 {
|
||||
self.imaginary_value
|
||||
}
|
||||
|
||||
pub fn to_i32(&self) -> i32 {
|
||||
self.value as i32
|
||||
}
|
||||
|
||||
#[wasm_bindgen(js_name = toString)]
|
||||
pub fn to_string_js(&self) -> String {
|
||||
self.to_string()
|
||||
}
|
||||
|
||||
#[wasm_bindgen(js_name = toPrettyString)]
|
||||
pub fn to_string_pretty_js(&self) -> String {
|
||||
self.to_string_pretty()
|
||||
}
|
||||
|
||||
#[wasm_bindgen(js_name = toStringBig)]
|
||||
pub fn to_string_big_js(&self) -> String {
|
||||
self.to_string_big()
|
||||
}
|
||||
|
||||
#[wasm_bindgen(js_name = isTooBig)]
|
||||
pub fn is_too_big_js(&self) -> bool {
|
||||
self.is_too_big()
|
||||
}
|
||||
|
||||
#[wasm_bindgen(js_name = toStringWithUnit)]
|
||||
pub fn to_string_with_unit_js(&self) -> String {
|
||||
self.to_string_with_unit()
|
||||
}
|
||||
|
||||
#[wasm_bindgen(js_name = hasUnit)]
|
||||
pub fn has_unit_js(&self) -> bool {
|
||||
self.has_unit()
|
||||
}
|
||||
|
||||
#[wasm_bindgen(js_name = getUnit)]
|
||||
pub fn get_unit(&self) -> String {
|
||||
self.unit.clone()
|
||||
}
|
||||
|
||||
#[wasm_bindgen(js_name = toScientificNotation)]
|
||||
pub fn to_scientific_notation_js(
|
||||
&self,
|
||||
complex_number_type: ComplexNumberType,
|
||||
) -> ScientificNotation {
|
||||
self.to_scientific_notation(complex_number_type)
|
||||
}
|
||||
|
||||
#[wasm_bindgen(js_name = estimate)]
|
||||
pub fn estimate_js(&self) -> Option<String> {
|
||||
self.estimate()
|
||||
}
|
||||
|
||||
pub(crate) fn pow(self, context: &mut crate::interpreter::Context, rhs: KalkNum) -> KalkNum {
|
||||
let right = calculate_unit(context, &self, rhs.clone()).unwrap_or(rhs);
|
||||
self.pow_without_unit(right)
|
||||
}
|
||||
|
||||
pub(crate) fn pow_without_unit(self, rhs: KalkNum) -> KalkNum {
|
||||
if self.has_imaginary() || rhs.has_imaginary() || (self.value < 0f64 && rhs.value < 1f64) {
|
||||
let a = self.value.clone();
|
||||
let b = self.imaginary_value.clone();
|
||||
let c = rhs.value;
|
||||
let d = rhs.imaginary_value;
|
||||
let arg = crate::prelude::funcs::arg(self).value;
|
||||
let raised = a.clone() * a + b.clone() * b;
|
||||
let exp = raised.clone().powf(c.clone() / 2f64) * (-d.clone() * arg.clone()).exp();
|
||||
let polar = c * arg + d / 2f64 * raised.ln();
|
||||
|
||||
KalkNum::new_with_imaginary(
|
||||
polar.clone().cos() * exp.clone(),
|
||||
&rhs.unit,
|
||||
polar.sin() * exp,
|
||||
)
|
||||
} else {
|
||||
KalkNum::new(self.value.powf(rhs.value), &rhs.unit)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<f64> for KalkNum {
|
||||
fn from(x: f64) -> Self {
|
||||
KalkNum::new(x, "")
|
||||
}
|
||||
}
|
||||
|
||||
impl From<f32> for KalkNum {
|
||||
fn from(x: f32) -> Self {
|
||||
KalkNum::new(x as f64, "")
|
||||
}
|
||||
}
|
||||
|
||||
impl From<i128> for KalkNum {
|
||||
fn from(x: i128) -> Self {
|
||||
KalkNum::new(x as f64, "")
|
||||
}
|
||||
}
|
||||
|
||||
impl From<i64> for KalkNum {
|
||||
fn from(x: i64) -> Self {
|
||||
KalkNum::new(x as f64, "")
|
||||
}
|
||||
}
|
||||
|
||||
impl From<i32> for KalkNum {
|
||||
fn from(x: i32) -> Self {
|
||||
KalkNum::new(x as f64, "")
|
||||
}
|
||||
}
|
@ -1,136 +0,0 @@
|
||||
use crate::kalk_num::*;
|
||||
|
||||
impl Default for KalkNum {
|
||||
fn default() -> Self {
|
||||
KalkNum::new(Float::with_val(63, 0), "")
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(PartialEq, Debug, Clone)]
|
||||
pub struct KalkNum {
|
||||
pub(crate) value: Float,
|
||||
pub(crate) unit: String,
|
||||
pub(crate) imaginary_value: Float,
|
||||
pub(crate) boolean_value: Option<bool>,
|
||||
pub(crate) other_radix: Option<u8>,
|
||||
}
|
||||
|
||||
impl KalkNum {
|
||||
pub fn new(value: Float, unit: &str) -> Self {
|
||||
let precision = value.prec();
|
||||
Self {
|
||||
value,
|
||||
unit: unit.to_string(),
|
||||
imaginary_value: Float::with_val(precision, 0),
|
||||
boolean_value: None,
|
||||
other_radix: None,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn new_with_imaginary(value: Float, unit: &str, imaginary_value: Float) -> Self {
|
||||
Self {
|
||||
value,
|
||||
unit: unit.to_string(),
|
||||
imaginary_value: if imaginary_value == -0f64 {
|
||||
imaginary_value.abs()
|
||||
} else {
|
||||
imaginary_value
|
||||
},
|
||||
boolean_value: None,
|
||||
other_radix: None,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn from_imaginary(value: Float) -> Self {
|
||||
Self {
|
||||
value: Float::with_val(value.prec(), 0),
|
||||
unit: String::new(),
|
||||
imaginary_value: value,
|
||||
boolean_value: None,
|
||||
other_radix: None,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn from_bool(value: bool) -> Self {
|
||||
Self {
|
||||
value: Float::with_val(63, 0),
|
||||
unit: String::new(),
|
||||
imaginary_value: Float::with_val(63, 0),
|
||||
boolean_value: Some(value),
|
||||
other_radix: None,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn to_f64(&self) -> f64 {
|
||||
self.value.to_f64_round(rug::float::Round::Nearest)
|
||||
}
|
||||
|
||||
pub fn imaginary_to_f64(&self) -> f64 {
|
||||
self.imaginary_value
|
||||
.to_f64_round(rug::float::Round::Nearest)
|
||||
}
|
||||
|
||||
pub fn to_i32(&self) -> i32 {
|
||||
self.value.to_i32_saturating().unwrap_or(0i32)
|
||||
}
|
||||
|
||||
pub fn get_unit(&self) -> &str {
|
||||
&self.unit
|
||||
}
|
||||
|
||||
pub(crate) fn pow(self, context: &mut crate::interpreter::Context, rhs: KalkNum) -> KalkNum {
|
||||
let right = calculate_unit(context, &self, rhs.clone()).unwrap_or(rhs);
|
||||
self.pow_without_unit(right)
|
||||
}
|
||||
|
||||
pub(crate) fn pow_without_unit(self, rhs: KalkNum) -> KalkNum {
|
||||
if self.has_imaginary() || rhs.has_imaginary() || (self.value < 0f64 && rhs.value < 1f64) {
|
||||
let a = self.value.clone();
|
||||
let b = self.imaginary_value.clone();
|
||||
let c = rhs.value;
|
||||
let d = rhs.imaginary_value;
|
||||
let arg = crate::prelude::funcs::arg(self).value;
|
||||
let raised = a.clone() * a + b.clone() * b;
|
||||
let exp = raised.clone().pow(c.clone() / 2f64) * (-d.clone() * arg.clone()).exp();
|
||||
let polar = c * arg + d / 2f64 * raised.ln();
|
||||
|
||||
KalkNum::new_with_imaginary(
|
||||
polar.clone().cos() * exp.clone(),
|
||||
&rhs.unit,
|
||||
polar.sin() * exp,
|
||||
)
|
||||
} else {
|
||||
KalkNum::new(self.value.pow(rhs.value), &rhs.unit)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<f64> for KalkNum {
|
||||
fn from(x: f64) -> Self {
|
||||
KalkNum::new(Float::with_val(63, x), "")
|
||||
}
|
||||
}
|
||||
|
||||
impl From<f32> for KalkNum {
|
||||
fn from(x: f32) -> Self {
|
||||
KalkNum::new(Float::with_val(63, x), "")
|
||||
}
|
||||
}
|
||||
|
||||
impl From<i128> for KalkNum {
|
||||
fn from(x: i128) -> Self {
|
||||
KalkNum::new(Float::with_val(63, x), "")
|
||||
}
|
||||
}
|
||||
|
||||
impl From<i64> for KalkNum {
|
||||
fn from(x: i64) -> Self {
|
||||
KalkNum::new(Float::with_val(63, x), "")
|
||||
}
|
||||
}
|
||||
|
||||
impl From<i32> for KalkNum {
|
||||
fn from(x: i32) -> Self {
|
||||
KalkNum::new(Float::with_val(63, x), "")
|
||||
}
|
||||
}
|
File diff suppressed because it is too large
Load Diff
27
kalk/src/kalk_value/regular.rs
Normal file
27
kalk/src/kalk_value/regular.rs
Normal file
@ -0,0 +1,27 @@
|
||||
use crate::kalk_value::*;
|
||||
|
||||
impl KalkValue {
|
||||
pub fn to_f64(&self) -> f64 {
|
||||
if let KalkValue::Number(real, _, _) = self {
|
||||
*real
|
||||
} else {
|
||||
f64::NAN
|
||||
}
|
||||
}
|
||||
|
||||
pub fn imaginary_to_f64(&self) -> f64 {
|
||||
if let KalkValue::Number(_, imaginary, _) = self {
|
||||
*imaginary
|
||||
} else {
|
||||
f64::NAN
|
||||
}
|
||||
}
|
||||
|
||||
pub fn values(self) -> (f64, f64) {
|
||||
if let KalkValue::Number(real, imaginary, _) = self {
|
||||
(real, imaginary)
|
||||
} else {
|
||||
(0f64, 0f64)
|
||||
}
|
||||
}
|
||||
}
|
166
kalk/src/kalk_value/rounding.rs
Normal file
166
kalk/src/kalk_value/rounding.rs
Normal file
@ -0,0 +1,166 @@
|
||||
use crate::float;
|
||||
|
||||
use super::{ComplexNumberType, KalkValue, CONSTANTS};
|
||||
|
||||
pub(super) fn estimate(
|
||||
input: &KalkValue,
|
||||
complex_number_type: ComplexNumberType,
|
||||
) -> Option<String> {
|
||||
let (real, imaginary, _) = if let KalkValue::Number(real, imaginary, unit) = input {
|
||||
(real, imaginary, unit)
|
||||
} else {
|
||||
return None;
|
||||
};
|
||||
|
||||
let (value, value_string) = match complex_number_type {
|
||||
ComplexNumberType::Real => (real, input.to_string_real(10)),
|
||||
ComplexNumberType::Imaginary => (imaginary, input.to_string_imaginary(10, true)),
|
||||
};
|
||||
|
||||
let fract = value.clone().fract().abs();
|
||||
let integer = value.clone().trunc();
|
||||
|
||||
#[cfg(feature = "rug")]
|
||||
let fract_as_string = fract.to_f64().to_string();
|
||||
#[cfg(not(feature = "rug"))]
|
||||
let fract_as_string = fract.to_string();
|
||||
|
||||
// If it's an integer, there's nothing that would be done to it.
|
||||
if fract == 0f64 {
|
||||
return None;
|
||||
}
|
||||
|
||||
// Eg. 0.5 to 1/2
|
||||
let as_abs_string = value_string.trim_start_matches("-").to_string();
|
||||
let sign = if value < &0f64 { "-" } else { "" };
|
||||
if as_abs_string.starts_with("0.5") {
|
||||
if as_abs_string.len() == 3 || (as_abs_string.len() > 6 && &as_abs_string[3..5] == "00") {
|
||||
return Some(format!("{}1/2", sign));
|
||||
}
|
||||
}
|
||||
|
||||
// Eg. 1.33333333 to 1 + 1/3
|
||||
if fract_as_string.len() >= 7 {
|
||||
let first_five_decimals = &fract_as_string[2..7];
|
||||
if first_five_decimals == "33333" || first_five_decimals == "66666" {
|
||||
let fraction = match first_five_decimals.as_ref() {
|
||||
"33333" => "1/3",
|
||||
"66666" => "2/3",
|
||||
_ => "?",
|
||||
};
|
||||
|
||||
if integer == 0f64 {
|
||||
return Some(format!("{}{}", sign, fraction));
|
||||
} else {
|
||||
let explicit_sign = if sign == "" { "+" } else { "-" };
|
||||
return Some(format!(
|
||||
"{} {} {}",
|
||||
trim_zeroes(&integer.to_string()),
|
||||
explicit_sign,
|
||||
fraction
|
||||
));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Match with common numbers, eg. π, 2π/3, √2
|
||||
if as_abs_string.len() >= 8 {
|
||||
if let Some(constant) = CONSTANTS.get(&as_abs_string[0..8]) {
|
||||
return Some(format!("{}{}", sign, constant.to_string()));
|
||||
}
|
||||
}
|
||||
|
||||
// If the value squared (and rounded) is an integer,
|
||||
// eg. x² is an integer,
|
||||
// then it can be expressed as sqrt(x²).
|
||||
// Ignore it if the square root of the result is an integer.
|
||||
if fract != 0f64 {
|
||||
let squared = KalkValue::Number(value.clone() * value, float!(0), String::new())
|
||||
.round_if_needed()
|
||||
.values()
|
||||
.0;
|
||||
if squared.clone().sqrt().fract() != 0f64 && squared.clone().fract() == 0f64 {
|
||||
return Some(format!("√{}", squared.to_string()));
|
||||
}
|
||||
}
|
||||
|
||||
// If nothing above was relevant, simply round it off a bit, eg. from 0.99999 to 1
|
||||
let rounded = match complex_number_type {
|
||||
ComplexNumberType::Real => round(input, complex_number_type)?.values().0,
|
||||
ComplexNumberType::Imaginary => round(input, complex_number_type)?.values().1,
|
||||
};
|
||||
let rounded_str = rounded.to_string();
|
||||
Some(trim_zeroes(if rounded_str == "-0" {
|
||||
"0"
|
||||
} else {
|
||||
&rounded_str
|
||||
}))
|
||||
}
|
||||
|
||||
pub(super) fn round(
|
||||
input: &KalkValue,
|
||||
complex_number_type: ComplexNumberType,
|
||||
) -> Option<KalkValue> {
|
||||
let (real, imaginary, _) = if let KalkValue::Number(real, imaginary, unit) = input {
|
||||
(real, imaginary, unit)
|
||||
} else {
|
||||
return None;
|
||||
};
|
||||
|
||||
let value = match complex_number_type {
|
||||
ComplexNumberType::Real => real,
|
||||
ComplexNumberType::Imaginary => imaginary,
|
||||
};
|
||||
let sign = if *value < 0f64 { -1f64 } else { 1f64 };
|
||||
let fract = value.clone().abs().fract();
|
||||
let integer = value.clone().abs().trunc();
|
||||
|
||||
// If it's zero something, don't do the rounding as aggressively.
|
||||
let (limit_floor, limit_ceil) = if integer == 0f64 {
|
||||
(-8f64, -5f64)
|
||||
} else {
|
||||
(-4f64, -6f64)
|
||||
};
|
||||
|
||||
if fract.clone().log10() < limit_floor {
|
||||
// If eg. 0.00xxx
|
||||
let new_value = integer * sign;
|
||||
let new_num = match complex_number_type {
|
||||
ComplexNumberType::Real => {
|
||||
KalkValue::Number(new_value, imaginary.clone(), input.get_unit())
|
||||
}
|
||||
ComplexNumberType::Imaginary => {
|
||||
KalkValue::Number(real.clone(), new_value, input.get_unit())
|
||||
}
|
||||
};
|
||||
|
||||
Some(new_num)
|
||||
} else if (1f64 - fract.clone()).log10() < limit_ceil {
|
||||
// If eg. 0.999
|
||||
// .abs() this before ceiling to make sure it rounds correctly. The sign is re-added afterwards.
|
||||
let new_value = value.clone().abs().ceil() * sign;
|
||||
let new_num = match complex_number_type {
|
||||
ComplexNumberType::Real => {
|
||||
KalkValue::Number(new_value, imaginary.clone(), input.get_unit())
|
||||
}
|
||||
ComplexNumberType::Imaginary => {
|
||||
KalkValue::Number(real.clone(), new_value, input.get_unit())
|
||||
}
|
||||
};
|
||||
|
||||
Some(new_num)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
pub(super) fn trim_zeroes(input: &str) -> String {
|
||||
if input.contains(".") {
|
||||
input
|
||||
.trim_end_matches("0")
|
||||
.trim_end_matches(".")
|
||||
.to_string()
|
||||
} else {
|
||||
input.into()
|
||||
}
|
||||
}
|
27
kalk/src/kalk_value/with_rug.rs
Normal file
27
kalk/src/kalk_value/with_rug.rs
Normal file
@ -0,0 +1,27 @@
|
||||
use crate::kalk_value::*;
|
||||
|
||||
impl KalkValue {
|
||||
pub fn to_f64(&self) -> f64 {
|
||||
if let KalkValue::Number(real, _, _) = self {
|
||||
real.to_f64_round(rug::float::Round::Nearest)
|
||||
} else {
|
||||
f64::NAN
|
||||
}
|
||||
}
|
||||
|
||||
pub fn imaginary_to_f64(&self) -> f64 {
|
||||
if let KalkValue::Number(_, imaginary, _) = self {
|
||||
imaginary.to_f64_round(rug::float::Round::Nearest)
|
||||
} else {
|
||||
f64::NAN
|
||||
}
|
||||
}
|
||||
|
||||
pub fn values(self) -> (Float, Float) {
|
||||
if let KalkValue::Number(real, imaginary, _) = self {
|
||||
(real, imaginary)
|
||||
} else {
|
||||
(Float::with_val(63, 0), Float::with_val(63, 0))
|
||||
}
|
||||
}
|
||||
}
|
@ -1,9 +1,10 @@
|
||||
pub mod ast;
|
||||
pub mod calculation_result;
|
||||
mod calculus;
|
||||
mod integration_testing;
|
||||
mod interpreter;
|
||||
mod inverter;
|
||||
pub mod kalk_num;
|
||||
pub mod kalk_value;
|
||||
mod lexer;
|
||||
pub mod parser;
|
||||
mod prelude;
|
||||
|
@ -1,5 +1,5 @@
|
||||
use crate::ast::Identifier;
|
||||
use crate::kalk_num::KalkNum;
|
||||
use crate::calculation_result::CalculationResult;
|
||||
use crate::{
|
||||
ast::{Expr, Stmt},
|
||||
interpreter, inverter,
|
||||
@ -79,7 +79,7 @@ impl Context {
|
||||
|
||||
#[wasm_bindgen(js_name = evaluate)]
|
||||
#[cfg(not(feature = "rug"))]
|
||||
pub fn js_eval(&mut self, input: &str) -> Result<Option<KalkNum>, JsValue> {
|
||||
pub fn js_eval(&mut self, input: &str) -> Result<Option<CalculationResult>, JsValue> {
|
||||
let result = eval(self, input);
|
||||
|
||||
match result {
|
||||
@ -158,7 +158,7 @@ pub fn eval(
|
||||
context: &mut Context,
|
||||
input: &str,
|
||||
#[cfg(feature = "rug")] precision: u32,
|
||||
) -> Result<Option<KalkNum>, CalcError> {
|
||||
) -> Result<Option<CalculationResult>, CalcError> {
|
||||
// Variable and function declaration parsers will set this to false
|
||||
// if the equal sign is for one of those instead.
|
||||
// It also should not contain an iverson bracket, since equal signs in there
|
||||
@ -180,7 +180,7 @@ pub fn eval(
|
||||
);
|
||||
let result = interpreter.interpret(statements);
|
||||
if let Ok(Some(mut num)) = result {
|
||||
num.other_radix = context.other_radix;
|
||||
num.set_radix(context.other_radix.unwrap_or(10));
|
||||
Ok(Some(num))
|
||||
} else {
|
||||
result
|
||||
|
@ -1,4 +1,4 @@
|
||||
use crate::kalk_num::KalkNum;
|
||||
use crate::kalk_value::KalkValue;
|
||||
use lazy_static::lazy_static;
|
||||
use std::collections::HashMap;
|
||||
use FuncType::*;
|
||||
@ -77,7 +77,7 @@ lazy_static! {
|
||||
m.insert("abs", (UnaryFuncInfo(abs, Other), ""));
|
||||
m.insert("cbrt", (UnaryFuncInfo(cbrt, Other), ""));
|
||||
m.insert("ceil", (UnaryFuncInfo(ceil, Other), ""));
|
||||
m.insert("iverson", (UnaryFuncInfo(inverson, Other), ""));
|
||||
m.insert("iverson", (UnaryFuncInfo(iverson, Other), ""));
|
||||
m.insert("exp", (UnaryFuncInfo(exp, Other), ""));
|
||||
m.insert("floor", (UnaryFuncInfo(floor, Other), ""));
|
||||
m.insert("frac", (UnaryFuncInfo(frac, Other), ""));
|
||||
@ -121,13 +121,17 @@ enum FuncType {
|
||||
Other,
|
||||
}
|
||||
|
||||
// Unary functions
|
||||
pub struct UnaryFuncInfo(fn(KalkNum) -> KalkNum, FuncType);
|
||||
pub struct UnaryFuncInfo(fn(KalkValue) -> KalkValue, FuncType);
|
||||
|
||||
pub struct BinaryFuncInfo(fn(KalkNum, KalkNum) -> KalkNum, FuncType);
|
||||
pub struct BinaryFuncInfo(fn(KalkValue, KalkValue) -> KalkValue, FuncType);
|
||||
|
||||
impl UnaryFuncInfo {
|
||||
fn call(&self, context: &mut interpreter::Context, x: KalkNum, angle_unit: &str) -> KalkNum {
|
||||
fn call(
|
||||
&self,
|
||||
context: &mut interpreter::Context,
|
||||
x: KalkValue,
|
||||
angle_unit: &str,
|
||||
) -> KalkValue {
|
||||
let func = self.0;
|
||||
match self.1 {
|
||||
FuncType::Trig => func(from_angle_unit(context, x, angle_unit)),
|
||||
@ -141,10 +145,10 @@ impl BinaryFuncInfo {
|
||||
fn call(
|
||||
&self,
|
||||
context: &mut interpreter::Context,
|
||||
x: KalkNum,
|
||||
y: KalkNum,
|
||||
x: KalkValue,
|
||||
y: KalkValue,
|
||||
angle_unit: &str,
|
||||
) -> KalkNum {
|
||||
) -> KalkValue {
|
||||
let func = self.0;
|
||||
match self.1 {
|
||||
FuncType::Trig => func(
|
||||
@ -177,9 +181,9 @@ pub fn is_constant(identifier: &str) -> bool {
|
||||
pub fn call_unary_func(
|
||||
context: &mut interpreter::Context,
|
||||
name: &str,
|
||||
x: KalkNum,
|
||||
x: KalkValue,
|
||||
angle_unit: &str,
|
||||
) -> Option<(KalkNum, String)> {
|
||||
) -> Option<(KalkValue, String)> {
|
||||
if let Some((func_info, func_unit)) = UNARY_FUNCS.get(name) {
|
||||
Some((
|
||||
func_info.call(context, x, &angle_unit),
|
||||
@ -193,10 +197,10 @@ pub fn call_unary_func(
|
||||
pub fn call_binary_func(
|
||||
context: &mut interpreter::Context,
|
||||
name: &str,
|
||||
x: KalkNum,
|
||||
y: KalkNum,
|
||||
x: KalkValue,
|
||||
y: KalkValue,
|
||||
angle_unit: &str,
|
||||
) -> Option<(KalkNum, String)> {
|
||||
) -> Option<(KalkValue, String)> {
|
||||
if let Some((func_info, func_unit)) = BINARY_FUNCS.get(name) {
|
||||
Some((
|
||||
func_info.call(context, x, y, angle_unit),
|
||||
@ -207,7 +211,7 @@ pub fn call_binary_func(
|
||||
}
|
||||
}
|
||||
|
||||
fn to_angle_unit(context: &mut interpreter::Context, x: KalkNum, angle_unit: &str) -> KalkNum {
|
||||
fn to_angle_unit(context: &mut interpreter::Context, x: KalkValue, angle_unit: &str) -> KalkValue {
|
||||
match angle_unit {
|
||||
"rad" => x,
|
||||
_ => interpreter::convert_unit(context, &Expr::Literal(x.to_f64()), "rad", angle_unit)
|
||||
@ -215,7 +219,11 @@ fn to_angle_unit(context: &mut interpreter::Context, x: KalkNum, angle_unit: &st
|
||||
}
|
||||
}
|
||||
|
||||
fn from_angle_unit(context: &mut interpreter::Context, x: KalkNum, angle_unit: &str) -> KalkNum {
|
||||
fn from_angle_unit(
|
||||
context: &mut interpreter::Context,
|
||||
x: KalkValue,
|
||||
angle_unit: &str,
|
||||
) -> KalkValue {
|
||||
match angle_unit {
|
||||
"rad" => x,
|
||||
_ => interpreter::convert_unit(context, &Expr::Literal(x.to_f64()), angle_unit, "rad")
|
||||
@ -229,305 +237,333 @@ pub mod funcs {
|
||||
use super::special_funcs::factorial;
|
||||
#[cfg(feature = "rug")]
|
||||
pub use super::with_rug::funcs::*;
|
||||
use crate::kalk_num::KalkNum;
|
||||
use crate::{as_number_or_return, float, kalk_value::KalkValue};
|
||||
|
||||
pub fn abs(x: KalkNum) -> KalkNum {
|
||||
if x.has_imaginary() {
|
||||
pub fn abs(x: KalkValue) -> KalkValue {
|
||||
let (real, imaginary, unit) = as_number_or_return!(x);
|
||||
if imaginary != 0f64 {
|
||||
// |z| = sqrt(a² + b²)
|
||||
let a = x.value.clone() * x.value;
|
||||
let b = x.imaginary_value.clone() * x.imaginary_value;
|
||||
let a = real.clone() * real;
|
||||
let b = imaginary.clone() * imaginary;
|
||||
|
||||
sqrt(KalkNum::new(a + b, &x.unit))
|
||||
sqrt(KalkValue::Number(a + b, float!(0), unit))
|
||||
} else {
|
||||
KalkNum::new(x.value.abs(), &x.unit)
|
||||
KalkValue::Number(real.abs(), float!(0), unit)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn acos(x: KalkNum) -> KalkNum {
|
||||
if x.has_imaginary() || x.value > 1f64 || x.value < -1f64 {
|
||||
pub fn acos(x: KalkValue) -> KalkValue {
|
||||
let (real, imaginary, unit) = as_number_or_return!(x.clone());
|
||||
if imaginary != 0f64 || real > 1f64 || real < -1f64 {
|
||||
// -i * ln(i * sqrt(1 - z²) + z)
|
||||
let root =
|
||||
sqrt(KalkNum::from(1f64).sub_without_unit(x.clone().mul_without_unit(x.clone())));
|
||||
sqrt(KalkValue::from(1f64).sub_without_unit(x.clone().mul_without_unit(x.clone())));
|
||||
let iroot = multiply_with_i(root.clone());
|
||||
let ln = ln(iroot.add_without_unit(x));
|
||||
let (ln_real, ln_imaginary, ln_unit) =
|
||||
as_number_or_return!(ln(iroot.add_without_unit(x)));
|
||||
|
||||
// -iz = -i(a + bi) = b - ai
|
||||
KalkNum::new_with_imaginary(ln.imaginary_value, &ln.unit, -ln.value)
|
||||
KalkValue::Number(ln_imaginary, -ln_real, ln_unit)
|
||||
} else {
|
||||
KalkNum::new(x.value.acos(), &x.unit)
|
||||
KalkValue::Number(real.acos(), float!(0), unit)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn acosh(x: KalkNum) -> KalkNum {
|
||||
if x.has_imaginary() || x.value < 1f64 {
|
||||
let sqrt1 = sqrt(KalkNum::new_with_imaginary(
|
||||
x.value.clone() + 1f64,
|
||||
&x.unit,
|
||||
x.imaginary_value.clone(),
|
||||
pub fn acosh(x: KalkValue) -> KalkValue {
|
||||
let (real, imaginary, unit) = as_number_or_return!(x.clone());
|
||||
if imaginary != 0f64 || real < 1f64 {
|
||||
let sqrt1 = sqrt(KalkValue::Number(
|
||||
real.clone() + 1f64,
|
||||
imaginary.clone(),
|
||||
unit.clone(),
|
||||
));
|
||||
let sqrt2 = sqrt(KalkNum::new_with_imaginary(
|
||||
x.value.clone() - 1f64,
|
||||
&x.unit,
|
||||
x.imaginary_value.clone(),
|
||||
let sqrt2 = sqrt(KalkValue::Number(
|
||||
real.clone() - 1f64,
|
||||
imaginary.clone(),
|
||||
unit,
|
||||
));
|
||||
|
||||
ln(x.add_without_unit(sqrt1.mul_without_unit(sqrt2)))
|
||||
} else {
|
||||
KalkNum::new(x.value.acosh(), &x.unit)
|
||||
KalkValue::Number(real.acosh(), float!(0), unit)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn acot(x: KalkNum) -> KalkNum {
|
||||
if x.has_imaginary() {
|
||||
pub fn acot(x: KalkValue) -> KalkValue {
|
||||
let (real, imaginary, unit) = as_number_or_return!(x.clone());
|
||||
if imaginary != 0f64 {
|
||||
// atan(1/z)
|
||||
atan(KalkNum::from(1f64).div_without_unit(x))
|
||||
atan(KalkValue::from(1f64).div_without_unit(x))
|
||||
} else {
|
||||
KalkNum::new((1f64 / x.value).atan(), &x.unit)
|
||||
KalkValue::Number((1f64 / real).atan(), float!(0), unit)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn acoth(x: KalkNum) -> KalkNum {
|
||||
if x.has_imaginary() || x.value <= 1f64 || x.value >= -1f64 {
|
||||
pub fn acoth(x: KalkValue) -> KalkValue {
|
||||
let (real, imaginary, unit) = as_number_or_return!(x.clone());
|
||||
if imaginary != 0f64 || real <= 1f64 || real >= -1f64 {
|
||||
// 1 / z
|
||||
let inv_x = KalkNum::from(1f64).div_without_unit(x);
|
||||
let ln1 = ln(KalkNum::new_with_imaginary(
|
||||
1f64 + inv_x.value.clone(),
|
||||
&inv_x.unit,
|
||||
inv_x.imaginary_value.clone(),
|
||||
));
|
||||
let ln2 = ln(KalkNum::new_with_imaginary(
|
||||
1f64 - inv_x.value,
|
||||
&inv_x.unit,
|
||||
-inv_x.imaginary_value,
|
||||
let (inv_real, inv_imaginary, inv_unit) =
|
||||
as_number_or_return!(KalkValue::from(1f64).div_without_unit(x));
|
||||
let ln1 = ln(KalkValue::Number(
|
||||
1f64 + inv_real.clone(),
|
||||
inv_imaginary.clone(),
|
||||
inv_unit.clone(),
|
||||
));
|
||||
let ln2 = ln(KalkValue::Number(1f64 - inv_real, -inv_imaginary, inv_unit));
|
||||
|
||||
ln1.sub_without_unit(ln2)
|
||||
.div_without_unit(KalkNum::from(2f64))
|
||||
.div_without_unit(KalkValue::from(2f64))
|
||||
} else {
|
||||
KalkNum::new((1f64 / x.value).atanh(), &x.unit)
|
||||
KalkValue::Number((1f64 / real).atanh(), float!(0), unit)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn acsc(x: KalkNum) -> KalkNum {
|
||||
if x.has_imaginary() || x.value < 1f64 || x.value > -1f64 {
|
||||
pub fn acsc(x: KalkValue) -> KalkValue {
|
||||
let (real, imaginary, unit) = as_number_or_return!(x.clone());
|
||||
if imaginary != 0f64 || real < 1f64 || real > -1f64 {
|
||||
// asin(1/z)
|
||||
asin(KalkNum::from(1f64).div_without_unit(x))
|
||||
asin(KalkValue::from(1f64).div_without_unit(x))
|
||||
} else {
|
||||
KalkNum::new((1f64 / x.value).asin(), &x.unit)
|
||||
KalkValue::Number((1f64 / real).asin(), float!(0), unit)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn acsch(x: KalkNum) -> KalkNum {
|
||||
if x.has_imaginary() || x.value == 0f64 {
|
||||
let inv_x2 =
|
||||
KalkNum::from(1f64).div_without_unit(x.clone().mul_without_unit(x.clone()));
|
||||
let sqrt = sqrt(KalkNum::new_with_imaginary(
|
||||
1f64 + inv_x2.value,
|
||||
&inv_x2.unit,
|
||||
inv_x2.imaginary_value,
|
||||
pub fn acsch(x: KalkValue) -> KalkValue {
|
||||
let (real, imaginary, unit) = as_number_or_return!(x.clone());
|
||||
if imaginary != 0f64 || real == 0f64 {
|
||||
let (inv_x2_real, inv_x2_imaginary, inv_x2_unit) = as_number_or_return!(
|
||||
KalkValue::from(1f64).div_without_unit(x.clone().mul_without_unit(x.clone()))
|
||||
);
|
||||
let sqrt = sqrt(KalkValue::Number(
|
||||
1f64 + inv_x2_real,
|
||||
inv_x2_imaginary,
|
||||
inv_x2_unit,
|
||||
));
|
||||
let inv_x = KalkNum::from(1f64).div_without_unit(x.clone());
|
||||
let inv_x = KalkValue::from(1f64).div_without_unit(x.clone());
|
||||
|
||||
ln(sqrt.add_without_unit(inv_x))
|
||||
} else {
|
||||
KalkNum::new((1f64 / x.value).asinh(), &x.unit)
|
||||
KalkValue::Number((1f64 / real).asinh(), float!(0), unit)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn asec(x: KalkNum) -> KalkNum {
|
||||
if x.has_imaginary() || x.value < 1f64 || x.value > -1f64 {
|
||||
pub fn asec(x: KalkValue) -> KalkValue {
|
||||
let (real, imaginary, unit) = as_number_or_return!(x.clone());
|
||||
if imaginary != 0f64 || real < 1f64 || real > -1f64 {
|
||||
// acos(1/z)
|
||||
acos(KalkNum::from(1f64).div_without_unit(x))
|
||||
acos(KalkValue::from(1f64).div_without_unit(x))
|
||||
} else {
|
||||
KalkNum::new((1f64 / x.value).acos(), &x.unit)
|
||||
KalkValue::Number((1f64 / real).acos(), float!(0), unit)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn asech(x: KalkNum) -> KalkNum {
|
||||
if x.has_imaginary() || x.value <= 0f64 || x.value > 1f64 {
|
||||
pub fn asech(x: KalkValue) -> KalkValue {
|
||||
let (real, imaginary, unit) = as_number_or_return!(x.clone());
|
||||
if imaginary != 0f64 || real <= 0f64 || real > 1f64 {
|
||||
// 1/z
|
||||
let inv_x = KalkNum::from(1f64).div_without_unit(x.clone());
|
||||
let inv = KalkValue::from(1f64).div_without_unit(x.clone());
|
||||
let (inv_real, inv_imaginary, inv_unit) = as_number_or_return!(inv.clone());
|
||||
// sqrt(1/z - 1)
|
||||
let sqrt1 = sqrt(KalkNum::new_with_imaginary(
|
||||
inv_x.value.clone() - 1f64,
|
||||
&inv_x.unit,
|
||||
inv_x.imaginary_value.clone(),
|
||||
let sqrt1 = sqrt(KalkValue::Number(
|
||||
inv_real.clone() - 1f64,
|
||||
inv_imaginary.clone(),
|
||||
inv_unit.clone(),
|
||||
));
|
||||
// sqrt(1/z + 1)
|
||||
let sqrt2 = sqrt(KalkNum::new_with_imaginary(
|
||||
inv_x.value.clone() + 1f64,
|
||||
&inv_x.unit,
|
||||
inv_x.imaginary_value.clone(),
|
||||
let sqrt2 = sqrt(KalkValue::Number(
|
||||
inv_real.clone() + 1f64,
|
||||
inv_imaginary.clone(),
|
||||
inv_unit,
|
||||
));
|
||||
|
||||
// ln(1/z + sqrt(1/z - 1) * sqrt(1/z + 1))
|
||||
ln(sqrt1.mul_without_unit(sqrt2).add_without_unit(inv_x))
|
||||
ln(sqrt1.mul_without_unit(sqrt2).add_without_unit(inv))
|
||||
} else {
|
||||
KalkNum::new((1f64 / x.value).acosh(), &x.unit)
|
||||
KalkValue::Number((1f64 / real).acosh(), float!(0), unit)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn asin(x: KalkNum) -> KalkNum {
|
||||
if x.has_imaginary() || x.value > 1f64 || x.value < -1f64 {
|
||||
pub fn asin(x: KalkValue) -> KalkValue {
|
||||
let (real, imaginary, unit) = as_number_or_return!(x.clone());
|
||||
if imaginary != 0f64 || real > 1f64 || real < -1f64 {
|
||||
// i * ln(sqrt(1 - z²) - iz)
|
||||
let root =
|
||||
sqrt(KalkNum::from(1f64).sub_without_unit(x.clone().mul_without_unit(x.clone())));
|
||||
sqrt(KalkValue::from(1f64).sub_without_unit(x.clone().mul_without_unit(x.clone())));
|
||||
let iz = multiply_with_i(x.clone());
|
||||
let ln = ln(root.sub_without_unit(iz));
|
||||
multiply_with_i(ln)
|
||||
} else {
|
||||
KalkNum::new(x.value.asin(), &x.unit)
|
||||
KalkValue::Number(real.asin(), float!(0), unit)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn asinh(x: KalkNum) -> KalkNum {
|
||||
if x.has_imaginary() {
|
||||
let x2 = x.clone().mul_without_unit(x.clone());
|
||||
let sqrt = sqrt(KalkNum::new_with_imaginary(
|
||||
x2.value + 1f64,
|
||||
&x2.unit,
|
||||
x2.imaginary_value,
|
||||
));
|
||||
pub fn asinh(x: KalkValue) -> KalkValue {
|
||||
let (real, imaginary, unit) = as_number_or_return!(x.clone());
|
||||
if imaginary != 0f64 {
|
||||
let (x2_real, x2_imaginary, x2_unit) =
|
||||
as_number_or_return!(x.clone().mul_without_unit(x.clone()));
|
||||
let sqrt = sqrt(KalkValue::Number(x2_real + 1f64, x2_imaginary, x2_unit));
|
||||
|
||||
ln(x.add_without_unit(sqrt))
|
||||
} else {
|
||||
KalkNum::new(x.value.asinh(), &x.unit)
|
||||
KalkValue::Number(real.asinh(), float!(0), unit)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn atan(x: KalkNum) -> KalkNum {
|
||||
if x.has_imaginary() {
|
||||
let iz = multiply_with_i(x);
|
||||
pub fn atan(x: KalkValue) -> KalkValue {
|
||||
let (real, imaginary, unit) = as_number_or_return!(x.clone());
|
||||
if imaginary != 0f64 {
|
||||
let (iz_real, iz_imaginary, iz_unit) = as_number_or_return!(multiply_with_i(x));
|
||||
|
||||
// 1 - iz
|
||||
let neg = KalkNum::new_with_imaginary(
|
||||
1f64 - iz.value.clone(),
|
||||
&iz.unit,
|
||||
-iz.imaginary_value.clone(),
|
||||
let neg = KalkValue::Number(
|
||||
1f64 - iz_real.clone(),
|
||||
-iz_imaginary.clone(),
|
||||
iz_unit.clone(),
|
||||
);
|
||||
|
||||
// 1 + iz
|
||||
let pos = KalkNum::new_with_imaginary(1f64 + iz.value, &iz.unit, iz.imaginary_value);
|
||||
let pos = KalkValue::Number(1f64 + iz_real, iz_imaginary, iz_unit);
|
||||
|
||||
// ln(1 - iz) - ln(1 + iz)
|
||||
let ln = ln(neg).sub_without_unit(ln(pos));
|
||||
|
||||
multiply_with_i(ln).div_without_unit(KalkNum::from(2f64))
|
||||
multiply_with_i(ln).div_without_unit(KalkValue::from(2f64))
|
||||
} else {
|
||||
KalkNum::new(x.value.atan(), &x.unit)
|
||||
KalkValue::Number(real.atan(), float!(0), unit)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn atanh(x: KalkNum) -> KalkNum {
|
||||
if x.has_imaginary() || x.value >= 1f64 || x.value <= -1f64 {
|
||||
pub fn atanh(x: KalkValue) -> KalkValue {
|
||||
let (real, imaginary, unit) = as_number_or_return!(x);
|
||||
if imaginary != 0f64 || real >= 1f64 || real <= -1f64 {
|
||||
// 1/2 * log(z + 1) - 1/2 * log(-z + 1)
|
||||
let log1 = ln(KalkNum::new_with_imaginary(
|
||||
1f64 + x.value.clone(),
|
||||
&x.unit,
|
||||
x.imaginary_value.clone(),
|
||||
));
|
||||
let log2 = ln(KalkNum::new_with_imaginary(
|
||||
1f64 - x.value,
|
||||
&x.unit,
|
||||
-x.imaginary_value,
|
||||
let log1 = ln(KalkValue::Number(
|
||||
1f64 + real.clone(),
|
||||
imaginary.clone(),
|
||||
unit.clone(),
|
||||
));
|
||||
let log2 = ln(KalkValue::Number(1f64 - real, -imaginary, unit));
|
||||
|
||||
log1.sub_without_unit(log2)
|
||||
.div_without_unit(KalkNum::from(2f64))
|
||||
.div_without_unit(KalkValue::from(2f64))
|
||||
} else {
|
||||
KalkNum::new(x.value.atanh(), &x.unit)
|
||||
KalkValue::Number(real.atanh(), float!(0), unit)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn cbrt(x: KalkNum) -> KalkNum {
|
||||
KalkNum::new(x.value.cbrt(), &x.unit)
|
||||
pub fn cbrt(x: KalkValue) -> KalkValue {
|
||||
let (real, _, unit) = as_number_or_return!(x);
|
||||
|
||||
KalkValue::Number(real.cbrt(), float!(0), unit)
|
||||
}
|
||||
|
||||
pub fn ceil(x: KalkNum) -> KalkNum {
|
||||
KalkNum::new_with_imaginary(x.value.ceil(), &x.unit, x.imaginary_value.ceil())
|
||||
pub fn ceil(x: KalkValue) -> KalkValue {
|
||||
let (real, imaginary, unit) = as_number_or_return!(x);
|
||||
|
||||
KalkValue::Number(real.ceil(), imaginary.ceil(), unit)
|
||||
}
|
||||
|
||||
pub fn cos(x: KalkNum) -> KalkNum {
|
||||
KalkNum::new_with_imaginary(
|
||||
x.value.clone().cos() * x.imaginary_value.clone().cosh(),
|
||||
&x.unit,
|
||||
-x.value.sin() * x.imaginary_value.sinh(),
|
||||
pub fn cos(x: KalkValue) -> KalkValue {
|
||||
let (real, imaginary, unit) = as_number_or_return!(x);
|
||||
|
||||
KalkValue::Number(
|
||||
real.clone().cos() * imaginary.clone().cosh(),
|
||||
-real.sin() * imaginary.sinh(),
|
||||
unit,
|
||||
)
|
||||
}
|
||||
|
||||
pub fn cosh(x: KalkNum) -> KalkNum {
|
||||
KalkNum::new_with_imaginary(
|
||||
x.value.clone().cosh() * x.imaginary_value.clone().cos(),
|
||||
&x.unit,
|
||||
x.value.sinh() * x.imaginary_value.sin(),
|
||||
pub fn cosh(x: KalkValue) -> KalkValue {
|
||||
let (real, imaginary, unit) = as_number_or_return!(x);
|
||||
|
||||
KalkValue::Number(
|
||||
real.clone().cosh() * imaginary.clone().cos(),
|
||||
real.sinh() * imaginary.sin(),
|
||||
unit,
|
||||
)
|
||||
}
|
||||
|
||||
pub fn csc(x: KalkNum) -> KalkNum {
|
||||
KalkNum::from(1f64).div_without_unit(sin(x))
|
||||
pub fn csc(x: KalkValue) -> KalkValue {
|
||||
KalkValue::from(1f64).div_without_unit(sin(x))
|
||||
}
|
||||
|
||||
pub fn csch(x: KalkNum) -> KalkNum {
|
||||
KalkNum::from(1f64).div_without_unit(sinh(x))
|
||||
pub fn csch(x: KalkValue) -> KalkValue {
|
||||
KalkValue::from(1f64).div_without_unit(sinh(x))
|
||||
}
|
||||
|
||||
pub fn cot(x: KalkNum) -> KalkNum {
|
||||
let a = x.value * 2f64;
|
||||
let b = x.imaginary_value * 2f64;
|
||||
KalkNum::new_with_imaginary(
|
||||
pub fn cot(x: KalkValue) -> KalkValue {
|
||||
let (real, imaginary, unit) = as_number_or_return!(x);
|
||||
|
||||
let a = real * 2f64;
|
||||
let b = imaginary * 2f64;
|
||||
KalkValue::Number(
|
||||
-a.clone().sin() / (a.clone().cos() - b.clone().cosh()),
|
||||
&x.unit,
|
||||
b.clone().sinh() / (a.cos() - b.cosh()),
|
||||
unit,
|
||||
)
|
||||
}
|
||||
|
||||
pub fn coth(x: KalkNum) -> KalkNum {
|
||||
let a = x.value * 2f64;
|
||||
let b = x.imaginary_value * 2f64;
|
||||
KalkNum::new_with_imaginary(
|
||||
pub fn coth(x: KalkValue) -> KalkValue {
|
||||
let (real, imaginary, unit) = as_number_or_return!(x);
|
||||
let a = real * 2f64;
|
||||
let b = imaginary * 2f64;
|
||||
KalkValue::Number(
|
||||
-a.clone().sinh() / (b.clone().cos() - a.clone().cosh()),
|
||||
&x.unit,
|
||||
b.clone().sin() / (b.cos() - a.cosh()),
|
||||
unit,
|
||||
)
|
||||
}
|
||||
|
||||
pub fn exp(x: KalkNum) -> KalkNum {
|
||||
if x.has_imaginary() {
|
||||
pub fn exp(x: KalkValue) -> KalkValue {
|
||||
let (real, imaginary, unit) = as_number_or_return!(x);
|
||||
|
||||
if imaginary != 0f64 {
|
||||
// e^a*cos(b) + ie^a*sin(b)
|
||||
let exp_a = x.value.exp();
|
||||
let b = x.imaginary_value;
|
||||
KalkNum::new_with_imaginary(exp_a.clone() * b.clone().cos(), &x.unit, exp_a * b.sin())
|
||||
let exp_a = real.exp();
|
||||
let b = imaginary;
|
||||
KalkValue::Number(exp_a.clone() * b.clone().cos(), exp_a * b.sin(), unit)
|
||||
} else {
|
||||
KalkNum::new(x.value.exp(), &x.unit)
|
||||
KalkValue::Number(real.exp(), float!(0), unit)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn floor(x: KalkNum) -> KalkNum {
|
||||
KalkNum::new_with_imaginary(x.value.floor(), &x.unit, x.imaginary_value.floor())
|
||||
pub fn floor(x: KalkValue) -> KalkValue {
|
||||
let (real, imaginary, unit) = as_number_or_return!(x);
|
||||
|
||||
KalkValue::Number(real.floor(), imaginary.floor(), unit)
|
||||
}
|
||||
|
||||
pub fn frac(x: KalkNum) -> KalkNum {
|
||||
KalkNum::new_with_imaginary(x.value.fract(), &x.unit, x.imaginary_value.fract())
|
||||
pub fn frac(x: KalkValue) -> KalkValue {
|
||||
let (real, imaginary, unit) = as_number_or_return!(x);
|
||||
|
||||
KalkValue::Number(real.fract(), imaginary.fract(), unit)
|
||||
}
|
||||
|
||||
pub fn gcd(x: KalkNum, y: KalkNum) -> KalkNum {
|
||||
pub fn gcd(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());
|
||||
|
||||
// Find the norm of a Gaussian integer
|
||||
fn norm(x: KalkNum) -> KalkNum {
|
||||
KalkNum::new(
|
||||
(x.value.clone() * x.value) + (x.imaginary_value.clone() * x.imaginary_value),
|
||||
&x.unit,
|
||||
fn norm(x: KalkValue) -> KalkValue {
|
||||
let (real, imaginary, unit) = as_number_or_return!(x);
|
||||
KalkValue::Number(
|
||||
(real.clone() * real) + (imaginary.clone() * imaginary),
|
||||
float!(0),
|
||||
unit,
|
||||
)
|
||||
}
|
||||
|
||||
if x.has_imaginary() || y.has_imaginary() {
|
||||
if x.value.clone().fract() != 0f64
|
||||
|| y.value.clone().fract() != 0f64
|
||||
|| x.imaginary_value.clone().fract() != 0f64
|
||||
|| y.imaginary_value.clone().fract() != 0f64
|
||||
if imaginary != 0f64 || y.has_imaginary() {
|
||||
if real.clone().fract() != 0f64
|
||||
|| real_rhs.clone().fract() != 0f64
|
||||
|| imaginary.clone().fract() != 0f64
|
||||
|| imaginary_rhs.clone().fract() != 0f64
|
||||
{
|
||||
// Not a Gaussian integer!
|
||||
// TODO: throw an actual error instead of returning NaN
|
||||
return KalkNum::from(f64::NAN);
|
||||
return KalkValue::from(f64::NAN);
|
||||
}
|
||||
|
||||
// Partially derived from:
|
||||
@ -537,7 +573,7 @@ pub mod funcs {
|
||||
let b;
|
||||
|
||||
// Ensure a > b
|
||||
if norm(x.clone()).value < norm(y.clone()).value {
|
||||
if norm(x.clone()).values().0 < norm(y.clone()).values().0 {
|
||||
a = y;
|
||||
b = x;
|
||||
} else {
|
||||
@ -545,43 +581,46 @@ pub mod funcs {
|
||||
b = y;
|
||||
}
|
||||
|
||||
let mut c = a.clone().div_without_unit(b.clone());
|
||||
if c.imaginary_value.clone().fract() == 0f64 {
|
||||
KalkNum::new_with_imaginary(b.value.abs(), &b.unit, b.imaginary_value)
|
||||
let (b_real, b_imaginary, b_unit) = as_number_or_return!(b.clone());
|
||||
let (c_real, c_imaginary, c_unit) =
|
||||
as_number_or_return!(a.clone().div_without_unit(b.clone()));
|
||||
if c_imaginary.clone().fract() == 0f64 {
|
||||
KalkValue::Number(b_real.abs(), b_imaginary, b_unit)
|
||||
} else {
|
||||
c.value = c.value.round();
|
||||
c.imaginary_value = c.imaginary_value.round();
|
||||
gcd(a.sub_without_unit(b.clone().mul_without_unit(c)), b)
|
||||
let rounded_c = KalkValue::Number(c_real.round(), c_imaginary.round(), c_unit);
|
||||
gcd(a.sub_without_unit(b.clone().mul_without_unit(rounded_c)), b)
|
||||
}
|
||||
} else {
|
||||
if x.value < 0f64 || y.value < 0f64 {
|
||||
if real < 0f64 || real_rhs < 0f64 {
|
||||
return gcd(
|
||||
KalkNum::new(x.value.abs(), &x.unit),
|
||||
KalkNum::new(y.value.abs(), &y.unit),
|
||||
KalkValue::Number(real.abs(), float!(0), unit.clone()),
|
||||
KalkValue::Number(real_rhs.abs(), float!(0), unit),
|
||||
);
|
||||
}
|
||||
|
||||
// Euclidean GCD algorithm, but with modulus
|
||||
let mut x_a = x.clone();
|
||||
let mut y_a = y.clone();
|
||||
while !y_a.value.eq(&0f64) {
|
||||
let t = y_a.value.clone();
|
||||
y_a.value = x_a.value % y_a.value;
|
||||
x_a.value = t;
|
||||
let mut x_a = real.clone();
|
||||
let mut y_a = real_rhs.clone();
|
||||
while !y_a.eq(&0f64) {
|
||||
let t = y_a.clone();
|
||||
y_a = x_a % y_a;
|
||||
x_a = t;
|
||||
}
|
||||
|
||||
// Usually we'd need to return max(x, -x), but since we've handled negative
|
||||
// values above, that is unnecessary.
|
||||
x_a
|
||||
KalkValue::Number(x_a, float!(0), unit)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn im(x: KalkNum) -> KalkNum {
|
||||
KalkNum::new_with_imaginary(x.value, "", KalkNum::default().value)
|
||||
pub fn im(x: KalkValue) -> KalkValue {
|
||||
let (_, imaginary, unit) = as_number_or_return!(x);
|
||||
|
||||
KalkValue::Number(imaginary, float!(0), unit)
|
||||
}
|
||||
|
||||
pub fn inverson(x: KalkNum) -> KalkNum {
|
||||
KalkNum::from(if let Some(boolean_value) = x.boolean_value {
|
||||
pub fn iverson(x: KalkValue) -> KalkValue {
|
||||
KalkValue::from(if let KalkValue::Boolean(boolean_value) = x {
|
||||
if boolean_value {
|
||||
1
|
||||
} else {
|
||||
@ -597,165 +636,186 @@ pub mod funcs {
|
||||
// lcm(a, b) = ⎜ ───────── ⎟ × ⎜b⎜
|
||||
// ⎜ gcd(a, b) ⎟
|
||||
// ⎝ ⎠
|
||||
pub fn lcm(x: KalkNum, y: KalkNum) -> KalkNum {
|
||||
pub fn lcm(x: KalkValue, y: KalkValue) -> KalkValue {
|
||||
let (real, imaginary, unit) = as_number_or_return!(x.clone());
|
||||
let (real_rhs, imaginary_rhs, unit_rhs) = as_number_or_return!(y.clone());
|
||||
let gcd = gcd(x.clone(), y.clone());
|
||||
let absx = KalkNum::new_with_imaginary(x.value.abs(), &x.unit, x.imaginary_value);
|
||||
let absy = KalkNum::new_with_imaginary(y.value.abs(), &y.unit, y.imaginary_value);
|
||||
let absx = KalkValue::Number(real.abs(), imaginary, unit);
|
||||
let absy = KalkValue::Number(real_rhs.abs(), imaginary_rhs, unit_rhs);
|
||||
return absx.div_without_unit(gcd).mul_without_unit(absy);
|
||||
}
|
||||
|
||||
pub fn log(x: KalkNum) -> KalkNum {
|
||||
if x.has_imaginary() || x.value < 0f64 {
|
||||
pub fn log(x: KalkValue) -> KalkValue {
|
||||
let (real, imaginary, unit) = as_number_or_return!(x.clone());
|
||||
if imaginary != 0f64 || real < 0f64 {
|
||||
// ln(z) / ln(10)
|
||||
ln(x).div_without_unit(KalkNum::from(10f64.ln()))
|
||||
ln(x).div_without_unit(KalkValue::from(10f64.ln()))
|
||||
} else {
|
||||
KalkNum::new(x.value.log10(), &x.unit)
|
||||
KalkValue::Number(real.log10(), float!(0), unit)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn logx(x: KalkNum, y: KalkNum) -> KalkNum {
|
||||
if x.has_imaginary() || y.has_imaginary() || x.value < 0f64 || y.value < 0f64 {
|
||||
pub fn logx(x: KalkValue, y: KalkValue) -> KalkValue {
|
||||
let (real, imaginary, unit) = as_number_or_return!(x.clone());
|
||||
let (real_rhs, _, _) = as_number_or_return!(y.clone());
|
||||
if imaginary != 0f64 || y.has_imaginary() || real < 0f64 || real_rhs < 0f64 {
|
||||
// ln(z) / ln(n)
|
||||
ln(x).div_without_unit(ln(y))
|
||||
} else {
|
||||
KalkNum::new(x.value.log10() / y.value.log10(), &x.unit)
|
||||
KalkValue::Number(real.log10() / real_rhs.log10(), float!(0), unit)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn ln(x: KalkNum) -> KalkNum {
|
||||
if x.has_imaginary() || x.value < 0f64 {
|
||||
pub fn ln(x: KalkValue) -> KalkValue {
|
||||
let (real, imaginary, unit) = as_number_or_return!(x.clone());
|
||||
if imaginary != 0f64 || real < 0f64 {
|
||||
let r = abs(x.clone());
|
||||
// ln|z| + i * arg z
|
||||
ln(r).add_without_unit(multiply_with_i(arg(x)))
|
||||
} else {
|
||||
KalkNum::new(x.value.ln(), &x.unit)
|
||||
KalkValue::Number(real.ln(), float!(0), unit)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn nth_root(x: KalkNum, n: KalkNum) -> KalkNum {
|
||||
x.pow_without_unit(KalkNum::from(1f64).div_without_unit(n))
|
||||
pub fn nth_root(x: KalkValue, n: KalkValue) -> KalkValue {
|
||||
x.pow_without_unit(KalkValue::from(1f64).div_without_unit(n))
|
||||
}
|
||||
|
||||
pub fn re(x: KalkNum) -> KalkNum {
|
||||
KalkNum::new_with_imaginary(KalkNum::default().value, "", x.imaginary_value)
|
||||
pub fn re(x: KalkValue) -> KalkValue {
|
||||
let (real, _, unit) = as_number_or_return!(x);
|
||||
KalkValue::Number(real, float!(0), unit)
|
||||
}
|
||||
|
||||
pub fn round(x: KalkNum) -> KalkNum {
|
||||
KalkNum::new_with_imaginary(x.value.round(), &x.unit, x.imaginary_value.round())
|
||||
pub fn round(x: KalkValue) -> KalkValue {
|
||||
let (real, imaginary, unit) = as_number_or_return!(x);
|
||||
KalkValue::Number(real.round(), imaginary.round(), unit)
|
||||
}
|
||||
|
||||
pub fn sec(x: KalkNum) -> KalkNum {
|
||||
KalkNum::from(1f64).div_without_unit(cos(x))
|
||||
pub fn sec(x: KalkValue) -> KalkValue {
|
||||
KalkValue::from(1f64).div_without_unit(cos(x))
|
||||
}
|
||||
|
||||
pub fn sech(x: KalkNum) -> KalkNum {
|
||||
KalkNum::from(1f64).div_without_unit(cosh(x))
|
||||
pub fn sech(x: KalkValue) -> KalkValue {
|
||||
KalkValue::from(1f64).div_without_unit(cosh(x))
|
||||
}
|
||||
|
||||
pub fn sin(x: KalkNum) -> KalkNum {
|
||||
KalkNum::new_with_imaginary(
|
||||
x.value.clone().sin() * x.imaginary_value.clone().cosh(),
|
||||
&x.unit,
|
||||
x.value.cos() * x.imaginary_value.sinh(),
|
||||
pub fn sin(x: KalkValue) -> KalkValue {
|
||||
let (real, imaginary, unit) = as_number_or_return!(x);
|
||||
KalkValue::Number(
|
||||
real.clone().sin() * imaginary.clone().cosh(),
|
||||
real.cos() * imaginary.sinh(),
|
||||
unit,
|
||||
)
|
||||
}
|
||||
|
||||
pub fn sinh(x: KalkNum) -> KalkNum {
|
||||
KalkNum::new_with_imaginary(
|
||||
x.value.clone().sinh() * x.imaginary_value.clone().cos(),
|
||||
&x.unit,
|
||||
x.value.cosh() * x.imaginary_value.sin(),
|
||||
pub fn sinh(x: KalkValue) -> KalkValue {
|
||||
let (real, imaginary, unit) = as_number_or_return!(x);
|
||||
KalkValue::Number(
|
||||
real.clone().sinh() * imaginary.clone().cos(),
|
||||
real.cosh() * imaginary.sin(),
|
||||
unit,
|
||||
)
|
||||
}
|
||||
|
||||
pub fn sqrt(x: KalkNum) -> KalkNum {
|
||||
if x.has_imaginary() {
|
||||
let abs = abs(x.clone());
|
||||
let r = abs.value;
|
||||
let a = x.value;
|
||||
let b = x.imaginary_value;
|
||||
pub fn sqrt(x: KalkValue) -> KalkValue {
|
||||
let (real, imaginary, unit) = as_number_or_return!(x.clone());
|
||||
if imaginary != 0f64 {
|
||||
let (abs_real, _, abs_unit) = as_number_or_return!(abs(x.clone()));
|
||||
let r = abs_real;
|
||||
let a = real;
|
||||
let b = imaginary;
|
||||
|
||||
// sqrt((|z| + a) / 2) + i * (b / |b|) * sqrt((|z| - a) / 2)
|
||||
KalkNum::new_with_imaginary(
|
||||
KalkValue::Number(
|
||||
((r.clone() + a.clone()) / 2f64).sqrt(),
|
||||
&abs.unit,
|
||||
(b.clone() / b.abs()) * ((r - a) / 2f64).sqrt(),
|
||||
abs_unit,
|
||||
)
|
||||
} else if x.value < 0f64 {
|
||||
KalkNum::from_imaginary(x.value.abs().sqrt())
|
||||
} else if real < 0f64 {
|
||||
KalkValue::Number(float!(0), real.abs().sqrt(), unit)
|
||||
} else {
|
||||
KalkNum::new(x.value.sqrt(), &x.unit)
|
||||
KalkValue::Number(real.sqrt(), float!(0), unit)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn tan(x: KalkNum) -> KalkNum {
|
||||
if x.has_imaginary() {
|
||||
let a = x.value * 2f64;
|
||||
let b = x.imaginary_value * 2f64;
|
||||
KalkNum::new_with_imaginary(
|
||||
pub fn tan(x: KalkValue) -> KalkValue {
|
||||
let (real, imaginary, unit) = as_number_or_return!(x);
|
||||
if imaginary != 0f64 {
|
||||
let a = real * 2f64;
|
||||
let b = imaginary * 2f64;
|
||||
KalkValue::Number(
|
||||
a.clone().sin() / (a.clone().cos() + b.clone().cosh()),
|
||||
&x.unit,
|
||||
b.clone().sinh() / (a.cos() + b.cosh()),
|
||||
unit,
|
||||
)
|
||||
} else {
|
||||
KalkNum::new(x.value.tan(), &x.unit)
|
||||
KalkValue::Number(real.tan(), float!(0), unit)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn tanh(x: KalkNum) -> KalkNum {
|
||||
if x.has_imaginary() {
|
||||
let a = x.value * 2f64;
|
||||
let b = x.imaginary_value * 2f64;
|
||||
KalkNum::new_with_imaginary(
|
||||
pub fn tanh(x: KalkValue) -> KalkValue {
|
||||
let (real, imaginary, unit) = as_number_or_return!(x);
|
||||
if imaginary != 0f64 {
|
||||
let a = real * 2f64;
|
||||
let b = imaginary * 2f64;
|
||||
KalkValue::Number(
|
||||
a.clone().sinh() / (a.clone().cosh() + b.clone().cos()),
|
||||
&x.unit,
|
||||
b.clone().sin() / (a.cosh() + b.cos()),
|
||||
unit,
|
||||
)
|
||||
} else {
|
||||
KalkNum::new(x.value.tanh(), &x.unit)
|
||||
KalkValue::Number(real.tanh(), float!(0), unit)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn trunc(x: KalkNum) -> KalkNum {
|
||||
KalkNum::new_with_imaginary(x.value.trunc(), &x.unit, x.imaginary_value.trunc())
|
||||
pub fn trunc(x: KalkValue) -> KalkValue {
|
||||
let (real, imaginary, unit) = as_number_or_return!(x);
|
||||
KalkValue::Number(real.trunc(), imaginary.trunc(), unit)
|
||||
}
|
||||
|
||||
pub fn ncr(x: KalkNum, y: KalkNum) -> KalkNum {
|
||||
pub fn ncr(x: KalkValue, y: KalkValue) -> KalkValue {
|
||||
factorial(x.clone()).div_without_unit(
|
||||
factorial(y.clone()).mul_without_unit(factorial(x.sub_without_unit(y))),
|
||||
)
|
||||
}
|
||||
|
||||
pub fn npr(x: KalkNum, y: KalkNum) -> KalkNum {
|
||||
pub fn npr(x: KalkValue, y: KalkValue) -> KalkValue {
|
||||
factorial(x.clone()).div_without_unit(factorial(x.sub_without_unit(y)))
|
||||
}
|
||||
|
||||
fn multiply_with_i(z: KalkNum) -> KalkNum {
|
||||
fn multiply_with_i(z: KalkValue) -> KalkValue {
|
||||
let (real, imaginary, unit) = as_number_or_return!(z);
|
||||
|
||||
// iz = i(a + bi) = -b + ai
|
||||
KalkNum::new_with_imaginary(-z.imaginary_value, &z.unit, z.value)
|
||||
KalkValue::Number(-imaginary, real, unit)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::funcs::*;
|
||||
use crate::prelude::KalkNum;
|
||||
use crate::float;
|
||||
use crate::prelude::KalkValue;
|
||||
use crate::test_helpers::cmp;
|
||||
|
||||
#[test]
|
||||
fn test_unary_funcs() {
|
||||
let in_out = vec![
|
||||
(abs as fn(KalkNum) -> KalkNum, (3f64, 4f64), (5f64, 0f64)),
|
||||
(
|
||||
abs as fn(KalkValue) -> KalkValue,
|
||||
(3f64, 4f64),
|
||||
(5f64, 0f64),
|
||||
),
|
||||
(abs, (-3f64, 4f64), (5f64, 0f64)),
|
||||
(abs, (3f64, -4f64), (5f64, 0f64)),
|
||||
(abs, (-3f64, 0f64), (3f64, 0f64)),
|
||||
];
|
||||
|
||||
for (i, (func, input, expected_output)) in in_out.iter().enumerate() {
|
||||
let actual_output = func(KalkNum::new_with_imaginary(
|
||||
KalkNum::from(input.0).value,
|
||||
"",
|
||||
KalkNum::from(input.1).value,
|
||||
let actual_output = func(KalkValue::Number(
|
||||
float!(input.0),
|
||||
float!(input.1),
|
||||
String::new(),
|
||||
));
|
||||
|
||||
println!(
|
||||
@ -777,7 +837,7 @@ mod tests {
|
||||
fn test_binary_funcs() {
|
||||
let in_out = vec![
|
||||
(
|
||||
gcd as fn(KalkNum, KalkNum) -> KalkNum,
|
||||
gcd as fn(KalkValue, KalkValue) -> KalkValue,
|
||||
((12f64, 0f64), (18f64, 0f64)),
|
||||
(6f64, 0f64),
|
||||
),
|
||||
@ -792,16 +852,8 @@ mod tests {
|
||||
|
||||
for (i, (func, input, expected_output)) in in_out.iter().enumerate() {
|
||||
let actual_output = func(
|
||||
KalkNum::new_with_imaginary(
|
||||
KalkNum::from(input.0 .0).value,
|
||||
"",
|
||||
KalkNum::from(input.0 .1).value,
|
||||
),
|
||||
KalkNum::new_with_imaginary(
|
||||
KalkNum::from(input.1 .0).value,
|
||||
"",
|
||||
KalkNum::from(input.1 .1).value,
|
||||
),
|
||||
KalkValue::Number(float!(input.0 .0), float!(input.0 .1), String::new()),
|
||||
KalkValue::Number(float!(input.1 .0), float!(input.1 .1), String::new()),
|
||||
);
|
||||
|
||||
println!(
|
||||
@ -824,7 +876,7 @@ mod tests {
|
||||
// Auto-generated using kalk/scripts/generate_funcs_test_cases.py
|
||||
let in_out = vec![
|
||||
(
|
||||
arg as fn(KalkNum) -> KalkNum,
|
||||
arg as fn(KalkValue) -> KalkValue,
|
||||
(0.3f64, 0f64),
|
||||
(0.0f64, 0.0f64),
|
||||
),
|
||||
@ -1047,10 +1099,10 @@ mod tests {
|
||||
];
|
||||
|
||||
for (i, (func, input, expected_output)) in in_out.iter().enumerate() {
|
||||
let actual_output = func(KalkNum::new_with_imaginary(
|
||||
KalkNum::from(input.0).value,
|
||||
"",
|
||||
KalkNum::from(input.1).value,
|
||||
let actual_output = func(KalkValue::Number(
|
||||
float!(input.0),
|
||||
float!(input.1),
|
||||
String::new(),
|
||||
));
|
||||
|
||||
let expected_has_nan_or_inf = expected_output.0.is_nan()
|
||||
|
@ -1,29 +1,38 @@
|
||||
pub mod special_funcs {
|
||||
use crate::kalk_num::KalkNum;
|
||||
use crate::{as_number_or_return, float, kalk_value::KalkValue};
|
||||
|
||||
pub fn factorial(x: KalkValue) -> KalkValue {
|
||||
let (real, _, unit) = as_number_or_return!(x);
|
||||
|
||||
pub fn factorial(x: KalkNum) -> KalkNum {
|
||||
// Round it a bit, to prevent floating point errors.
|
||||
KalkNum::new(
|
||||
(super::funcs::precise_gamma(x.value + 1f64) * 10e6f64).round() / 10e6f64,
|
||||
&x.unit,
|
||||
KalkValue::Number(
|
||||
(super::funcs::precise_gamma(real + 1f64) * 10e6f64).round() / 10e6f64,
|
||||
float!(0),
|
||||
unit,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) mod funcs {
|
||||
use crate::kalk_num::KalkNum;
|
||||
use crate::kalk_value::KalkValue;
|
||||
use crate::prelude::funcs::abs;
|
||||
use crate::{as_number_or_return, float};
|
||||
|
||||
pub fn arg(x: KalkValue) -> KalkValue {
|
||||
let (real, imaginary, unit) = as_number_or_return!(x);
|
||||
|
||||
pub fn arg(x: KalkNum) -> KalkNum {
|
||||
// i(ln|x| - ln(x))
|
||||
KalkNum::new(x.imaginary_value.atan2(x.value), &x.unit)
|
||||
KalkValue::Number(imaginary.atan2(real), float!(0), unit)
|
||||
}
|
||||
|
||||
pub fn gamma(x: KalkNum) -> KalkNum {
|
||||
pub fn gamma(x: KalkValue) -> KalkValue {
|
||||
let (real, _, unit) = as_number_or_return!(x);
|
||||
|
||||
// Round it a bit, to prevent floating point errors.
|
||||
KalkNum::new(
|
||||
(precise_gamma(x.value) * 10e6f64).round() / 10e6f64,
|
||||
&x.unit,
|
||||
KalkValue::Number(
|
||||
(precise_gamma(real) * 10e6f64).round() / 10e6f64,
|
||||
float!(0),
|
||||
unit,
|
||||
)
|
||||
}
|
||||
|
||||
@ -50,34 +59,49 @@ pub(crate) mod funcs {
|
||||
2f64.sqrt() * pi.sqrt() * t.powf(x - 0.5f64) * (-t).exp() * a
|
||||
}
|
||||
|
||||
pub fn bitcmp(x: KalkNum) -> KalkNum {
|
||||
KalkNum::from(!(x.value.round() as i32))
|
||||
pub fn bitcmp(x: KalkValue) -> KalkValue {
|
||||
let (real, _, _) = as_number_or_return!(x);
|
||||
|
||||
KalkValue::from(!(real.round() as i32))
|
||||
}
|
||||
|
||||
pub fn bitand(x: KalkNum, y: KalkNum) -> KalkNum {
|
||||
KalkNum::from(x.value.round() as i32 & y.value.round() as i32)
|
||||
pub fn bitand(x: KalkValue, y: KalkValue) -> KalkValue {
|
||||
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)
|
||||
}
|
||||
|
||||
pub fn bitor(x: KalkNum, y: KalkNum) -> KalkNum {
|
||||
KalkNum::from(x.value.round() as i32 | y.value.round() as i32)
|
||||
pub fn bitor(x: KalkValue, y: KalkValue) -> KalkValue {
|
||||
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)
|
||||
}
|
||||
|
||||
pub fn bitxor(x: KalkNum, y: KalkNum) -> KalkNum {
|
||||
KalkNum::from(x.value.round() as i32 ^ y.value.round() as i32)
|
||||
pub fn bitxor(x: KalkValue, y: KalkValue) -> KalkValue {
|
||||
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)
|
||||
}
|
||||
|
||||
pub fn bitshift(x: KalkNum, y: KalkNum) -> KalkNum {
|
||||
let x = x.value.round() as i32;
|
||||
let y = y.value.round() as i32;
|
||||
pub fn bitshift(x: KalkValue, y: KalkValue) -> KalkValue {
|
||||
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 {
|
||||
KalkNum::from((x >> y.abs()))
|
||||
KalkValue::from(x >> y.abs())
|
||||
} else {
|
||||
KalkNum::from((x << y))
|
||||
KalkValue::from(x << y)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn hypot(x: KalkNum, y: KalkNum) -> KalkNum {
|
||||
if x.has_imaginary() || y.has_imaginary() {
|
||||
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());
|
||||
if imaginary.abs() != 0f64 || imaginary_rhs != 0f64 {
|
||||
let abs_x = abs(x);
|
||||
let abs_y = abs(y);
|
||||
crate::prelude::funcs::sqrt(
|
||||
@ -87,15 +111,21 @@ pub(crate) mod funcs {
|
||||
.add_without_unit(abs_y.clone().mul_without_unit(abs_y)),
|
||||
)
|
||||
} else {
|
||||
KalkNum::new(x.value.hypot(y.value), &x.unit)
|
||||
KalkValue::Number(real.hypot(real_rhs), float!(0), unit)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn max(x: KalkNum, y: KalkNum) -> KalkNum {
|
||||
KalkNum::new(x.value.max(y.value), &x.unit)
|
||||
pub fn max(x: KalkValue, y: KalkValue) -> KalkValue {
|
||||
let (real, _, unit) = as_number_or_return!(x);
|
||||
let (real_rhs, _, _) = as_number_or_return!(y);
|
||||
|
||||
KalkValue::Number(real.max(real_rhs), float!(0), unit)
|
||||
}
|
||||
|
||||
pub fn min(x: KalkNum, y: KalkNum) -> KalkNum {
|
||||
KalkNum::new(x.value.min(y.value), &x.unit)
|
||||
pub fn min(x: KalkValue, y: KalkValue) -> KalkValue {
|
||||
let (real, _, unit) = as_number_or_return!(x);
|
||||
let (real_rhs, _, _) = as_number_or_return!(y);
|
||||
|
||||
KalkValue::Number(real.min(real_rhs), float!(0), unit)
|
||||
}
|
||||
}
|
||||
|
@ -1,59 +1,82 @@
|
||||
pub mod special_funcs {
|
||||
use crate::prelude::KalkNum;
|
||||
use crate::{as_number_or_return, float, prelude::KalkValue};
|
||||
|
||||
pub fn factorial(x: KalkNum) -> KalkNum {
|
||||
KalkNum::new((x.value + 1f64).gamma(), &x.unit)
|
||||
pub fn factorial(x: KalkValue) -> KalkValue {
|
||||
let (real, _, unit) = as_number_or_return!(x);
|
||||
|
||||
KalkValue::Number((real + 1f64).gamma(), float!(0), unit)
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) mod funcs {
|
||||
use crate::kalk_num::KalkNum;
|
||||
use crate::kalk_value::KalkValue;
|
||||
use crate::prelude::funcs::abs;
|
||||
use crate::{as_number_or_return, float};
|
||||
|
||||
pub fn arg(x: KalkNum) -> KalkNum {
|
||||
KalkNum::new(x.imaginary_value.atan2(&x.value), &x.unit)
|
||||
pub fn arg(x: KalkValue) -> KalkValue {
|
||||
let (real, imaginary, unit) = as_number_or_return!(x);
|
||||
|
||||
KalkValue::Number(imaginary.atan2(&real), float!(0), unit)
|
||||
}
|
||||
|
||||
pub fn gamma(x: KalkNum) -> KalkNum {
|
||||
KalkNum::new(x.value.gamma(), &x.unit)
|
||||
pub fn gamma(x: KalkValue) -> KalkValue {
|
||||
let (real, _, unit) = as_number_or_return!(x);
|
||||
|
||||
KalkValue::Number(real.gamma(), float!(0), unit)
|
||||
}
|
||||
|
||||
pub fn bitcmp(x: KalkNum) -> KalkNum {
|
||||
KalkNum::from(!x.value.to_i32_saturating().unwrap_or(i32::MAX))
|
||||
pub fn bitcmp(x: KalkValue) -> KalkValue {
|
||||
let (real, _, _) = as_number_or_return!(x);
|
||||
|
||||
KalkValue::from(!real.to_i32_saturating().unwrap_or(i32::MAX))
|
||||
}
|
||||
|
||||
pub fn bitand(x: KalkNum, y: KalkNum) -> KalkNum {
|
||||
KalkNum::from(
|
||||
x.value.to_i32_saturating().unwrap_or(i32::MAX)
|
||||
& y.value.to_i32_saturating().unwrap_or(i32::MAX),
|
||||
pub fn bitand(x: KalkValue, y: KalkValue) -> KalkValue {
|
||||
let (real, _, _) = as_number_or_return!(x);
|
||||
let (real_rhs, _, _) = as_number_or_return!(y);
|
||||
|
||||
KalkValue::from(
|
||||
real.to_i32_saturating().unwrap_or(i32::MAX)
|
||||
& real_rhs.to_i32_saturating().unwrap_or(i32::MAX),
|
||||
)
|
||||
}
|
||||
|
||||
pub fn bitor(x: KalkNum, y: KalkNum) -> KalkNum {
|
||||
KalkNum::from(
|
||||
x.value.to_i32_saturating().unwrap_or(i32::MAX)
|
||||
| y.value.to_i32_saturating().unwrap_or(i32::MAX),
|
||||
pub fn bitor(x: KalkValue, y: KalkValue) -> KalkValue {
|
||||
let (real, _, _) = as_number_or_return!(x);
|
||||
let (real_rhs, _, _) = as_number_or_return!(y);
|
||||
|
||||
KalkValue::from(
|
||||
real.to_i32_saturating().unwrap_or(i32::MAX)
|
||||
| real_rhs.to_i32_saturating().unwrap_or(i32::MAX),
|
||||
)
|
||||
}
|
||||
|
||||
pub fn bitxor(x: KalkNum, y: KalkNum) -> KalkNum {
|
||||
KalkNum::from(
|
||||
x.value.to_i32_saturating().unwrap_or(i32::MAX)
|
||||
^ y.value.to_i32_saturating().unwrap_or(i32::MAX),
|
||||
pub fn bitxor(x: KalkValue, y: KalkValue) -> KalkValue {
|
||||
let (real, _, _) = as_number_or_return!(x);
|
||||
let (real_rhs, _, _) = as_number_or_return!(y);
|
||||
|
||||
KalkValue::from(
|
||||
real.to_i32_saturating().unwrap_or(i32::MAX)
|
||||
^ real_rhs.to_i32_saturating().unwrap_or(i32::MAX),
|
||||
)
|
||||
}
|
||||
|
||||
pub fn bitshift(x: KalkNum, y: KalkNum) -> KalkNum {
|
||||
let x = x.value.to_i32_saturating().unwrap_or(i32::MAX) as i32;
|
||||
let y = y.value.to_i32_saturating().unwrap_or(i32::MAX) as i32;
|
||||
pub fn bitshift(x: KalkValue, y: KalkValue) -> KalkValue {
|
||||
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 {
|
||||
KalkNum::from(x >> y.abs())
|
||||
KalkValue::from(x >> y.abs())
|
||||
} else {
|
||||
KalkNum::from(x << y)
|
||||
KalkValue::from(x << y)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn hypot(x: KalkNum, y: KalkNum) -> KalkNum {
|
||||
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);
|
||||
@ -64,15 +87,21 @@ pub(crate) mod funcs {
|
||||
.add_without_unit(abs_y.clone().mul_without_unit(abs_y)),
|
||||
)
|
||||
} else {
|
||||
KalkNum::new(x.value.hypot(&y.value), &x.unit)
|
||||
KalkValue::Number(real.hypot(&real_rhs), float!(0), unit)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn max(x: KalkNum, y: KalkNum) -> KalkNum {
|
||||
KalkNum::new(x.value.max(&y.value), &x.unit)
|
||||
pub fn max(x: KalkValue, y: KalkValue) -> KalkValue {
|
||||
let (real, _, unit) = as_number_or_return!(x);
|
||||
let (real_rhs, _, _) = as_number_or_return!(y);
|
||||
|
||||
KalkValue::Number(real.max(&real_rhs), float!(0), unit)
|
||||
}
|
||||
|
||||
pub fn min(x: KalkNum, y: KalkNum) -> KalkNum {
|
||||
KalkNum::new(x.value.min(&y.value), &x.unit)
|
||||
pub fn min(x: KalkValue, y: KalkValue) -> KalkValue {
|
||||
let (real, _, unit) = as_number_or_return!(x);
|
||||
let (real_rhs, _, _) = as_number_or_return!(y);
|
||||
|
||||
KalkValue::Number(real.min(&real_rhs), float!(0), unit)
|
||||
}
|
||||
}
|
||||
|
@ -60,7 +60,7 @@ pub fn float_to_radix(value: f64, radix: u8) -> String {
|
||||
|
||||
pub fn to_radix_pretty(value: f64, radix: u8) -> String {
|
||||
if radix == 10 {
|
||||
crate::kalk_num::format_number(value)
|
||||
crate::kalk_value::format_number(value)
|
||||
} else {
|
||||
format!(
|
||||
"{}{}",
|
||||
|
Loading…
Reference in New Issue
Block a user