mirror of
https://github.com/PaddiM8/kalker.git
synced 2024-12-13 10:00:51 +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 {
|
pub fn build_literal_ast(kalk_value: &crate::kalk_value::KalkValue) -> Expr {
|
||||||
if kalk_num.has_imaginary() {
|
if kalk_value.has_imaginary() {
|
||||||
Expr::Binary(
|
Expr::Binary(
|
||||||
Box::new(Expr::Literal(kalk_num.to_f64())),
|
Box::new(Expr::Literal(kalk_value.to_f64())),
|
||||||
TokenKind::Plus,
|
TokenKind::Plus,
|
||||||
Box::new(Expr::Binary(
|
Box::new(Expr::Binary(
|
||||||
Box::new(Expr::Literal(kalk_num.imaginary_to_f64())),
|
Box::new(Expr::Literal(kalk_value.imaginary_to_f64())),
|
||||||
TokenKind::Star,
|
TokenKind::Star,
|
||||||
Box::new(Expr::Var(Identifier::from_full_name("i"))),
|
Box::new(Expr::Var(Identifier::from_full_name("i"))),
|
||||||
)),
|
)),
|
||||||
)
|
)
|
||||||
} else {
|
} 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;
|
||||||
use crate::ast::Expr;
|
use crate::ast::Expr;
|
||||||
use crate::ast::Identifier;
|
use crate::ast::Identifier;
|
||||||
use crate::ast::Stmt;
|
use crate::ast::Stmt;
|
||||||
|
use crate::float;
|
||||||
use crate::interpreter;
|
use crate::interpreter;
|
||||||
use crate::kalk_num::KalkNum;
|
use crate::kalk_value::KalkValue;
|
||||||
use crate::lexer::TokenKind;
|
use crate::lexer::TokenKind;
|
||||||
use crate::parser::CalcError;
|
use crate::parser::CalcError;
|
||||||
|
|
||||||
pub fn derive_func(
|
pub fn derive_func(
|
||||||
context: &mut interpreter::Context,
|
context: &mut interpreter::Context,
|
||||||
name: &Identifier,
|
name: &Identifier,
|
||||||
argument: KalkNum,
|
argument: KalkValue,
|
||||||
) -> Result<KalkNum, CalcError> {
|
) -> Result<KalkValue, CalcError> {
|
||||||
const H: f64 = 0.000001;
|
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_with_h = ast::build_literal_ast(&argument.clone().add_without_unit(H.into()));
|
||||||
let argument_without_h = ast::build_literal_ast(&argument.sub_without_unit(H.into()));
|
let argument_without_h = ast::build_literal_ast(&argument.sub_without_unit(H.into()));
|
||||||
@ -34,7 +36,7 @@ pub fn integrate_with_unknown_variable(
|
|||||||
a: &Expr,
|
a: &Expr,
|
||||||
b: &Expr,
|
b: &Expr,
|
||||||
expr: &Expr,
|
expr: &Expr,
|
||||||
) -> Result<KalkNum, CalcError> {
|
) -> Result<KalkValue, CalcError> {
|
||||||
let mut integration_variable: Option<&str> = None;
|
let mut integration_variable: Option<&str> = None;
|
||||||
|
|
||||||
// integral(a, b, expr dx)
|
// integral(a, b, expr dx)
|
||||||
@ -66,7 +68,7 @@ pub fn integrate(
|
|||||||
b: &Expr,
|
b: &Expr,
|
||||||
expr: &Expr,
|
expr: &Expr,
|
||||||
integration_variable: &str,
|
integration_variable: &str,
|
||||||
) -> Result<KalkNum, CalcError> {
|
) -> Result<KalkValue, CalcError> {
|
||||||
Ok(simpsons_rule(context, a, b, expr, integration_variable)?.round_if_needed())
|
Ok(simpsons_rule(context, a, b, expr, integration_variable)?.round_if_needed())
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -77,8 +79,9 @@ fn simpsons_rule(
|
|||||||
b_expr: &Expr,
|
b_expr: &Expr,
|
||||||
expr: &Expr,
|
expr: &Expr,
|
||||||
integration_variable: &str,
|
integration_variable: &str,
|
||||||
) -> Result<KalkNum, CalcError> {
|
) -> Result<KalkValue, CalcError> {
|
||||||
let mut result = KalkNum::default();
|
let mut result_real = float!(0);
|
||||||
|
let mut result_imaginary = float!(0);
|
||||||
let original_variable_value = context
|
let original_variable_value = context
|
||||||
.symbol_table
|
.symbol_table
|
||||||
.get_and_remove_var(integration_variable);
|
.get_and_remove_var(integration_variable);
|
||||||
@ -86,26 +89,27 @@ fn simpsons_rule(
|
|||||||
const N: i32 = 900;
|
const N: i32 = 900;
|
||||||
let a = interpreter::eval_expr(context, a_expr, "")?;
|
let a = interpreter::eval_expr(context, a_expr, "")?;
|
||||||
let b = interpreter::eval_expr(context, b_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 {
|
for i in 0..=N {
|
||||||
let variable_value = a
|
let variable_value = a
|
||||||
.clone()
|
.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(
|
context.symbol_table.set(Stmt::VarDecl(
|
||||||
Identifier::from_full_name(integration_variable),
|
Identifier::from_full_name(integration_variable),
|
||||||
Box::new(crate::ast::build_literal_ast(&variable_value)),
|
Box::new(crate::ast::build_literal_ast(&variable_value)),
|
||||||
));
|
));
|
||||||
|
|
||||||
let factor = KalkNum::from(match i {
|
let factor = KalkValue::from(match i {
|
||||||
0 | N => 1,
|
0 | N => 1,
|
||||||
_ if i % 3 == 0 => 2,
|
_ if i % 3 == 0 => 2,
|
||||||
_ => 3,
|
_ => 3,
|
||||||
});
|
});
|
||||||
|
|
||||||
// factor * f(x_n)
|
// factor * f(x_n)
|
||||||
let mul = factor.mul_without_unit(interpreter::eval_expr(context, expr, "")?);
|
let (mul_real, mul_imaginary, _) =
|
||||||
result.value += mul.value;
|
as_number_or_zero!(factor.mul_without_unit(interpreter::eval_expr(context, expr, "")?));
|
||||||
result.imaginary_value += mul.imaginary_value;
|
result_real += mul_real;
|
||||||
|
result_imaginary += mul_imaginary;
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Some(value) = original_variable_value {
|
if let Some(value) = original_variable_value {
|
||||||
@ -116,10 +120,13 @@ fn simpsons_rule(
|
|||||||
.get_and_remove_var(integration_variable);
|
.get_and_remove_var(integration_variable);
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(result.mul_without_unit(KalkNum::new_with_imaginary(
|
let result = KalkValue::Number(result_real, result_imaginary, String::new());
|
||||||
3f64 / 8f64 * h.value,
|
let (h_real, h_imaginary, h_unit) = as_number_or_zero!(h);
|
||||||
&h.unit,
|
|
||||||
3f64 / 8f64 * h.imaginary_value,
|
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::ast;
|
||||||
use crate::calculus::Identifier;
|
use crate::calculus::Identifier;
|
||||||
use crate::calculus::Stmt;
|
use crate::calculus::Stmt;
|
||||||
|
use crate::float;
|
||||||
use crate::interpreter;
|
use crate::interpreter;
|
||||||
use crate::kalk_num::KalkNum;
|
use crate::kalk_value::KalkValue;
|
||||||
use crate::lexer::TokenKind::*;
|
use crate::lexer::TokenKind::*;
|
||||||
use crate::symbol_table::SymbolTable;
|
use crate::symbol_table::SymbolTable;
|
||||||
use crate::test_helpers::*;
|
use crate::test_helpers::*;
|
||||||
@ -210,7 +218,7 @@ mod tests {
|
|||||||
let result = super::derive_func(
|
let result = super::derive_func(
|
||||||
&mut context,
|
&mut context,
|
||||||
&Identifier::from_full_name("f'"),
|
&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();
|
.unwrap();
|
||||||
assert!(cmp(result.to_f64(), -4.5f64) || cmp(result.to_f64(), -4.499999f64));
|
assert!(cmp(result.to_f64(), -4.5f64) || cmp(result.to_f64(), -4.499999f64));
|
||||||
@ -255,10 +263,10 @@ mod tests {
|
|||||||
let result = super::integrate(
|
let result = super::integrate(
|
||||||
&mut context,
|
&mut context,
|
||||||
&*literal(2f64),
|
&*literal(2f64),
|
||||||
&ast::build_literal_ast(&KalkNum::new_with_imaginary(
|
&ast::build_literal_ast(&KalkValue::Number(
|
||||||
KalkNum::from(3f64).value,
|
float!(3f64),
|
||||||
"",
|
float!(4f64),
|
||||||
KalkNum::from(4f64).value,
|
String::new(),
|
||||||
)),
|
)),
|
||||||
&*binary(var("x"), Star, var("i")),
|
&*binary(var("x"), Star, var("i")),
|
||||||
"x",
|
"x",
|
||||||
|
@ -2,9 +2,9 @@
|
|||||||
mod tests {
|
mod tests {
|
||||||
use std::{fs::File, io::Read, path::PathBuf};
|
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();
|
let mut path = PathBuf::new();
|
||||||
path.push(env!("CARGO_MANIFEST_DIR"));
|
path.push(env!("CARGO_MANIFEST_DIR"));
|
||||||
path.push("..");
|
path.push("..");
|
||||||
@ -19,9 +19,17 @@ mod tests {
|
|||||||
.unwrap();
|
.unwrap();
|
||||||
let mut context = crate::parser::Context::new();
|
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()
|
||||||
.unwrap()
|
.unwrap()
|
||||||
|
.get_value();
|
||||||
|
|
||||||
|
#[cfg(not(feature = "rug"))]
|
||||||
|
crate::parser::eval(&mut context, &file_content)
|
||||||
|
.unwrap()
|
||||||
|
.unwrap()
|
||||||
|
.get_value()
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
@ -1,12 +1,13 @@
|
|||||||
use crate::ast::Identifier;
|
use crate::ast::Identifier;
|
||||||
use crate::ast::{Expr, Stmt};
|
use crate::ast::{Expr, Stmt};
|
||||||
use crate::calculus;
|
use crate::calculation_result::CalculationResult;
|
||||||
use crate::kalk_num::KalkNum;
|
use crate::kalk_value::KalkValue;
|
||||||
use crate::lexer::TokenKind;
|
use crate::lexer::TokenKind;
|
||||||
use crate::parser::CalcError;
|
use crate::parser::CalcError;
|
||||||
use crate::parser::DECL_UNIT;
|
use crate::parser::DECL_UNIT;
|
||||||
use crate::prelude;
|
|
||||||
use crate::symbol_table::SymbolTable;
|
use crate::symbol_table::SymbolTable;
|
||||||
|
use crate::{as_number_or_zero, calculus};
|
||||||
|
use crate::{float, prelude};
|
||||||
|
|
||||||
pub struct Context<'a> {
|
pub struct Context<'a> {
|
||||||
pub symbol_table: &'a mut SymbolTable,
|
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() {
|
for (i, stmt) in statements.iter().enumerate() {
|
||||||
let num = eval_stmt(self, stmt)?;
|
let num = eval_stmt(self, stmt)?;
|
||||||
|
|
||||||
// Insert the last value into the `ans` variable.
|
// 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(
|
Stmt::VarDecl(
|
||||||
Identifier::from_full_name("ans"),
|
Identifier::from_full_name("ans"),
|
||||||
Box::new(Expr::Unit(
|
Box::new(Expr::Unit(
|
||||||
num.unit.clone(),
|
num.get_unit().clone(),
|
||||||
Box::new(crate::ast::build_literal_ast(&num)),
|
Box::new(crate::ast::build_literal_ast(&num)),
|
||||||
)),
|
)),
|
||||||
)
|
)
|
||||||
@ -62,7 +66,7 @@ impl<'a> Context<'a> {
|
|||||||
|
|
||||||
if i == statements.len() - 1 {
|
if i == statements.len() - 1 {
|
||||||
if let Stmt::Expr(_) = stmt {
|
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 {
|
match stmt {
|
||||||
Stmt::VarDecl(_, _) => eval_var_decl_stmt(context, stmt),
|
Stmt::VarDecl(_, _) => eval_var_decl_stmt(context, stmt),
|
||||||
Stmt::FnDecl(_, _, _) => eval_fn_decl_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());
|
context.symbol_table.insert(stmt.clone());
|
||||||
Ok(KalkNum::from(1))
|
Ok(KalkValue::from(1))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn eval_fn_decl_stmt() -> Result<KalkNum, CalcError> {
|
fn eval_fn_decl_stmt() -> Result<KalkValue, CalcError> {
|
||||||
Ok(KalkNum::from(1)) // Nothing needs to happen here, since the parser will already have added the FnDecl's to the symbol table.
|
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> {
|
fn eval_unit_decl_stmt() -> Result<KalkValue, CalcError> {
|
||||||
Ok(KalkNum::from(1))
|
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, "")
|
eval_expr(context, &expr, "")
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -101,7 +105,7 @@ pub(crate) fn eval_expr(
|
|||||||
context: &mut Context,
|
context: &mut Context,
|
||||||
expr: &Expr,
|
expr: &Expr,
|
||||||
unit: &str,
|
unit: &str,
|
||||||
) -> Result<KalkNum, CalcError> {
|
) -> Result<KalkValue, CalcError> {
|
||||||
#[cfg(not(target_arch = "wasm32"))]
|
#[cfg(not(target_arch = "wasm32"))]
|
||||||
if let (Ok(elapsed), Some(timeout)) = (context.start_time.elapsed(), context.timeout) {
|
if let (Ok(elapsed), Some(timeout)) = (context.start_time.elapsed(), context.timeout) {
|
||||||
if elapsed.as_millis() >= timeout {
|
if elapsed.as_millis() >= timeout {
|
||||||
@ -129,12 +133,12 @@ fn eval_binary_expr(
|
|||||||
op: &TokenKind,
|
op: &TokenKind,
|
||||||
right_expr: &Expr,
|
right_expr: &Expr,
|
||||||
unit: &str,
|
unit: &str,
|
||||||
) -> Result<KalkNum, CalcError> {
|
) -> Result<KalkValue, CalcError> {
|
||||||
if let TokenKind::ToKeyword = op {
|
if let TokenKind::ToKeyword = op {
|
||||||
// TODO: When the unit conversion function takes a Float instead of Expr,
|
// TODO: When the unit conversion function takes a Float instead of Expr,
|
||||||
// move this to the match statement further down.
|
// move this to the match statement further down.
|
||||||
if let Expr::Var(right_unit) = right_expr {
|
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);
|
return convert_unit(context, left_expr, &left_unit, &right_unit.full_name);
|
||||||
// TODO: Avoid evaluating this twice.
|
// 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::Plus => left.add(context, right),
|
||||||
TokenKind::Minus => left.sub(context, right),
|
TokenKind::Minus => left.sub(context, right),
|
||||||
TokenKind::Star => left.mul(context, right),
|
TokenKind::Star => left.mul(context, right),
|
||||||
@ -162,11 +166,13 @@ fn eval_binary_expr(
|
|||||||
TokenKind::LessThan => left.less_than(context, right),
|
TokenKind::LessThan => left.less_than(context, right),
|
||||||
TokenKind::GreaterOrEquals => left.greater_or_equals(context, right),
|
TokenKind::GreaterOrEquals => left.greater_or_equals(context, right),
|
||||||
TokenKind::LessOrEquals => left.less_or_equals(context, right),
|
TokenKind::LessOrEquals => left.less_or_equals(context, right),
|
||||||
_ => KalkNum::from(1),
|
_ => KalkValue::from(1),
|
||||||
};
|
};
|
||||||
|
|
||||||
if unit.len() > 0 {
|
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)
|
Ok(result)
|
||||||
@ -177,12 +183,12 @@ fn eval_unary_expr(
|
|||||||
op: &TokenKind,
|
op: &TokenKind,
|
||||||
expr: &Expr,
|
expr: &Expr,
|
||||||
unit: &str,
|
unit: &str,
|
||||||
) -> Result<KalkNum, CalcError> {
|
) -> Result<KalkValue, CalcError> {
|
||||||
let num = eval_expr(context, &expr, unit)?;
|
let num = eval_expr(context, &expr, unit)?;
|
||||||
|
|
||||||
match op {
|
match op {
|
||||||
TokenKind::Minus => Ok(num.mul(context, KalkNum::from(-1f64))),
|
TokenKind::Minus => Ok(num.mul(context, KalkValue::from(-1f64))),
|
||||||
TokenKind::Percent => Ok(num.mul(context, KalkNum::from(0.01f64))),
|
TokenKind::Percent => Ok(num.mul(context, KalkValue::from(0.01f64))),
|
||||||
TokenKind::Exclamation => Ok(prelude::special_funcs::factorial(num)),
|
TokenKind::Exclamation => Ok(prelude::special_funcs::factorial(num)),
|
||||||
_ => Err(CalcError::InvalidOperator),
|
_ => Err(CalcError::InvalidOperator),
|
||||||
}
|
}
|
||||||
@ -192,7 +198,7 @@ fn eval_unit_expr(
|
|||||||
context: &mut Context,
|
context: &mut Context,
|
||||||
identifier: &str,
|
identifier: &str,
|
||||||
expr: &Expr,
|
expr: &Expr,
|
||||||
) -> Result<KalkNum, CalcError> {
|
) -> Result<KalkValue, CalcError> {
|
||||||
let angle_unit = &context.angle_unit.clone();
|
let angle_unit = &context.angle_unit.clone();
|
||||||
if (identifier == "rad" || identifier == "deg") && angle_unit != identifier {
|
if (identifier == "rad" || identifier == "deg") && angle_unit != identifier {
|
||||||
return convert_unit(context, expr, identifier, angle_unit);
|
return convert_unit(context, expr, identifier, angle_unit);
|
||||||
@ -206,7 +212,7 @@ pub fn convert_unit(
|
|||||||
expr: &Expr,
|
expr: &Expr,
|
||||||
from_unit: &str,
|
from_unit: &str,
|
||||||
to_unit: &str,
|
to_unit: &str,
|
||||||
) -> Result<KalkNum, CalcError> {
|
) -> Result<KalkValue, CalcError> {
|
||||||
if let Some(Stmt::UnitDecl(_, _, unit_def)) =
|
if let Some(Stmt::UnitDecl(_, _, unit_def)) =
|
||||||
context.symbol_table.get_unit(to_unit, from_unit).cloned()
|
context.symbol_table.get_unit(to_unit, from_unit).cloned()
|
||||||
{
|
{
|
||||||
@ -215,12 +221,8 @@ pub fn convert_unit(
|
|||||||
Box::new(expr.clone()),
|
Box::new(expr.clone()),
|
||||||
));
|
));
|
||||||
|
|
||||||
let num = eval_expr(context, &unit_def, "")?;
|
let (real, imaginary, _) = as_number_or_zero!(eval_expr(context, &unit_def, "")?);
|
||||||
Ok(KalkNum::new_with_imaginary(
|
Ok(KalkValue::Number(real, imaginary, to_unit.into()))
|
||||||
num.value,
|
|
||||||
to_unit.into(),
|
|
||||||
num.imaginary_value,
|
|
||||||
))
|
|
||||||
} else {
|
} else {
|
||||||
Err(CalcError::InvalidUnit)
|
Err(CalcError::InvalidUnit)
|
||||||
}
|
}
|
||||||
@ -230,7 +232,7 @@ fn eval_var_expr(
|
|||||||
context: &mut Context,
|
context: &mut Context,
|
||||||
identifier: &Identifier,
|
identifier: &Identifier,
|
||||||
unit: &str,
|
unit: &str,
|
||||||
) -> Result<KalkNum, CalcError> {
|
) -> Result<KalkValue, CalcError> {
|
||||||
// If there is a constant with this name, return a literal expression with its value
|
// 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) {
|
if let Some(value) = prelude::CONSTANTS.get(identifier.full_name.as_ref() as &str) {
|
||||||
return eval_expr(context, &Expr::Literal(*value), unit);
|
return eval_expr(context, &Expr::Literal(*value), unit);
|
||||||
@ -238,7 +240,7 @@ fn eval_var_expr(
|
|||||||
|
|
||||||
if identifier.full_name == "n" {
|
if identifier.full_name == "n" {
|
||||||
if let Some(value) = context.sum_n_value {
|
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)]
|
#[allow(unused_variables)]
|
||||||
fn eval_literal_expr(context: &mut Context, value: f64, unit: &str) -> Result<KalkNum, CalcError> {
|
#[cfg(feature = "rug")]
|
||||||
let mut num: KalkNum = value.into();
|
fn eval_literal_expr(
|
||||||
num.unit = unit.into();
|
context: &mut Context,
|
||||||
|
value: f64,
|
||||||
|
unit: &str,
|
||||||
|
) -> Result<KalkValue, CalcError> {
|
||||||
|
let mut float = float!(value);
|
||||||
|
float.set_prec(context.precision);
|
||||||
|
|
||||||
#[cfg(feature = "rug")]
|
Ok(KalkValue::Number(float, float!(0), unit.to_string()))
|
||||||
num.value.set_prec(context.precision);
|
|
||||||
|
|
||||||
Ok(num)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
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)
|
eval_expr(context, expr, unit)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -273,7 +291,7 @@ pub(crate) fn eval_fn_call_expr(
|
|||||||
identifier: &Identifier,
|
identifier: &Identifier,
|
||||||
expressions: &[Expr],
|
expressions: &[Expr],
|
||||||
unit: &str,
|
unit: &str,
|
||||||
) -> Result<KalkNum, CalcError> {
|
) -> Result<KalkValue, CalcError> {
|
||||||
// Prelude
|
// Prelude
|
||||||
let prelude_func = match expressions.len() {
|
let prelude_func = match expressions.len() {
|
||||||
1 => {
|
1 => {
|
||||||
@ -327,27 +345,26 @@ pub(crate) fn eval_fn_call_expr(
|
|||||||
_ => unreachable!(),
|
_ => unreachable!(),
|
||||||
};
|
};
|
||||||
let mut sum = if sum_else_prod {
|
let mut sum = if sum_else_prod {
|
||||||
KalkNum::default()
|
KalkValue::from(0f64)
|
||||||
} else {
|
} else {
|
||||||
KalkNum::from(1f64)
|
KalkValue::from(1f64)
|
||||||
};
|
};
|
||||||
|
|
||||||
for n in start..=end {
|
for n in start..=end {
|
||||||
context.sum_n_value = Some(n);
|
context.sum_n_value = Some(n);
|
||||||
let eval = eval_expr(context, &expressions[2], "")?;
|
let eval = eval_expr(context, &expressions[2], "")?;
|
||||||
if sum_else_prod {
|
if sum_else_prod {
|
||||||
sum.value += eval.value;
|
sum = sum.add(context, eval);
|
||||||
sum.imaginary_value += eval.imaginary_value;
|
|
||||||
} else {
|
} else {
|
||||||
sum.value *= eval.value;
|
sum = sum.mul(context, eval);
|
||||||
sum.imaginary_value *= eval.imaginary_value;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
context.sum_n_value = None;
|
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" => {
|
"integrate" => {
|
||||||
return match expressions.len() {
|
return match expressions.len() {
|
||||||
@ -447,9 +464,9 @@ fn eval_piecewise(
|
|||||||
context: &mut Context,
|
context: &mut Context,
|
||||||
pieces: &Vec<crate::ast::ConditionalPiece>,
|
pieces: &Vec<crate::ast::ConditionalPiece>,
|
||||||
unit: &str,
|
unit: &str,
|
||||||
) -> Result<KalkNum, CalcError> {
|
) -> Result<KalkValue, CalcError> {
|
||||||
for piece in pieces {
|
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 {
|
if condition_is_true {
|
||||||
return Ok(eval_expr(context, &piece.expr, unit)?);
|
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();
|
let mut symbol_table = SymbolTable::new();
|
||||||
symbol_table
|
symbol_table
|
||||||
.insert(DEG_RAD_UNIT.clone())
|
.insert(DEG_RAD_UNIT.clone())
|
||||||
@ -500,9 +517,9 @@ mod tests {
|
|||||||
context(&mut symbol_table, "rad").interpret(vec![stmt])
|
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)? {
|
if let Some(result) = interpret_with_unit(stmt)? {
|
||||||
Ok(Some(result))
|
Ok(Some(result.get_value()))
|
||||||
} else {
|
} else {
|
||||||
Ok(None)
|
Ok(None)
|
||||||
}
|
}
|
||||||
@ -518,10 +535,18 @@ mod tests {
|
|||||||
Context::new(symbol_table, angle_unit, None)
|
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
|
(x.to_f64() - y).abs() < 0.0001
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn bool(x: &KalkValue) -> bool {
|
||||||
|
if let KalkValue::Boolean(boolean_value) = x {
|
||||||
|
*boolean_value
|
||||||
|
} else {
|
||||||
|
false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_literal() {
|
fn test_literal() {
|
||||||
let stmt = Stmt::Expr(literal(1f64));
|
let stmt = Stmt::Expr(literal(1f64));
|
||||||
@ -550,40 +575,28 @@ mod tests {
|
|||||||
assert_eq!(interpret(pow).unwrap().unwrap().to_f64(), 8f64);
|
assert_eq!(interpret(pow).unwrap().unwrap().to_f64(), 8f64);
|
||||||
|
|
||||||
let result = interpret(equals).unwrap().unwrap();
|
let result = interpret(equals).unwrap().unwrap();
|
||||||
assert_eq!(
|
assert_eq!(bool(&result), false);
|
||||||
(result.to_f64(), result.boolean_value.unwrap()),
|
assert!(result.to_f64().is_nan());
|
||||||
(0f64, false)
|
|
||||||
);
|
|
||||||
|
|
||||||
let result = interpret(not_equals).unwrap().unwrap();
|
let result = interpret(not_equals).unwrap().unwrap();
|
||||||
assert_eq!(
|
assert_eq!(bool(&result), true);
|
||||||
(result.to_f64(), result.boolean_value.unwrap()),
|
assert!(result.to_f64().is_nan());
|
||||||
(0f64, true)
|
|
||||||
);
|
|
||||||
|
|
||||||
let result = interpret(greater_than).unwrap().unwrap();
|
let result = interpret(greater_than).unwrap().unwrap();
|
||||||
assert_eq!(
|
assert_eq!(bool(&result), false);
|
||||||
(result.to_f64(), result.boolean_value.unwrap()),
|
assert!(result.to_f64().is_nan());
|
||||||
(0f64, false)
|
|
||||||
);
|
|
||||||
|
|
||||||
let result = interpret(less_than).unwrap().unwrap();
|
let result = interpret(less_than).unwrap().unwrap();
|
||||||
assert_eq!(
|
assert_eq!(bool(&result), true);
|
||||||
(result.to_f64(), result.boolean_value.unwrap()),
|
assert!(result.to_f64().is_nan());
|
||||||
(0f64, true)
|
|
||||||
);
|
|
||||||
|
|
||||||
let result = interpret(greater_or_equals).unwrap().unwrap();
|
let result = interpret(greater_or_equals).unwrap().unwrap();
|
||||||
assert_eq!(
|
assert_eq!(bool(&result), false);
|
||||||
(result.to_f64(), result.boolean_value.unwrap()),
|
assert!(result.to_f64().is_nan());
|
||||||
(0f64, false)
|
|
||||||
);
|
|
||||||
|
|
||||||
let result = interpret(less_or_equals).unwrap().unwrap();
|
let result = interpret(less_or_equals).unwrap().unwrap();
|
||||||
assert_eq!(
|
assert_eq!(bool(&result), true);
|
||||||
(result.to_f64(), result.boolean_value.unwrap()),
|
assert!(result.to_f64().is_nan());
|
||||||
(0f64, true)
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
@ -630,11 +643,16 @@ mod tests {
|
|||||||
rad_context
|
rad_context
|
||||||
.interpret(vec![implicit.clone()])
|
.interpret(vec![implicit.clone()])
|
||||||
.unwrap()
|
.unwrap()
|
||||||
.unwrap(),
|
.unwrap()
|
||||||
|
.get_value(),
|
||||||
0.84147098
|
0.84147098
|
||||||
));
|
));
|
||||||
assert!(cmp(
|
assert!(cmp(
|
||||||
deg_context.interpret(vec![implicit]).unwrap().unwrap(),
|
deg_context
|
||||||
|
.interpret(vec![implicit])
|
||||||
|
.unwrap()
|
||||||
|
.unwrap()
|
||||||
|
.get_value(),
|
||||||
0.01745240
|
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 ast;
|
||||||
|
pub mod calculation_result;
|
||||||
mod calculus;
|
mod calculus;
|
||||||
mod integration_testing;
|
mod integration_testing;
|
||||||
mod interpreter;
|
mod interpreter;
|
||||||
mod inverter;
|
mod inverter;
|
||||||
pub mod kalk_num;
|
pub mod kalk_value;
|
||||||
mod lexer;
|
mod lexer;
|
||||||
pub mod parser;
|
pub mod parser;
|
||||||
mod prelude;
|
mod prelude;
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
use crate::ast::Identifier;
|
use crate::ast::Identifier;
|
||||||
use crate::kalk_num::KalkNum;
|
use crate::calculation_result::CalculationResult;
|
||||||
use crate::{
|
use crate::{
|
||||||
ast::{Expr, Stmt},
|
ast::{Expr, Stmt},
|
||||||
interpreter, inverter,
|
interpreter, inverter,
|
||||||
@ -79,7 +79,7 @@ impl Context {
|
|||||||
|
|
||||||
#[wasm_bindgen(js_name = evaluate)]
|
#[wasm_bindgen(js_name = evaluate)]
|
||||||
#[cfg(not(feature = "rug"))]
|
#[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);
|
let result = eval(self, input);
|
||||||
|
|
||||||
match result {
|
match result {
|
||||||
@ -158,7 +158,7 @@ pub fn eval(
|
|||||||
context: &mut Context,
|
context: &mut Context,
|
||||||
input: &str,
|
input: &str,
|
||||||
#[cfg(feature = "rug")] precision: u32,
|
#[cfg(feature = "rug")] precision: u32,
|
||||||
) -> Result<Option<KalkNum>, CalcError> {
|
) -> Result<Option<CalculationResult>, CalcError> {
|
||||||
// Variable and function declaration parsers will set this to false
|
// Variable and function declaration parsers will set this to false
|
||||||
// if the equal sign is for one of those instead.
|
// if the equal sign is for one of those instead.
|
||||||
// It also should not contain an iverson bracket, since equal signs in there
|
// 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);
|
let result = interpreter.interpret(statements);
|
||||||
if let Ok(Some(mut num)) = result {
|
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))
|
Ok(Some(num))
|
||||||
} else {
|
} else {
|
||||||
result
|
result
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
use crate::kalk_num::KalkNum;
|
use crate::kalk_value::KalkValue;
|
||||||
use lazy_static::lazy_static;
|
use lazy_static::lazy_static;
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
use FuncType::*;
|
use FuncType::*;
|
||||||
@ -77,7 +77,7 @@ lazy_static! {
|
|||||||
m.insert("abs", (UnaryFuncInfo(abs, Other), ""));
|
m.insert("abs", (UnaryFuncInfo(abs, Other), ""));
|
||||||
m.insert("cbrt", (UnaryFuncInfo(cbrt, Other), ""));
|
m.insert("cbrt", (UnaryFuncInfo(cbrt, Other), ""));
|
||||||
m.insert("ceil", (UnaryFuncInfo(ceil, 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("exp", (UnaryFuncInfo(exp, Other), ""));
|
||||||
m.insert("floor", (UnaryFuncInfo(floor, Other), ""));
|
m.insert("floor", (UnaryFuncInfo(floor, Other), ""));
|
||||||
m.insert("frac", (UnaryFuncInfo(frac, Other), ""));
|
m.insert("frac", (UnaryFuncInfo(frac, Other), ""));
|
||||||
@ -121,13 +121,17 @@ enum FuncType {
|
|||||||
Other,
|
Other,
|
||||||
}
|
}
|
||||||
|
|
||||||
// Unary functions
|
pub struct UnaryFuncInfo(fn(KalkValue) -> KalkValue, FuncType);
|
||||||
pub struct UnaryFuncInfo(fn(KalkNum) -> KalkNum, FuncType);
|
|
||||||
|
|
||||||
pub struct BinaryFuncInfo(fn(KalkNum, KalkNum) -> KalkNum, FuncType);
|
pub struct BinaryFuncInfo(fn(KalkValue, KalkValue) -> KalkValue, FuncType);
|
||||||
|
|
||||||
impl UnaryFuncInfo {
|
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;
|
let func = self.0;
|
||||||
match self.1 {
|
match self.1 {
|
||||||
FuncType::Trig => func(from_angle_unit(context, x, angle_unit)),
|
FuncType::Trig => func(from_angle_unit(context, x, angle_unit)),
|
||||||
@ -141,10 +145,10 @@ impl BinaryFuncInfo {
|
|||||||
fn call(
|
fn call(
|
||||||
&self,
|
&self,
|
||||||
context: &mut interpreter::Context,
|
context: &mut interpreter::Context,
|
||||||
x: KalkNum,
|
x: KalkValue,
|
||||||
y: KalkNum,
|
y: KalkValue,
|
||||||
angle_unit: &str,
|
angle_unit: &str,
|
||||||
) -> KalkNum {
|
) -> KalkValue {
|
||||||
let func = self.0;
|
let func = self.0;
|
||||||
match self.1 {
|
match self.1 {
|
||||||
FuncType::Trig => func(
|
FuncType::Trig => func(
|
||||||
@ -177,9 +181,9 @@ pub fn is_constant(identifier: &str) -> bool {
|
|||||||
pub fn call_unary_func(
|
pub fn call_unary_func(
|
||||||
context: &mut interpreter::Context,
|
context: &mut interpreter::Context,
|
||||||
name: &str,
|
name: &str,
|
||||||
x: KalkNum,
|
x: KalkValue,
|
||||||
angle_unit: &str,
|
angle_unit: &str,
|
||||||
) -> Option<(KalkNum, String)> {
|
) -> Option<(KalkValue, String)> {
|
||||||
if let Some((func_info, func_unit)) = UNARY_FUNCS.get(name) {
|
if let Some((func_info, func_unit)) = UNARY_FUNCS.get(name) {
|
||||||
Some((
|
Some((
|
||||||
func_info.call(context, x, &angle_unit),
|
func_info.call(context, x, &angle_unit),
|
||||||
@ -193,10 +197,10 @@ pub fn call_unary_func(
|
|||||||
pub fn call_binary_func(
|
pub fn call_binary_func(
|
||||||
context: &mut interpreter::Context,
|
context: &mut interpreter::Context,
|
||||||
name: &str,
|
name: &str,
|
||||||
x: KalkNum,
|
x: KalkValue,
|
||||||
y: KalkNum,
|
y: KalkValue,
|
||||||
angle_unit: &str,
|
angle_unit: &str,
|
||||||
) -> Option<(KalkNum, String)> {
|
) -> Option<(KalkValue, String)> {
|
||||||
if let Some((func_info, func_unit)) = BINARY_FUNCS.get(name) {
|
if let Some((func_info, func_unit)) = BINARY_FUNCS.get(name) {
|
||||||
Some((
|
Some((
|
||||||
func_info.call(context, x, y, angle_unit),
|
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 {
|
match angle_unit {
|
||||||
"rad" => x,
|
"rad" => x,
|
||||||
_ => interpreter::convert_unit(context, &Expr::Literal(x.to_f64()), "rad", angle_unit)
|
_ => 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 {
|
match angle_unit {
|
||||||
"rad" => x,
|
"rad" => x,
|
||||||
_ => interpreter::convert_unit(context, &Expr::Literal(x.to_f64()), angle_unit, "rad")
|
_ => interpreter::convert_unit(context, &Expr::Literal(x.to_f64()), angle_unit, "rad")
|
||||||
@ -229,305 +237,333 @@ pub mod funcs {
|
|||||||
use super::special_funcs::factorial;
|
use super::special_funcs::factorial;
|
||||||
#[cfg(feature = "rug")]
|
#[cfg(feature = "rug")]
|
||||||
pub use super::with_rug::funcs::*;
|
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 {
|
pub fn abs(x: KalkValue) -> KalkValue {
|
||||||
if x.has_imaginary() {
|
let (real, imaginary, unit) = as_number_or_return!(x);
|
||||||
|
if imaginary != 0f64 {
|
||||||
// |z| = sqrt(a² + b²)
|
// |z| = sqrt(a² + b²)
|
||||||
let a = x.value.clone() * x.value;
|
let a = real.clone() * real;
|
||||||
let b = x.imaginary_value.clone() * x.imaginary_value;
|
let b = imaginary.clone() * imaginary;
|
||||||
|
|
||||||
sqrt(KalkNum::new(a + b, &x.unit))
|
sqrt(KalkValue::Number(a + b, float!(0), unit))
|
||||||
} else {
|
} else {
|
||||||
KalkNum::new(x.value.abs(), &x.unit)
|
KalkValue::Number(real.abs(), float!(0), unit)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn acos(x: KalkNum) -> KalkNum {
|
pub fn acos(x: KalkValue) -> KalkValue {
|
||||||
if x.has_imaginary() || x.value > 1f64 || x.value < -1f64 {
|
let (real, imaginary, unit) = as_number_or_return!(x.clone());
|
||||||
|
if imaginary != 0f64 || real > 1f64 || real < -1f64 {
|
||||||
// -i * ln(i * sqrt(1 - z²) + z)
|
// -i * ln(i * sqrt(1 - z²) + z)
|
||||||
let root =
|
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 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
|
// -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 {
|
} else {
|
||||||
KalkNum::new(x.value.acos(), &x.unit)
|
KalkValue::Number(real.acos(), float!(0), unit)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn acosh(x: KalkNum) -> KalkNum {
|
pub fn acosh(x: KalkValue) -> KalkValue {
|
||||||
if x.has_imaginary() || x.value < 1f64 {
|
let (real, imaginary, unit) = as_number_or_return!(x.clone());
|
||||||
let sqrt1 = sqrt(KalkNum::new_with_imaginary(
|
if imaginary != 0f64 || real < 1f64 {
|
||||||
x.value.clone() + 1f64,
|
let sqrt1 = sqrt(KalkValue::Number(
|
||||||
&x.unit,
|
real.clone() + 1f64,
|
||||||
x.imaginary_value.clone(),
|
imaginary.clone(),
|
||||||
|
unit.clone(),
|
||||||
));
|
));
|
||||||
let sqrt2 = sqrt(KalkNum::new_with_imaginary(
|
let sqrt2 = sqrt(KalkValue::Number(
|
||||||
x.value.clone() - 1f64,
|
real.clone() - 1f64,
|
||||||
&x.unit,
|
imaginary.clone(),
|
||||||
x.imaginary_value.clone(),
|
unit,
|
||||||
));
|
));
|
||||||
|
|
||||||
ln(x.add_without_unit(sqrt1.mul_without_unit(sqrt2)))
|
ln(x.add_without_unit(sqrt1.mul_without_unit(sqrt2)))
|
||||||
} else {
|
} else {
|
||||||
KalkNum::new(x.value.acosh(), &x.unit)
|
KalkValue::Number(real.acosh(), float!(0), unit)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn acot(x: KalkNum) -> KalkNum {
|
pub fn acot(x: KalkValue) -> KalkValue {
|
||||||
if x.has_imaginary() {
|
let (real, imaginary, unit) = as_number_or_return!(x.clone());
|
||||||
|
if imaginary != 0f64 {
|
||||||
// atan(1/z)
|
// atan(1/z)
|
||||||
atan(KalkNum::from(1f64).div_without_unit(x))
|
atan(KalkValue::from(1f64).div_without_unit(x))
|
||||||
} else {
|
} else {
|
||||||
KalkNum::new((1f64 / x.value).atan(), &x.unit)
|
KalkValue::Number((1f64 / real).atan(), float!(0), unit)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn acoth(x: KalkNum) -> KalkNum {
|
pub fn acoth(x: KalkValue) -> KalkValue {
|
||||||
if x.has_imaginary() || x.value <= 1f64 || x.value >= -1f64 {
|
let (real, imaginary, unit) = as_number_or_return!(x.clone());
|
||||||
|
if imaginary != 0f64 || real <= 1f64 || real >= -1f64 {
|
||||||
// 1 / z
|
// 1 / z
|
||||||
let inv_x = KalkNum::from(1f64).div_without_unit(x);
|
let (inv_real, inv_imaginary, inv_unit) =
|
||||||
let ln1 = ln(KalkNum::new_with_imaginary(
|
as_number_or_return!(KalkValue::from(1f64).div_without_unit(x));
|
||||||
1f64 + inv_x.value.clone(),
|
let ln1 = ln(KalkValue::Number(
|
||||||
&inv_x.unit,
|
1f64 + inv_real.clone(),
|
||||||
inv_x.imaginary_value.clone(),
|
inv_imaginary.clone(),
|
||||||
));
|
inv_unit.clone(),
|
||||||
let ln2 = ln(KalkNum::new_with_imaginary(
|
|
||||||
1f64 - inv_x.value,
|
|
||||||
&inv_x.unit,
|
|
||||||
-inv_x.imaginary_value,
|
|
||||||
));
|
));
|
||||||
|
let ln2 = ln(KalkValue::Number(1f64 - inv_real, -inv_imaginary, inv_unit));
|
||||||
|
|
||||||
ln1.sub_without_unit(ln2)
|
ln1.sub_without_unit(ln2)
|
||||||
.div_without_unit(KalkNum::from(2f64))
|
.div_without_unit(KalkValue::from(2f64))
|
||||||
} else {
|
} else {
|
||||||
KalkNum::new((1f64 / x.value).atanh(), &x.unit)
|
KalkValue::Number((1f64 / real).atanh(), float!(0), unit)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn acsc(x: KalkNum) -> KalkNum {
|
pub fn acsc(x: KalkValue) -> KalkValue {
|
||||||
if x.has_imaginary() || x.value < 1f64 || x.value > -1f64 {
|
let (real, imaginary, unit) = as_number_or_return!(x.clone());
|
||||||
|
if imaginary != 0f64 || real < 1f64 || real > -1f64 {
|
||||||
// asin(1/z)
|
// asin(1/z)
|
||||||
asin(KalkNum::from(1f64).div_without_unit(x))
|
asin(KalkValue::from(1f64).div_without_unit(x))
|
||||||
} else {
|
} else {
|
||||||
KalkNum::new((1f64 / x.value).asin(), &x.unit)
|
KalkValue::Number((1f64 / real).asin(), float!(0), unit)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn acsch(x: KalkNum) -> KalkNum {
|
pub fn acsch(x: KalkValue) -> KalkValue {
|
||||||
if x.has_imaginary() || x.value == 0f64 {
|
let (real, imaginary, unit) = as_number_or_return!(x.clone());
|
||||||
let inv_x2 =
|
if imaginary != 0f64 || real == 0f64 {
|
||||||
KalkNum::from(1f64).div_without_unit(x.clone().mul_without_unit(x.clone()));
|
let (inv_x2_real, inv_x2_imaginary, inv_x2_unit) = as_number_or_return!(
|
||||||
let sqrt = sqrt(KalkNum::new_with_imaginary(
|
KalkValue::from(1f64).div_without_unit(x.clone().mul_without_unit(x.clone()))
|
||||||
1f64 + inv_x2.value,
|
);
|
||||||
&inv_x2.unit,
|
let sqrt = sqrt(KalkValue::Number(
|
||||||
inv_x2.imaginary_value,
|
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))
|
ln(sqrt.add_without_unit(inv_x))
|
||||||
} else {
|
} else {
|
||||||
KalkNum::new((1f64 / x.value).asinh(), &x.unit)
|
KalkValue::Number((1f64 / real).asinh(), float!(0), unit)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn asec(x: KalkNum) -> KalkNum {
|
pub fn asec(x: KalkValue) -> KalkValue {
|
||||||
if x.has_imaginary() || x.value < 1f64 || x.value > -1f64 {
|
let (real, imaginary, unit) = as_number_or_return!(x.clone());
|
||||||
|
if imaginary != 0f64 || real < 1f64 || real > -1f64 {
|
||||||
// acos(1/z)
|
// acos(1/z)
|
||||||
acos(KalkNum::from(1f64).div_without_unit(x))
|
acos(KalkValue::from(1f64).div_without_unit(x))
|
||||||
} else {
|
} else {
|
||||||
KalkNum::new((1f64 / x.value).acos(), &x.unit)
|
KalkValue::Number((1f64 / real).acos(), float!(0), unit)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn asech(x: KalkNum) -> KalkNum {
|
pub fn asech(x: KalkValue) -> KalkValue {
|
||||||
if x.has_imaginary() || x.value <= 0f64 || x.value > 1f64 {
|
let (real, imaginary, unit) = as_number_or_return!(x.clone());
|
||||||
|
if imaginary != 0f64 || real <= 0f64 || real > 1f64 {
|
||||||
// 1/z
|
// 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)
|
// sqrt(1/z - 1)
|
||||||
let sqrt1 = sqrt(KalkNum::new_with_imaginary(
|
let sqrt1 = sqrt(KalkValue::Number(
|
||||||
inv_x.value.clone() - 1f64,
|
inv_real.clone() - 1f64,
|
||||||
&inv_x.unit,
|
inv_imaginary.clone(),
|
||||||
inv_x.imaginary_value.clone(),
|
inv_unit.clone(),
|
||||||
));
|
));
|
||||||
// sqrt(1/z + 1)
|
// sqrt(1/z + 1)
|
||||||
let sqrt2 = sqrt(KalkNum::new_with_imaginary(
|
let sqrt2 = sqrt(KalkValue::Number(
|
||||||
inv_x.value.clone() + 1f64,
|
inv_real.clone() + 1f64,
|
||||||
&inv_x.unit,
|
inv_imaginary.clone(),
|
||||||
inv_x.imaginary_value.clone(),
|
inv_unit,
|
||||||
));
|
));
|
||||||
|
|
||||||
// ln(1/z + sqrt(1/z - 1) * sqrt(1/z + 1))
|
// 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 {
|
} else {
|
||||||
KalkNum::new((1f64 / x.value).acosh(), &x.unit)
|
KalkValue::Number((1f64 / real).acosh(), float!(0), unit)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn asin(x: KalkNum) -> KalkNum {
|
pub fn asin(x: KalkValue) -> KalkValue {
|
||||||
if x.has_imaginary() || x.value > 1f64 || x.value < -1f64 {
|
let (real, imaginary, unit) = as_number_or_return!(x.clone());
|
||||||
|
if imaginary != 0f64 || real > 1f64 || real < -1f64 {
|
||||||
// i * ln(sqrt(1 - z²) - iz)
|
// i * ln(sqrt(1 - z²) - iz)
|
||||||
let root =
|
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 iz = multiply_with_i(x.clone());
|
||||||
let ln = ln(root.sub_without_unit(iz));
|
let ln = ln(root.sub_without_unit(iz));
|
||||||
multiply_with_i(ln)
|
multiply_with_i(ln)
|
||||||
} else {
|
} else {
|
||||||
KalkNum::new(x.value.asin(), &x.unit)
|
KalkValue::Number(real.asin(), float!(0), unit)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn asinh(x: KalkNum) -> KalkNum {
|
pub fn asinh(x: KalkValue) -> KalkValue {
|
||||||
if x.has_imaginary() {
|
let (real, imaginary, unit) = as_number_or_return!(x.clone());
|
||||||
let x2 = x.clone().mul_without_unit(x.clone());
|
if imaginary != 0f64 {
|
||||||
let sqrt = sqrt(KalkNum::new_with_imaginary(
|
let (x2_real, x2_imaginary, x2_unit) =
|
||||||
x2.value + 1f64,
|
as_number_or_return!(x.clone().mul_without_unit(x.clone()));
|
||||||
&x2.unit,
|
let sqrt = sqrt(KalkValue::Number(x2_real + 1f64, x2_imaginary, x2_unit));
|
||||||
x2.imaginary_value,
|
|
||||||
));
|
|
||||||
|
|
||||||
ln(x.add_without_unit(sqrt))
|
ln(x.add_without_unit(sqrt))
|
||||||
} else {
|
} else {
|
||||||
KalkNum::new(x.value.asinh(), &x.unit)
|
KalkValue::Number(real.asinh(), float!(0), unit)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn atan(x: KalkNum) -> KalkNum {
|
pub fn atan(x: KalkValue) -> KalkValue {
|
||||||
if x.has_imaginary() {
|
let (real, imaginary, unit) = as_number_or_return!(x.clone());
|
||||||
let iz = multiply_with_i(x);
|
if imaginary != 0f64 {
|
||||||
|
let (iz_real, iz_imaginary, iz_unit) = as_number_or_return!(multiply_with_i(x));
|
||||||
|
|
||||||
// 1 - iz
|
// 1 - iz
|
||||||
let neg = KalkNum::new_with_imaginary(
|
let neg = KalkValue::Number(
|
||||||
1f64 - iz.value.clone(),
|
1f64 - iz_real.clone(),
|
||||||
&iz.unit,
|
-iz_imaginary.clone(),
|
||||||
-iz.imaginary_value.clone(),
|
iz_unit.clone(),
|
||||||
);
|
);
|
||||||
|
|
||||||
// 1 + iz
|
// 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)
|
// ln(1 - iz) - ln(1 + iz)
|
||||||
let ln = ln(neg).sub_without_unit(ln(pos));
|
let ln = ln(neg).sub_without_unit(ln(pos));
|
||||||
|
|
||||||
multiply_with_i(ln).div_without_unit(KalkNum::from(2f64))
|
multiply_with_i(ln).div_without_unit(KalkValue::from(2f64))
|
||||||
} else {
|
} else {
|
||||||
KalkNum::new(x.value.atan(), &x.unit)
|
KalkValue::Number(real.atan(), float!(0), unit)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn atanh(x: KalkNum) -> KalkNum {
|
pub fn atanh(x: KalkValue) -> KalkValue {
|
||||||
if x.has_imaginary() || x.value >= 1f64 || x.value <= -1f64 {
|
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)
|
// 1/2 * log(z + 1) - 1/2 * log(-z + 1)
|
||||||
let log1 = ln(KalkNum::new_with_imaginary(
|
let log1 = ln(KalkValue::Number(
|
||||||
1f64 + x.value.clone(),
|
1f64 + real.clone(),
|
||||||
&x.unit,
|
imaginary.clone(),
|
||||||
x.imaginary_value.clone(),
|
unit.clone(),
|
||||||
));
|
|
||||||
let log2 = ln(KalkNum::new_with_imaginary(
|
|
||||||
1f64 - x.value,
|
|
||||||
&x.unit,
|
|
||||||
-x.imaginary_value,
|
|
||||||
));
|
));
|
||||||
|
let log2 = ln(KalkValue::Number(1f64 - real, -imaginary, unit));
|
||||||
|
|
||||||
log1.sub_without_unit(log2)
|
log1.sub_without_unit(log2)
|
||||||
.div_without_unit(KalkNum::from(2f64))
|
.div_without_unit(KalkValue::from(2f64))
|
||||||
} else {
|
} else {
|
||||||
KalkNum::new(x.value.atanh(), &x.unit)
|
KalkValue::Number(real.atanh(), float!(0), unit)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn cbrt(x: KalkNum) -> KalkNum {
|
pub fn cbrt(x: KalkValue) -> KalkValue {
|
||||||
KalkNum::new(x.value.cbrt(), &x.unit)
|
let (real, _, unit) = as_number_or_return!(x);
|
||||||
|
|
||||||
|
KalkValue::Number(real.cbrt(), float!(0), unit)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn ceil(x: KalkNum) -> KalkNum {
|
pub fn ceil(x: KalkValue) -> KalkValue {
|
||||||
KalkNum::new_with_imaginary(x.value.ceil(), &x.unit, x.imaginary_value.ceil())
|
let (real, imaginary, unit) = as_number_or_return!(x);
|
||||||
|
|
||||||
|
KalkValue::Number(real.ceil(), imaginary.ceil(), unit)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn cos(x: KalkNum) -> KalkNum {
|
pub fn cos(x: KalkValue) -> KalkValue {
|
||||||
KalkNum::new_with_imaginary(
|
let (real, imaginary, unit) = as_number_or_return!(x);
|
||||||
x.value.clone().cos() * x.imaginary_value.clone().cosh(),
|
|
||||||
&x.unit,
|
KalkValue::Number(
|
||||||
-x.value.sin() * x.imaginary_value.sinh(),
|
real.clone().cos() * imaginary.clone().cosh(),
|
||||||
|
-real.sin() * imaginary.sinh(),
|
||||||
|
unit,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn cosh(x: KalkNum) -> KalkNum {
|
pub fn cosh(x: KalkValue) -> KalkValue {
|
||||||
KalkNum::new_with_imaginary(
|
let (real, imaginary, unit) = as_number_or_return!(x);
|
||||||
x.value.clone().cosh() * x.imaginary_value.clone().cos(),
|
|
||||||
&x.unit,
|
KalkValue::Number(
|
||||||
x.value.sinh() * x.imaginary_value.sin(),
|
real.clone().cosh() * imaginary.clone().cos(),
|
||||||
|
real.sinh() * imaginary.sin(),
|
||||||
|
unit,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn csc(x: KalkNum) -> KalkNum {
|
pub fn csc(x: KalkValue) -> KalkValue {
|
||||||
KalkNum::from(1f64).div_without_unit(sin(x))
|
KalkValue::from(1f64).div_without_unit(sin(x))
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn csch(x: KalkNum) -> KalkNum {
|
pub fn csch(x: KalkValue) -> KalkValue {
|
||||||
KalkNum::from(1f64).div_without_unit(sinh(x))
|
KalkValue::from(1f64).div_without_unit(sinh(x))
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn cot(x: KalkNum) -> KalkNum {
|
pub fn cot(x: KalkValue) -> KalkValue {
|
||||||
let a = x.value * 2f64;
|
let (real, imaginary, unit) = as_number_or_return!(x);
|
||||||
let b = x.imaginary_value * 2f64;
|
|
||||||
KalkNum::new_with_imaginary(
|
let a = real * 2f64;
|
||||||
|
let b = imaginary * 2f64;
|
||||||
|
KalkValue::Number(
|
||||||
-a.clone().sin() / (a.clone().cos() - b.clone().cosh()),
|
-a.clone().sin() / (a.clone().cos() - b.clone().cosh()),
|
||||||
&x.unit,
|
|
||||||
b.clone().sinh() / (a.cos() - b.cosh()),
|
b.clone().sinh() / (a.cos() - b.cosh()),
|
||||||
|
unit,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn coth(x: KalkNum) -> KalkNum {
|
pub fn coth(x: KalkValue) -> KalkValue {
|
||||||
let a = x.value * 2f64;
|
let (real, imaginary, unit) = as_number_or_return!(x);
|
||||||
let b = x.imaginary_value * 2f64;
|
let a = real * 2f64;
|
||||||
KalkNum::new_with_imaginary(
|
let b = imaginary * 2f64;
|
||||||
|
KalkValue::Number(
|
||||||
-a.clone().sinh() / (b.clone().cos() - a.clone().cosh()),
|
-a.clone().sinh() / (b.clone().cos() - a.clone().cosh()),
|
||||||
&x.unit,
|
|
||||||
b.clone().sin() / (b.cos() - a.cosh()),
|
b.clone().sin() / (b.cos() - a.cosh()),
|
||||||
|
unit,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn exp(x: KalkNum) -> KalkNum {
|
pub fn exp(x: KalkValue) -> KalkValue {
|
||||||
if x.has_imaginary() {
|
let (real, imaginary, unit) = as_number_or_return!(x);
|
||||||
|
|
||||||
|
if imaginary != 0f64 {
|
||||||
// e^a*cos(b) + ie^a*sin(b)
|
// e^a*cos(b) + ie^a*sin(b)
|
||||||
let exp_a = x.value.exp();
|
let exp_a = real.exp();
|
||||||
let b = x.imaginary_value;
|
let b = imaginary;
|
||||||
KalkNum::new_with_imaginary(exp_a.clone() * b.clone().cos(), &x.unit, exp_a * b.sin())
|
KalkValue::Number(exp_a.clone() * b.clone().cos(), exp_a * b.sin(), unit)
|
||||||
} else {
|
} else {
|
||||||
KalkNum::new(x.value.exp(), &x.unit)
|
KalkValue::Number(real.exp(), float!(0), unit)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn floor(x: KalkNum) -> KalkNum {
|
pub fn floor(x: KalkValue) -> KalkValue {
|
||||||
KalkNum::new_with_imaginary(x.value.floor(), &x.unit, x.imaginary_value.floor())
|
let (real, imaginary, unit) = as_number_or_return!(x);
|
||||||
|
|
||||||
|
KalkValue::Number(real.floor(), imaginary.floor(), unit)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn frac(x: KalkNum) -> KalkNum {
|
pub fn frac(x: KalkValue) -> KalkValue {
|
||||||
KalkNum::new_with_imaginary(x.value.fract(), &x.unit, x.imaginary_value.fract())
|
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
|
// Find the norm of a Gaussian integer
|
||||||
fn norm(x: KalkNum) -> KalkNum {
|
fn norm(x: KalkValue) -> KalkValue {
|
||||||
KalkNum::new(
|
let (real, imaginary, unit) = as_number_or_return!(x);
|
||||||
(x.value.clone() * x.value) + (x.imaginary_value.clone() * x.imaginary_value),
|
KalkValue::Number(
|
||||||
&x.unit,
|
(real.clone() * real) + (imaginary.clone() * imaginary),
|
||||||
|
float!(0),
|
||||||
|
unit,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
if x.has_imaginary() || y.has_imaginary() {
|
if imaginary != 0f64 || y.has_imaginary() {
|
||||||
if x.value.clone().fract() != 0f64
|
if real.clone().fract() != 0f64
|
||||||
|| y.value.clone().fract() != 0f64
|
|| real_rhs.clone().fract() != 0f64
|
||||||
|| x.imaginary_value.clone().fract() != 0f64
|
|| imaginary.clone().fract() != 0f64
|
||||||
|| y.imaginary_value.clone().fract() != 0f64
|
|| imaginary_rhs.clone().fract() != 0f64
|
||||||
{
|
{
|
||||||
// Not a Gaussian integer!
|
// Not a Gaussian integer!
|
||||||
// TODO: throw an actual error instead of returning NaN
|
// TODO: throw an actual error instead of returning NaN
|
||||||
return KalkNum::from(f64::NAN);
|
return KalkValue::from(f64::NAN);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Partially derived from:
|
// Partially derived from:
|
||||||
@ -537,7 +573,7 @@ pub mod funcs {
|
|||||||
let b;
|
let b;
|
||||||
|
|
||||||
// Ensure a > 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;
|
a = y;
|
||||||
b = x;
|
b = x;
|
||||||
} else {
|
} else {
|
||||||
@ -545,43 +581,46 @@ pub mod funcs {
|
|||||||
b = y;
|
b = y;
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut c = a.clone().div_without_unit(b.clone());
|
let (b_real, b_imaginary, b_unit) = as_number_or_return!(b.clone());
|
||||||
if c.imaginary_value.clone().fract() == 0f64 {
|
let (c_real, c_imaginary, c_unit) =
|
||||||
KalkNum::new_with_imaginary(b.value.abs(), &b.unit, b.imaginary_value)
|
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 {
|
} else {
|
||||||
c.value = c.value.round();
|
let rounded_c = KalkValue::Number(c_real.round(), c_imaginary.round(), c_unit);
|
||||||
c.imaginary_value = c.imaginary_value.round();
|
gcd(a.sub_without_unit(b.clone().mul_without_unit(rounded_c)), b)
|
||||||
gcd(a.sub_without_unit(b.clone().mul_without_unit(c)), b)
|
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if x.value < 0f64 || y.value < 0f64 {
|
if real < 0f64 || real_rhs < 0f64 {
|
||||||
return gcd(
|
return gcd(
|
||||||
KalkNum::new(x.value.abs(), &x.unit),
|
KalkValue::Number(real.abs(), float!(0), unit.clone()),
|
||||||
KalkNum::new(y.value.abs(), &y.unit),
|
KalkValue::Number(real_rhs.abs(), float!(0), unit),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Euclidean GCD algorithm, but with modulus
|
// Euclidean GCD algorithm, but with modulus
|
||||||
let mut x_a = x.clone();
|
let mut x_a = real.clone();
|
||||||
let mut y_a = y.clone();
|
let mut y_a = real_rhs.clone();
|
||||||
while !y_a.value.eq(&0f64) {
|
while !y_a.eq(&0f64) {
|
||||||
let t = y_a.value.clone();
|
let t = y_a.clone();
|
||||||
y_a.value = x_a.value % y_a.value;
|
y_a = x_a % y_a;
|
||||||
x_a.value = t;
|
x_a = t;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Usually we'd need to return max(x, -x), but since we've handled negative
|
// Usually we'd need to return max(x, -x), but since we've handled negative
|
||||||
// values above, that is unnecessary.
|
// values above, that is unnecessary.
|
||||||
x_a
|
KalkValue::Number(x_a, float!(0), unit)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn im(x: KalkNum) -> KalkNum {
|
pub fn im(x: KalkValue) -> KalkValue {
|
||||||
KalkNum::new_with_imaginary(x.value, "", KalkNum::default().value)
|
let (_, imaginary, unit) = as_number_or_return!(x);
|
||||||
|
|
||||||
|
KalkValue::Number(imaginary, float!(0), unit)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn inverson(x: KalkNum) -> KalkNum {
|
pub fn iverson(x: KalkValue) -> KalkValue {
|
||||||
KalkNum::from(if let Some(boolean_value) = x.boolean_value {
|
KalkValue::from(if let KalkValue::Boolean(boolean_value) = x {
|
||||||
if boolean_value {
|
if boolean_value {
|
||||||
1
|
1
|
||||||
} else {
|
} else {
|
||||||
@ -597,165 +636,186 @@ pub mod funcs {
|
|||||||
// lcm(a, b) = ⎜ ───────── ⎟ × ⎜b⎜
|
// lcm(a, b) = ⎜ ───────── ⎟ × ⎜b⎜
|
||||||
// ⎜ gcd(a, 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 gcd = gcd(x.clone(), y.clone());
|
||||||
let absx = KalkNum::new_with_imaginary(x.value.abs(), &x.unit, x.imaginary_value);
|
let absx = KalkValue::Number(real.abs(), imaginary, unit);
|
||||||
let absy = KalkNum::new_with_imaginary(y.value.abs(), &y.unit, y.imaginary_value);
|
let absy = KalkValue::Number(real_rhs.abs(), imaginary_rhs, unit_rhs);
|
||||||
return absx.div_without_unit(gcd).mul_without_unit(absy);
|
return absx.div_without_unit(gcd).mul_without_unit(absy);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn log(x: KalkNum) -> KalkNum {
|
pub fn log(x: KalkValue) -> KalkValue {
|
||||||
if x.has_imaginary() || x.value < 0f64 {
|
let (real, imaginary, unit) = as_number_or_return!(x.clone());
|
||||||
|
if imaginary != 0f64 || real < 0f64 {
|
||||||
// ln(z) / ln(10)
|
// ln(z) / ln(10)
|
||||||
ln(x).div_without_unit(KalkNum::from(10f64.ln()))
|
ln(x).div_without_unit(KalkValue::from(10f64.ln()))
|
||||||
} else {
|
} else {
|
||||||
KalkNum::new(x.value.log10(), &x.unit)
|
KalkValue::Number(real.log10(), float!(0), unit)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn logx(x: KalkNum, y: KalkNum) -> KalkNum {
|
pub fn logx(x: KalkValue, y: KalkValue) -> KalkValue {
|
||||||
if x.has_imaginary() || y.has_imaginary() || x.value < 0f64 || y.value < 0f64 {
|
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(z) / ln(n)
|
||||||
ln(x).div_without_unit(ln(y))
|
ln(x).div_without_unit(ln(y))
|
||||||
} else {
|
} 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 {
|
pub fn ln(x: KalkValue) -> KalkValue {
|
||||||
if x.has_imaginary() || x.value < 0f64 {
|
let (real, imaginary, unit) = as_number_or_return!(x.clone());
|
||||||
|
if imaginary != 0f64 || real < 0f64 {
|
||||||
let r = abs(x.clone());
|
let r = abs(x.clone());
|
||||||
// ln|z| + i * arg z
|
// ln|z| + i * arg z
|
||||||
ln(r).add_without_unit(multiply_with_i(arg(x)))
|
ln(r).add_without_unit(multiply_with_i(arg(x)))
|
||||||
} else {
|
} else {
|
||||||
KalkNum::new(x.value.ln(), &x.unit)
|
KalkValue::Number(real.ln(), float!(0), unit)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn nth_root(x: KalkNum, n: KalkNum) -> KalkNum {
|
pub fn nth_root(x: KalkValue, n: KalkValue) -> KalkValue {
|
||||||
x.pow_without_unit(KalkNum::from(1f64).div_without_unit(n))
|
x.pow_without_unit(KalkValue::from(1f64).div_without_unit(n))
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn re(x: KalkNum) -> KalkNum {
|
pub fn re(x: KalkValue) -> KalkValue {
|
||||||
KalkNum::new_with_imaginary(KalkNum::default().value, "", x.imaginary_value)
|
let (real, _, unit) = as_number_or_return!(x);
|
||||||
|
KalkValue::Number(real, float!(0), unit)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn round(x: KalkNum) -> KalkNum {
|
pub fn round(x: KalkValue) -> KalkValue {
|
||||||
KalkNum::new_with_imaginary(x.value.round(), &x.unit, x.imaginary_value.round())
|
let (real, imaginary, unit) = as_number_or_return!(x);
|
||||||
|
KalkValue::Number(real.round(), imaginary.round(), unit)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn sec(x: KalkNum) -> KalkNum {
|
pub fn sec(x: KalkValue) -> KalkValue {
|
||||||
KalkNum::from(1f64).div_without_unit(cos(x))
|
KalkValue::from(1f64).div_without_unit(cos(x))
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn sech(x: KalkNum) -> KalkNum {
|
pub fn sech(x: KalkValue) -> KalkValue {
|
||||||
KalkNum::from(1f64).div_without_unit(cosh(x))
|
KalkValue::from(1f64).div_without_unit(cosh(x))
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn sin(x: KalkNum) -> KalkNum {
|
pub fn sin(x: KalkValue) -> KalkValue {
|
||||||
KalkNum::new_with_imaginary(
|
let (real, imaginary, unit) = as_number_or_return!(x);
|
||||||
x.value.clone().sin() * x.imaginary_value.clone().cosh(),
|
KalkValue::Number(
|
||||||
&x.unit,
|
real.clone().sin() * imaginary.clone().cosh(),
|
||||||
x.value.cos() * x.imaginary_value.sinh(),
|
real.cos() * imaginary.sinh(),
|
||||||
|
unit,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn sinh(x: KalkNum) -> KalkNum {
|
pub fn sinh(x: KalkValue) -> KalkValue {
|
||||||
KalkNum::new_with_imaginary(
|
let (real, imaginary, unit) = as_number_or_return!(x);
|
||||||
x.value.clone().sinh() * x.imaginary_value.clone().cos(),
|
KalkValue::Number(
|
||||||
&x.unit,
|
real.clone().sinh() * imaginary.clone().cos(),
|
||||||
x.value.cosh() * x.imaginary_value.sin(),
|
real.cosh() * imaginary.sin(),
|
||||||
|
unit,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn sqrt(x: KalkNum) -> KalkNum {
|
pub fn sqrt(x: KalkValue) -> KalkValue {
|
||||||
if x.has_imaginary() {
|
let (real, imaginary, unit) = as_number_or_return!(x.clone());
|
||||||
let abs = abs(x.clone());
|
if imaginary != 0f64 {
|
||||||
let r = abs.value;
|
let (abs_real, _, abs_unit) = as_number_or_return!(abs(x.clone()));
|
||||||
let a = x.value;
|
let r = abs_real;
|
||||||
let b = x.imaginary_value;
|
let a = real;
|
||||||
|
let b = imaginary;
|
||||||
|
|
||||||
// sqrt((|z| + a) / 2) + i * (b / |b|) * sqrt((|z| - a) / 2)
|
// sqrt((|z| + a) / 2) + i * (b / |b|) * sqrt((|z| - a) / 2)
|
||||||
KalkNum::new_with_imaginary(
|
KalkValue::Number(
|
||||||
((r.clone() + a.clone()) / 2f64).sqrt(),
|
((r.clone() + a.clone()) / 2f64).sqrt(),
|
||||||
&abs.unit,
|
|
||||||
(b.clone() / b.abs()) * ((r - a) / 2f64).sqrt(),
|
(b.clone() / b.abs()) * ((r - a) / 2f64).sqrt(),
|
||||||
|
abs_unit,
|
||||||
)
|
)
|
||||||
} else if x.value < 0f64 {
|
} else if real < 0f64 {
|
||||||
KalkNum::from_imaginary(x.value.abs().sqrt())
|
KalkValue::Number(float!(0), real.abs().sqrt(), unit)
|
||||||
} else {
|
} else {
|
||||||
KalkNum::new(x.value.sqrt(), &x.unit)
|
KalkValue::Number(real.sqrt(), float!(0), unit)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn tan(x: KalkNum) -> KalkNum {
|
pub fn tan(x: KalkValue) -> KalkValue {
|
||||||
if x.has_imaginary() {
|
let (real, imaginary, unit) = as_number_or_return!(x);
|
||||||
let a = x.value * 2f64;
|
if imaginary != 0f64 {
|
||||||
let b = x.imaginary_value * 2f64;
|
let a = real * 2f64;
|
||||||
KalkNum::new_with_imaginary(
|
let b = imaginary * 2f64;
|
||||||
|
KalkValue::Number(
|
||||||
a.clone().sin() / (a.clone().cos() + b.clone().cosh()),
|
a.clone().sin() / (a.clone().cos() + b.clone().cosh()),
|
||||||
&x.unit,
|
|
||||||
b.clone().sinh() / (a.cos() + b.cosh()),
|
b.clone().sinh() / (a.cos() + b.cosh()),
|
||||||
|
unit,
|
||||||
)
|
)
|
||||||
} else {
|
} else {
|
||||||
KalkNum::new(x.value.tan(), &x.unit)
|
KalkValue::Number(real.tan(), float!(0), unit)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn tanh(x: KalkNum) -> KalkNum {
|
pub fn tanh(x: KalkValue) -> KalkValue {
|
||||||
if x.has_imaginary() {
|
let (real, imaginary, unit) = as_number_or_return!(x);
|
||||||
let a = x.value * 2f64;
|
if imaginary != 0f64 {
|
||||||
let b = x.imaginary_value * 2f64;
|
let a = real * 2f64;
|
||||||
KalkNum::new_with_imaginary(
|
let b = imaginary * 2f64;
|
||||||
|
KalkValue::Number(
|
||||||
a.clone().sinh() / (a.clone().cosh() + b.clone().cos()),
|
a.clone().sinh() / (a.clone().cosh() + b.clone().cos()),
|
||||||
&x.unit,
|
|
||||||
b.clone().sin() / (a.cosh() + b.cos()),
|
b.clone().sin() / (a.cosh() + b.cos()),
|
||||||
|
unit,
|
||||||
)
|
)
|
||||||
} else {
|
} else {
|
||||||
KalkNum::new(x.value.tanh(), &x.unit)
|
KalkValue::Number(real.tanh(), float!(0), unit)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn trunc(x: KalkNum) -> KalkNum {
|
pub fn trunc(x: KalkValue) -> KalkValue {
|
||||||
KalkNum::new_with_imaginary(x.value.trunc(), &x.unit, x.imaginary_value.trunc())
|
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(x.clone()).div_without_unit(
|
||||||
factorial(y.clone()).mul_without_unit(factorial(x.sub_without_unit(y))),
|
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)))
|
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
|
// iz = i(a + bi) = -b + ai
|
||||||
KalkNum::new_with_imaginary(-z.imaginary_value, &z.unit, z.value)
|
KalkValue::Number(-imaginary, real, unit)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use super::funcs::*;
|
use super::funcs::*;
|
||||||
use crate::prelude::KalkNum;
|
use crate::float;
|
||||||
|
use crate::prelude::KalkValue;
|
||||||
use crate::test_helpers::cmp;
|
use crate::test_helpers::cmp;
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_unary_funcs() {
|
fn test_unary_funcs() {
|
||||||
let in_out = vec![
|
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, -4f64), (5f64, 0f64)),
|
(abs, (3f64, -4f64), (5f64, 0f64)),
|
||||||
(abs, (-3f64, 0f64), (3f64, 0f64)),
|
(abs, (-3f64, 0f64), (3f64, 0f64)),
|
||||||
];
|
];
|
||||||
|
|
||||||
for (i, (func, input, expected_output)) in in_out.iter().enumerate() {
|
for (i, (func, input, expected_output)) in in_out.iter().enumerate() {
|
||||||
let actual_output = func(KalkNum::new_with_imaginary(
|
let actual_output = func(KalkValue::Number(
|
||||||
KalkNum::from(input.0).value,
|
float!(input.0),
|
||||||
"",
|
float!(input.1),
|
||||||
KalkNum::from(input.1).value,
|
String::new(),
|
||||||
));
|
));
|
||||||
|
|
||||||
println!(
|
println!(
|
||||||
@ -777,7 +837,7 @@ mod tests {
|
|||||||
fn test_binary_funcs() {
|
fn test_binary_funcs() {
|
||||||
let in_out = vec![
|
let in_out = vec![
|
||||||
(
|
(
|
||||||
gcd as fn(KalkNum, KalkNum) -> KalkNum,
|
gcd as fn(KalkValue, KalkValue) -> KalkValue,
|
||||||
((12f64, 0f64), (18f64, 0f64)),
|
((12f64, 0f64), (18f64, 0f64)),
|
||||||
(6f64, 0f64),
|
(6f64, 0f64),
|
||||||
),
|
),
|
||||||
@ -792,16 +852,8 @@ mod tests {
|
|||||||
|
|
||||||
for (i, (func, input, expected_output)) in in_out.iter().enumerate() {
|
for (i, (func, input, expected_output)) in in_out.iter().enumerate() {
|
||||||
let actual_output = func(
|
let actual_output = func(
|
||||||
KalkNum::new_with_imaginary(
|
KalkValue::Number(float!(input.0 .0), float!(input.0 .1), String::new()),
|
||||||
KalkNum::from(input.0 .0).value,
|
KalkValue::Number(float!(input.1 .0), float!(input.1 .1), String::new()),
|
||||||
"",
|
|
||||||
KalkNum::from(input.0 .1).value,
|
|
||||||
),
|
|
||||||
KalkNum::new_with_imaginary(
|
|
||||||
KalkNum::from(input.1 .0).value,
|
|
||||||
"",
|
|
||||||
KalkNum::from(input.1 .1).value,
|
|
||||||
),
|
|
||||||
);
|
);
|
||||||
|
|
||||||
println!(
|
println!(
|
||||||
@ -824,7 +876,7 @@ mod tests {
|
|||||||
// Auto-generated using kalk/scripts/generate_funcs_test_cases.py
|
// Auto-generated using kalk/scripts/generate_funcs_test_cases.py
|
||||||
let in_out = vec![
|
let in_out = vec![
|
||||||
(
|
(
|
||||||
arg as fn(KalkNum) -> KalkNum,
|
arg as fn(KalkValue) -> KalkValue,
|
||||||
(0.3f64, 0f64),
|
(0.3f64, 0f64),
|
||||||
(0.0f64, 0.0f64),
|
(0.0f64, 0.0f64),
|
||||||
),
|
),
|
||||||
@ -1047,10 +1099,10 @@ mod tests {
|
|||||||
];
|
];
|
||||||
|
|
||||||
for (i, (func, input, expected_output)) in in_out.iter().enumerate() {
|
for (i, (func, input, expected_output)) in in_out.iter().enumerate() {
|
||||||
let actual_output = func(KalkNum::new_with_imaginary(
|
let actual_output = func(KalkValue::Number(
|
||||||
KalkNum::from(input.0).value,
|
float!(input.0),
|
||||||
"",
|
float!(input.1),
|
||||||
KalkNum::from(input.1).value,
|
String::new(),
|
||||||
));
|
));
|
||||||
|
|
||||||
let expected_has_nan_or_inf = expected_output.0.is_nan()
|
let expected_has_nan_or_inf = expected_output.0.is_nan()
|
||||||
|
@ -1,29 +1,38 @@
|
|||||||
pub mod special_funcs {
|
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.
|
// Round it a bit, to prevent floating point errors.
|
||||||
KalkNum::new(
|
KalkValue::Number(
|
||||||
(super::funcs::precise_gamma(x.value + 1f64) * 10e6f64).round() / 10e6f64,
|
(super::funcs::precise_gamma(real + 1f64) * 10e6f64).round() / 10e6f64,
|
||||||
&x.unit,
|
float!(0),
|
||||||
|
unit,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) mod funcs {
|
pub(crate) mod funcs {
|
||||||
use crate::kalk_num::KalkNum;
|
use crate::kalk_value::KalkValue;
|
||||||
use crate::prelude::funcs::abs;
|
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))
|
// 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.
|
// Round it a bit, to prevent floating point errors.
|
||||||
KalkNum::new(
|
KalkValue::Number(
|
||||||
(precise_gamma(x.value) * 10e6f64).round() / 10e6f64,
|
(precise_gamma(real) * 10e6f64).round() / 10e6f64,
|
||||||
&x.unit,
|
float!(0),
|
||||||
|
unit,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -50,34 +59,49 @@ pub(crate) mod funcs {
|
|||||||
2f64.sqrt() * pi.sqrt() * t.powf(x - 0.5f64) * (-t).exp() * a
|
2f64.sqrt() * pi.sqrt() * t.powf(x - 0.5f64) * (-t).exp() * a
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn bitcmp(x: KalkNum) -> KalkNum {
|
pub fn bitcmp(x: KalkValue) -> KalkValue {
|
||||||
KalkNum::from(!(x.value.round() as i32))
|
let (real, _, _) = as_number_or_return!(x);
|
||||||
|
|
||||||
|
KalkValue::from(!(real.round() as i32))
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn bitand(x: KalkNum, y: KalkNum) -> KalkNum {
|
pub fn bitand(x: KalkValue, y: KalkValue) -> KalkValue {
|
||||||
KalkNum::from(x.value.round() as i32 & y.value.round() as i32)
|
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 {
|
pub fn bitor(x: KalkValue, y: KalkValue) -> KalkValue {
|
||||||
KalkNum::from(x.value.round() as i32 | y.value.round() as i32)
|
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 {
|
pub fn bitxor(x: KalkValue, y: KalkValue) -> KalkValue {
|
||||||
KalkNum::from(x.value.round() as i32 ^ y.value.round() as i32)
|
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 {
|
pub fn bitshift(x: KalkValue, y: KalkValue) -> KalkValue {
|
||||||
let x = x.value.round() as i32;
|
let (real, _, _) = as_number_or_return!(x);
|
||||||
let y = y.value.round() as i32;
|
let (real_rhs, _, _) = as_number_or_return!(y);
|
||||||
|
let x = real.round() as i32;
|
||||||
|
let y = real_rhs.round() as i32;
|
||||||
if y < 0 {
|
if y < 0 {
|
||||||
KalkNum::from((x >> y.abs()))
|
KalkValue::from(x >> y.abs())
|
||||||
} else {
|
} 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 {
|
||||||
if x.has_imaginary() || y.has_imaginary() {
|
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_x = abs(x);
|
||||||
let abs_y = abs(y);
|
let abs_y = abs(y);
|
||||||
crate::prelude::funcs::sqrt(
|
crate::prelude::funcs::sqrt(
|
||||||
@ -87,15 +111,21 @@ pub(crate) mod funcs {
|
|||||||
.add_without_unit(abs_y.clone().mul_without_unit(abs_y)),
|
.add_without_unit(abs_y.clone().mul_without_unit(abs_y)),
|
||||||
)
|
)
|
||||||
} else {
|
} 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 {
|
pub fn max(x: KalkValue, y: KalkValue) -> KalkValue {
|
||||||
KalkNum::new(x.value.max(y.value), &x.unit)
|
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 {
|
pub fn min(x: KalkValue, y: KalkValue) -> KalkValue {
|
||||||
KalkNum::new(x.value.min(y.value), &x.unit)
|
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 {
|
pub mod special_funcs {
|
||||||
use crate::prelude::KalkNum;
|
use crate::{as_number_or_return, float, prelude::KalkValue};
|
||||||
|
|
||||||
pub fn factorial(x: KalkNum) -> KalkNum {
|
pub fn factorial(x: KalkValue) -> KalkValue {
|
||||||
KalkNum::new((x.value + 1f64).gamma(), &x.unit)
|
let (real, _, unit) = as_number_or_return!(x);
|
||||||
|
|
||||||
|
KalkValue::Number((real + 1f64).gamma(), float!(0), unit)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) mod funcs {
|
pub(crate) mod funcs {
|
||||||
use crate::kalk_num::KalkNum;
|
use crate::kalk_value::KalkValue;
|
||||||
use crate::prelude::funcs::abs;
|
use crate::prelude::funcs::abs;
|
||||||
|
use crate::{as_number_or_return, float};
|
||||||
|
|
||||||
pub fn arg(x: KalkNum) -> KalkNum {
|
pub fn arg(x: KalkValue) -> KalkValue {
|
||||||
KalkNum::new(x.imaginary_value.atan2(&x.value), &x.unit)
|
let (real, imaginary, unit) = as_number_or_return!(x);
|
||||||
|
|
||||||
|
KalkValue::Number(imaginary.atan2(&real), float!(0), unit)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn gamma(x: KalkNum) -> KalkNum {
|
pub fn gamma(x: KalkValue) -> KalkValue {
|
||||||
KalkNum::new(x.value.gamma(), &x.unit)
|
let (real, _, unit) = as_number_or_return!(x);
|
||||||
|
|
||||||
|
KalkValue::Number(real.gamma(), float!(0), unit)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn bitcmp(x: KalkNum) -> KalkNum {
|
pub fn bitcmp(x: KalkValue) -> KalkValue {
|
||||||
KalkNum::from(!x.value.to_i32_saturating().unwrap_or(i32::MAX))
|
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 {
|
pub fn bitand(x: KalkValue, y: KalkValue) -> KalkValue {
|
||||||
KalkNum::from(
|
let (real, _, _) = as_number_or_return!(x);
|
||||||
x.value.to_i32_saturating().unwrap_or(i32::MAX)
|
let (real_rhs, _, _) = as_number_or_return!(y);
|
||||||
& y.value.to_i32_saturating().unwrap_or(i32::MAX),
|
|
||||||
|
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 {
|
pub fn bitor(x: KalkValue, y: KalkValue) -> KalkValue {
|
||||||
KalkNum::from(
|
let (real, _, _) = as_number_or_return!(x);
|
||||||
x.value.to_i32_saturating().unwrap_or(i32::MAX)
|
let (real_rhs, _, _) = as_number_or_return!(y);
|
||||||
| y.value.to_i32_saturating().unwrap_or(i32::MAX),
|
|
||||||
|
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 {
|
pub fn bitxor(x: KalkValue, y: KalkValue) -> KalkValue {
|
||||||
KalkNum::from(
|
let (real, _, _) = as_number_or_return!(x);
|
||||||
x.value.to_i32_saturating().unwrap_or(i32::MAX)
|
let (real_rhs, _, _) = as_number_or_return!(y);
|
||||||
^ y.value.to_i32_saturating().unwrap_or(i32::MAX),
|
|
||||||
|
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 {
|
pub fn bitshift(x: KalkValue, y: KalkValue) -> KalkValue {
|
||||||
let x = x.value.to_i32_saturating().unwrap_or(i32::MAX) as i32;
|
let (real, _, _) = as_number_or_return!(x);
|
||||||
let y = y.value.to_i32_saturating().unwrap_or(i32::MAX) as i32;
|
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 {
|
if y < 0 {
|
||||||
KalkNum::from(x >> y.abs())
|
KalkValue::from(x >> y.abs())
|
||||||
} else {
|
} 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() {
|
if x.has_imaginary() || y.has_imaginary() {
|
||||||
let abs_x = abs(x);
|
let abs_x = abs(x);
|
||||||
let abs_y = abs(y);
|
let abs_y = abs(y);
|
||||||
@ -64,15 +87,21 @@ pub(crate) mod funcs {
|
|||||||
.add_without_unit(abs_y.clone().mul_without_unit(abs_y)),
|
.add_without_unit(abs_y.clone().mul_without_unit(abs_y)),
|
||||||
)
|
)
|
||||||
} else {
|
} 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 {
|
pub fn max(x: KalkValue, y: KalkValue) -> KalkValue {
|
||||||
KalkNum::new(x.value.max(&y.value), &x.unit)
|
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 {
|
pub fn min(x: KalkValue, y: KalkValue) -> KalkValue {
|
||||||
KalkNum::new(x.value.min(&y.value), &x.unit)
|
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 {
|
pub fn to_radix_pretty(value: f64, radix: u8) -> String {
|
||||||
if radix == 10 {
|
if radix == 10 {
|
||||||
crate::kalk_num::format_number(value)
|
crate::kalk_value::format_number(value)
|
||||||
} else {
|
} else {
|
||||||
format!(
|
format!(
|
||||||
"{}{}",
|
"{}{}",
|
||||||
|
Loading…
Reference in New Issue
Block a user