Implemented complex variants of prelude functions

This commit is contained in:
bakk 2021-05-22 20:47:10 +02:00
parent 7898f40021
commit 637c3e331b
9 changed files with 717 additions and 610 deletions

View File

@ -177,10 +177,7 @@ fn eval_unary_expr(
match op {
TokenKind::Minus => Ok(num.mul(context, KalkNum::from(-1f64))),
TokenKind::Percent => Ok(num.mul(context, KalkNum::from(0.01f64))),
TokenKind::Exclamation => Ok(KalkNum::new(
prelude::special_funcs::factorial(num.value),
unit,
)),
TokenKind::Exclamation => Ok(prelude::special_funcs::factorial(num)),
_ => Err(CalcError::InvalidOperator),
}
}
@ -276,11 +273,13 @@ pub(crate) fn eval_fn_call_expr(
1 => {
let x = eval_expr(context, &expressions[0], "")?;
// Turn eg. sqrt(-1) into i
if x.value < 0f64 && (identifier.full_name == "sqrt" || identifier.full_name == "") {
let abs_value = x.mul(context, KalkNum::from(-1f64));
let (sqrt, unit) = prelude::call_unary_func(
context,
&identifier.full_name,
x.value * (-1f64),
abs_value,
&context.angle_unit.clone(),
)
.unwrap();
@ -288,7 +287,7 @@ pub(crate) fn eval_fn_call_expr(
return Ok(KalkNum::new_with_imaginary(
KalkNum::default().value,
&unit,
sqrt,
sqrt.value,
));
}
@ -298,14 +297,14 @@ pub(crate) fn eval_fn_call_expr(
prelude::call_unary_func(
context,
&identifier.full_name,
x.value,
x,
&context.angle_unit.clone(),
)
}
}
2 => {
let x = eval_expr(context, &expressions[0], "")?.value;
let y = eval_expr(context, &expressions[1], "")?.value;
let x = eval_expr(context, &expressions[0], "")?;
let y = eval_expr(context, &expressions[1], "")?;
prelude::call_binary_func(
context,
&identifier.full_name,
@ -317,11 +316,8 @@ pub(crate) fn eval_fn_call_expr(
_ => None,
};
if let Some((result, func_unit)) = prelude_func {
return Ok(KalkNum::new(
result,
if unit.len() > 0 { unit } else { &func_unit },
));
if let Some((result, _)) = prelude_func {
return Ok(result);
}
// Special functions
@ -455,7 +451,6 @@ mod tests {
}
fn cmp(x: KalkNum, y: f64) -> bool {
println!("{} = {}", x.to_f64(), y);
(x.to_f64() - y).abs() < 0.0001
}

View File

@ -11,8 +11,8 @@ lazy_static! {
pub static ref INVERSE_UNARY_FUNCS: HashMap<&'static str, &'static str> = {
let mut m = HashMap::new();
m.insert("cos", "acos");
m.insert("cosec", "acosec");
m.insert("cosech", "cosech");
m.insert("csc", "acsc");
m.insert("csch", "csch");
m.insert("cosh", "acosh");
m.insert("cot", "acot");
m.insert("coth", "acoth");
@ -24,8 +24,8 @@ lazy_static! {
m.insert("tanh", "atanh");
m.insert("acos", "cos");
m.insert("acosec", "cosec");
m.insert("acosech", "cosech");
m.insert("acsc", "csc");
m.insert("acsch", "csch");
m.insert("acosh", "cosh");
m.insert("acot", "cot");
m.insert("acoth", "coth");

View File

@ -92,6 +92,10 @@ impl ScientificNotation {
}
impl KalkNum {
pub fn has_imaginary(&self) -> bool {
self.imaginary_value != 0f64
}
pub fn to_scientific_notation(
&self,
complex_number_type: ComplexNumberType,
@ -128,7 +132,7 @@ impl KalkNum {
pub fn to_string(&self) -> String {
let as_str = trim_number_string(&self.to_f64().to_string());
if self.imaginary_value != 0f64 {
if self.has_imaginary() {
let imaginary_as_str = trim_number_string(&self.imaginary_to_f64().to_string());
let sign = if self.imaginary_value < 0f64 {
"-"
@ -143,7 +147,7 @@ impl KalkNum {
}
pub fn to_string_big(&self) -> String {
if self.imaginary_value == 0f64 {
if !self.has_imaginary() {
self.value.to_string()
} else {
let sign = if self.imaginary_value < 0f64 {
@ -196,7 +200,7 @@ impl KalkNum {
};
let mut output = result_str;
if self.imaginary_value != 0f64 {
if self.has_imaginary() {
// If the real value is 0, and there is an imaginary one,
// clear the output so that the real value is not shown.
if output == "0" {
@ -264,51 +268,22 @@ impl KalkNum {
pub(crate) fn add(self, context: &mut crate::interpreter::Context, rhs: KalkNum) -> KalkNum {
let right = calculate_unit(context, &self, rhs.clone()).unwrap_or(rhs);
KalkNum::new_with_imaginary(
self.value + right.value,
&right.unit,
self.imaginary_value + right.imaginary_value,
)
self.add_without_unit(right)
}
pub(crate) fn sub(self, context: &mut crate::interpreter::Context, rhs: KalkNum) -> KalkNum {
let right = calculate_unit(context, &self, rhs.clone()).unwrap_or(rhs);
KalkNum::new_with_imaginary(
self.value - right.value,
&right.unit,
self.imaginary_value - right.imaginary_value,
)
self.sub_without_unit(right)
}
pub(crate) fn mul(self, context: &mut crate::interpreter::Context, rhs: KalkNum) -> KalkNum {
let right = calculate_unit(context, &self, rhs.clone()).unwrap_or(rhs);
// (a + bi)(c + di) = ac + adi + bci + bdi²
KalkNum::new_with_imaginary(
self.value.clone() * right.value.clone()
- self.imaginary_value.clone() * right.imaginary_value.clone(),
&right.unit,
self.value * right.imaginary_value + self.imaginary_value * right.value,
)
self.mul_without_unit(right)
}
pub(crate) fn div(self, context: &mut crate::interpreter::Context, rhs: KalkNum) -> KalkNum {
let right = calculate_unit(context, &self, rhs.clone()).unwrap_or(rhs.clone());
// Avoid unecessary calculations
if self.imaginary_value == 0f64 && right.imaginary_value == 0f64 {
KalkNum::new(self.value / right.value, &right.unit)
} else {
// Multiply both the numerator and denominator
// with the conjugate of the denominator, and divide.
let conjugate = rhs.get_conjugate();
let numerator = self.clone().mul(context, conjugate.clone());
let denominator = rhs.clone().mul(context, conjugate);
KalkNum::new_with_imaginary(
numerator.value / denominator.value.clone(),
&right.unit,
numerator.imaginary_value / denominator.value,
)
}
self.div_without_unit(right)
}
pub(crate) fn rem(self, context: &mut crate::interpreter::Context, rhs: KalkNum) -> KalkNum {
@ -316,6 +291,50 @@ impl KalkNum {
KalkNum::new(self.value % right.value, &right.unit)
}
pub(crate) fn add_without_unit(self, rhs: KalkNum) -> KalkNum {
KalkNum::new_with_imaginary(
self.value + rhs.value,
&rhs.unit,
self.imaginary_value + rhs.imaginary_value,
)
}
pub(crate) fn sub_without_unit(self, rhs: KalkNum) -> KalkNum {
KalkNum::new_with_imaginary(
self.value - rhs.value,
&rhs.unit,
self.imaginary_value - rhs.imaginary_value,
)
}
pub(crate) fn mul_without_unit(self, rhs: KalkNum) -> KalkNum {
// (a + bi)(c + di) = ac + adi + bci + bdi²
KalkNum::new_with_imaginary(
self.value.clone() * rhs.value.clone()
- self.imaginary_value.clone() * rhs.imaginary_value.clone(),
&rhs.unit,
self.value * rhs.imaginary_value + self.imaginary_value * rhs.value,
)
}
pub(crate) fn div_without_unit(self, rhs: KalkNum) -> KalkNum {
// Avoid unecessary calculations
if self.imaginary_value == 0f64 && rhs.imaginary_value == 0f64 {
KalkNum::new(self.value / rhs.value, &rhs.unit)
} else {
// Multiply both the numerator and denominator
// with the conjugate of the denominator, and divide.
let conjugate = rhs.get_conjugate();
let numerator = self.clone().mul_without_unit(conjugate.clone());
let denominator = rhs.clone().mul_without_unit(conjugate);
KalkNum::new_with_imaginary(
numerator.value / denominator.value.clone(),
&rhs.unit,
numerator.imaginary_value / denominator.value,
)
}
}
pub fn get_conjugate(&self) -> KalkNum {
KalkNum::new_with_imaginary(
self.value.clone(),

View File

@ -27,6 +27,14 @@ impl KalkNum {
}
}
pub fn from_imaginary(value: f64) -> Self {
Self {
value: 0f64,
unit: String::new(),
imaginary_value: value,
}
}
#[wasm_bindgen(js_name = getValue)]
pub fn to_f64(&self) -> f64 {
self.value
@ -86,7 +94,28 @@ impl KalkNum {
pub(crate) fn pow(self, context: &mut crate::interpreter::Context, rhs: KalkNum) -> KalkNum {
let right = calculate_unit(context, &self, rhs.clone()).unwrap_or(rhs);
KalkNum::new(self.value.powf(right.value), &right.unit)
self.pow_without_unit(right)
}
pub(crate) fn pow_without_unit(self, rhs: KalkNum) -> KalkNum {
if self.has_imaginary() || rhs.has_imaginary() {
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)
}
}
}

View File

@ -30,6 +30,14 @@ impl KalkNum {
}
}
pub fn from_imaginary(value: Float) -> Self {
Self {
value: Float::with_val(63, 0),
unit: String::new(),
imaginary_value: value,
}
}
pub fn to_f64(&self) -> f64 {
self.value.to_f64_round(rug::float::Round::Nearest)
}
@ -49,7 +57,28 @@ impl KalkNum {
pub(crate) fn pow(self, context: &mut crate::interpreter::Context, rhs: KalkNum) -> KalkNum {
let right = calculate_unit(context, &self, rhs.clone()).unwrap_or(rhs);
KalkNum::new(self.value.pow(right.value), &right.unit)
self.pow_without_unit(right)
}
pub(crate) fn pow_without_unit(self, rhs: KalkNum) -> KalkNum {
if self.has_imaginary() || rhs.has_imaginary() {
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)
}
}
}

View File

@ -1,7 +1,26 @@
use crate::kalk_num::KalkNum;
use lazy_static::lazy_static;
use std::collections::HashMap;
use FuncType::*;
#[cfg(feature = "rug")]
pub mod with_rug;
#[cfg(feature = "rug")]
pub use with_rug::funcs::*;
#[cfg(feature = "rug")]
pub use with_rug::*;
#[cfg(not(feature = "rug"))]
pub mod regular;
#[cfg(not(feature = "rug"))]
pub use regular::funcs::*;
#[cfg(not(feature = "rug"))]
pub use regular::*;
use crate::ast::Expr;
use crate::interpreter;
pub use funcs::*;
// `i` is added in the symbol_table module, since for some reason it didn't work here.
pub const INIT: &'static str = "unit deg = (rad*180)/pi";
@ -41,8 +60,8 @@ lazy_static! {
pub static ref UNARY_FUNCS: HashMap<&'static str, (UnaryFuncInfo, &'static str)> = {
let mut m = HashMap::new();
m.insert("cos", (UnaryFuncInfo(cos, Trig), ""));
m.insert("cosec", (UnaryFuncInfo(cosec, Trig), ""));
m.insert("cosech", (UnaryFuncInfo(cosech, Trig), ""));
m.insert("csc", (UnaryFuncInfo(csc, Trig), ""));
m.insert("csch", (UnaryFuncInfo(csch, Trig), ""));
m.insert("cosh", (UnaryFuncInfo(cosh, Trig), ""));
m.insert("cot", (UnaryFuncInfo(cot, Trig), ""));
m.insert("coth", (UnaryFuncInfo(coth, Trig), ""));
@ -54,8 +73,8 @@ lazy_static! {
m.insert("tanh", (UnaryFuncInfo(tanh, Trig), ""));
m.insert("acos", (UnaryFuncInfo(acos, InverseTrig), "rad"));
m.insert("acosec", (UnaryFuncInfo(acosec, InverseTrig), "rad"));
m.insert("acosech", (UnaryFuncInfo(acosech, InverseTrig), "rad"));
m.insert("acsc", (UnaryFuncInfo(acsc, InverseTrig), "rad"));
m.insert("acsch", (UnaryFuncInfo(acsch, InverseTrig), "rad"));
m.insert("acosh", (UnaryFuncInfo(acosh, InverseTrig), "rad"));
m.insert("acot", (UnaryFuncInfo(acot, InverseTrig), "rad"));
m.insert("acoth", (UnaryFuncInfo(acoth, InverseTrig), "rad"));
@ -66,6 +85,7 @@ lazy_static! {
m.insert("atan", (UnaryFuncInfo(atan, InverseTrig), "rad"));
m.insert("atanh", (UnaryFuncInfo(atanh, InverseTrig), "rad"));
m.insert("arg", (UnaryFuncInfo(arg, Other), ""));
m.insert("abs", (UnaryFuncInfo(abs, Other), ""));
m.insert("cbrt", (UnaryFuncInfo(cbrt, Other), ""));
m.insert("ceil", (UnaryFuncInfo(ceil, Other), ""));
@ -86,7 +106,7 @@ lazy_static! {
let mut m = HashMap::new();
m.insert("max", (BinaryFuncInfo(max, Other), ""));
m.insert("min", (BinaryFuncInfo(min, Other), ""));
m.insert("hyp", (BinaryFuncInfo(hyp, Other), ""));
m.insert("hypot", (BinaryFuncInfo(hypot, Other), ""));
m.insert("log", (BinaryFuncInfo(logx, Other), ""));
m.insert("root", (BinaryFuncInfo(nth_root, Other), ""));
m
@ -99,16 +119,496 @@ enum FuncType {
Other,
}
#[cfg(feature = "rug")]
pub mod with_rug;
#[cfg(feature = "rug")]
pub use with_rug::funcs::*;
#[cfg(feature = "rug")]
pub use with_rug::*;
// Unary functions
pub struct UnaryFuncInfo(fn(KalkNum) -> KalkNum, FuncType);
#[cfg(not(feature = "rug"))]
pub mod regular;
#[cfg(not(feature = "rug"))]
pub use regular::funcs::*;
#[cfg(not(feature = "rug"))]
pub use regular::*;
pub struct BinaryFuncInfo(fn(KalkNum, KalkNum) -> KalkNum, FuncType);
impl UnaryFuncInfo {
fn call(&self, context: &mut interpreter::Context, x: KalkNum, angle_unit: &str) -> KalkNum {
let func = self.0;
match self.1 {
FuncType::Trig => func(from_angle_unit(context, x, angle_unit)),
FuncType::InverseTrig => to_angle_unit(context, func(x), angle_unit),
FuncType::Other => func(x),
}
}
}
impl BinaryFuncInfo {
fn call(
&self,
context: &mut interpreter::Context,
x: KalkNum,
y: KalkNum,
angle_unit: &str,
) -> KalkNum {
let func = self.0;
match self.1 {
FuncType::Trig => func(
from_angle_unit(context, x, angle_unit),
from_angle_unit(context, y, angle_unit),
),
FuncType::InverseTrig => to_angle_unit(context, func(x, y), angle_unit),
FuncType::Other => func(x, y),
}
}
}
pub fn is_prelude_func(identifier: &str) -> bool {
identifier == "sum"
|| identifier == "Σ"
|| identifier == "integrate"
|| identifier == ""
|| UNARY_FUNCS.contains_key(identifier)
|| BINARY_FUNCS.contains_key(identifier)
}
pub fn call_unary_func(
context: &mut interpreter::Context,
name: &str,
x: KalkNum,
angle_unit: &str,
) -> Option<(KalkNum, String)> {
if let Some((func_info, func_unit)) = UNARY_FUNCS.get(name) {
Some((
func_info.call(context, x, &angle_unit),
func_unit.to_string(),
))
} else {
None
}
}
pub fn call_binary_func(
context: &mut interpreter::Context,
name: &str,
x: KalkNum,
y: KalkNum,
angle_unit: &str,
) -> Option<(KalkNum, String)> {
if let Some((func_info, func_unit)) = BINARY_FUNCS.get(name) {
Some((
func_info.call(context, x, y, angle_unit),
func_unit.to_string(),
))
} else {
None
}
}
fn to_angle_unit(context: &mut interpreter::Context, x: KalkNum, angle_unit: &str) -> KalkNum {
match angle_unit {
"rad" => x,
_ => interpreter::convert_unit(context, &Expr::Literal(x.to_f64()), "rad", angle_unit)
.unwrap(),
}
}
fn from_angle_unit(context: &mut interpreter::Context, x: KalkNum, angle_unit: &str) -> KalkNum {
match angle_unit {
"rad" => x,
_ => interpreter::convert_unit(context, &Expr::Literal(x.to_f64()), angle_unit, "rad")
.unwrap(),
}
}
pub mod funcs {
#[cfg(not(feature = "rug"))]
pub use super::regular::funcs::*;
#[cfg(feature = "rug")]
pub use super::with_rug::funcs::*;
use crate::kalk_num::KalkNum;
pub fn abs(x: KalkNum) -> KalkNum {
if x.has_imaginary() {
// |z| = sqrt(a² + b²)
let a = x.value.clone() * x.value;
let b = x.imaginary_value.clone() * x.imaginary_value;
sqrt(KalkNum::new(a + b, &x.unit))
} else {
KalkNum::new(x.value.abs(), &x.unit)
}
}
pub fn acos(x: KalkNum) -> KalkNum {
if x.has_imaginary() {
// -i * ln(i * sqrt(1 - z²) + z)
let root =
sqrt(KalkNum::from(1f64).sub_without_unit(x.clone().mul_without_unit(x.clone())));
let iroot = multiply_with_i(root.clone());
let ln = ln(iroot.add_without_unit(x));
// -iz = -i(a + bi) = b - ai
KalkNum::new_with_imaginary(ln.imaginary_value, &ln.unit, -ln.value)
} else {
KalkNum::new(x.value.acos(), &x.unit)
}
}
pub fn acosh(x: KalkNum) -> KalkNum {
if x.has_imaginary() {
let sqrt1 = sqrt(KalkNum::new_with_imaginary(
x.value.clone() + 1f64,
&x.unit,
x.imaginary_value.clone(),
));
let sqrt2 = sqrt(KalkNum::new_with_imaginary(
x.value.clone() - 1f64,
&x.unit,
x.imaginary_value.clone(),
));
ln(x.add_without_unit(sqrt1.mul_without_unit(sqrt2)))
} else {
KalkNum::new(x.value.acosh(), &x.unit)
}
}
pub fn acot(x: KalkNum) -> KalkNum {
if x.has_imaginary() {
// atan(1/z)
atan(KalkNum::from(1f64).div_without_unit(x))
} else {
KalkNum::new((1f64 / x.value).atan(), &x.unit)
}
}
pub fn acoth(x: KalkNum) -> KalkNum {
if x.has_imaginary() {
// 1 / z
let inv_x = KalkNum::from(1f64).div_without_unit(x);
let ln1 = ln(KalkNum::new_with_imaginary(
1f64 + inv_x.value.clone(),
&inv_x.unit,
inv_x.imaginary_value.clone(),
));
let ln2 = ln(KalkNum::new_with_imaginary(
1f64 - inv_x.value,
&inv_x.unit,
-inv_x.imaginary_value,
));
ln1.sub_without_unit(ln2)
.div_without_unit(KalkNum::from(2f64))
} else {
KalkNum::new((1f64 / x.value).atanh(), &x.unit)
}
}
pub fn acsc(x: KalkNum) -> KalkNum {
if x.has_imaginary() {
// asin(1/z)
asin(KalkNum::from(1f64).div_without_unit(x))
} else {
KalkNum::new((1f64 / x.value).asin(), &x.unit)
}
}
pub fn acsch(x: KalkNum) -> KalkNum {
if x.has_imaginary() {
let inv_x2 =
KalkNum::from(1f64).div_without_unit(x.clone().mul_without_unit(x.clone()));
let sqrt = sqrt(KalkNum::new_with_imaginary(
1f64 + inv_x2.value,
&inv_x2.unit,
inv_x2.imaginary_value,
));
let inv_x = KalkNum::from(1f64).div_without_unit(x.clone());
ln(sqrt.add_without_unit(inv_x))
} else {
KalkNum::new((1f64 / x.value).asinh(), &x.unit)
}
}
pub fn asec(x: KalkNum) -> KalkNum {
if x.has_imaginary() {
// acos(1/z)
acos(KalkNum::from(1f64).div_without_unit(x))
} else {
KalkNum::new((1f64 / x.value).acos(), &x.unit)
}
}
pub fn asech(x: KalkNum) -> KalkNum {
if x.has_imaginary() {
// 1/z
let inv_x = KalkNum::from(1f64).div_without_unit(x.clone());
// sqrt(1/z - 1)
let sqrt1 = sqrt(KalkNum::new_with_imaginary(
inv_x.value.clone() - 1f64,
&inv_x.unit,
inv_x.imaginary_value.clone(),
));
// sqrt(1/z + 1)
let sqrt2 = sqrt(KalkNum::new_with_imaginary(
inv_x.value.clone() + 1f64,
&inv_x.unit,
inv_x.imaginary_value.clone(),
));
// ln(1/z + sqrt(1/z - 1) * sqrt(1/z + 1))
ln(sqrt1.mul_without_unit(sqrt2).add_without_unit(inv_x))
} else {
KalkNum::new((1f64 / x.value).acosh(), &x.unit)
}
}
pub fn asin(x: KalkNum) -> KalkNum {
if x.has_imaginary() {
// i * ln(sqrt(1 - z²) - iz)
let root =
sqrt(KalkNum::from(1f64).sub_without_unit(x.clone().mul_without_unit(x.clone())));
let iz = multiply_with_i(x.clone());
let ln = ln(root.sub_without_unit(iz));
multiply_with_i(ln)
} else {
KalkNum::new(x.value.asin(), &x.unit)
}
}
pub fn asinh(x: KalkNum) -> KalkNum {
if x.has_imaginary() {
let x2 = x.clone().mul_without_unit(x.clone());
let sqrt = sqrt(KalkNum::new_with_imaginary(
x2.value + 1f64,
&x2.unit,
x2.imaginary_value,
));
ln(x.add_without_unit(sqrt))
} else {
KalkNum::new(x.value.asinh(), &x.unit)
}
}
pub fn atan(x: KalkNum) -> KalkNum {
if x.has_imaginary() {
let iz = multiply_with_i(x);
// 1 + iz
let numerator = KalkNum::new_with_imaginary(
1f64 + iz.value.clone(),
&iz.unit,
iz.imaginary_value.clone(),
);
// 1 - iz
let denominator =
KalkNum::new_with_imaginary(1f64 - iz.value, &iz.unit, -iz.imaginary_value);
let ln = ln(numerator.div_without_unit(denominator));
// -0.5iz = -0.5i(a + bi) = b/2 - ai/2
multiply_with_i(ln).div_without_unit(KalkNum::from(-2f64))
} else {
KalkNum::new(x.value.atan(), &x.unit)
}
}
pub fn atanh(x: KalkNum) -> KalkNum {
if x.has_imaginary() {
// 1/2 * log(z + 1) - 1/2 * log(-z + 1)
let log1 = ln(KalkNum::new_with_imaginary(
1f64 + x.value.clone(),
&x.unit,
x.imaginary_value.clone(),
));
let log2 = ln(KalkNum::new_with_imaginary(
1f64 - x.value,
&x.unit,
-x.imaginary_value,
));
log1.sub_without_unit(log2)
.div_without_unit(KalkNum::from(2f64))
} else {
KalkNum::new(x.value.atanh(), &x.unit)
}
}
pub fn cbrt(x: KalkNum) -> KalkNum {
KalkNum::new(x.value.cbrt(), &x.unit)
}
pub fn ceil(x: KalkNum) -> KalkNum {
KalkNum::new_with_imaginary(x.value.ceil(), &x.unit, x.imaginary_value.ceil())
}
pub fn cos(x: KalkNum) -> KalkNum {
KalkNum::new_with_imaginary(
x.value.clone().cos() * x.imaginary_value.clone().cosh(),
&x.unit,
-x.value.sin() * x.imaginary_value.sinh(),
)
}
pub fn cosh(x: KalkNum) -> KalkNum {
KalkNum::new_with_imaginary(
x.value.clone().cosh() * x.imaginary_value.clone().cos(),
&x.unit,
x.value.sinh() * x.imaginary_value.sin(),
)
}
pub fn csc(x: KalkNum) -> KalkNum {
KalkNum::from(1f64).div_without_unit(sin(x))
}
pub fn csch(x: KalkNum) -> KalkNum {
KalkNum::from(1f64).div_without_unit(sinh(x))
}
pub fn cot(x: KalkNum) -> KalkNum {
let a = x.value * 2f64;
let b = x.imaginary_value * 2f64;
KalkNum::new_with_imaginary(
-a.clone().sin() / (a.clone().cos() - b.clone().cosh()),
&x.unit,
b.clone().sinh() / (a.cos() - b.cosh()),
)
}
pub fn coth(x: KalkNum) -> KalkNum {
let a = x.value * 2f64;
let b = x.imaginary_value * 2f64;
KalkNum::new_with_imaginary(
-a.clone().sinh() / (b.clone().cos() - a.clone().cosh()),
&x.unit,
b.clone().sin() / (b.cos() - a.cosh()),
)
}
pub fn exp(x: KalkNum) -> KalkNum {
if x.has_imaginary() {
// e^a*cos(b) + ie^a*sin(b)
let exp_a = x.value.exp();
let b = x.imaginary_value;
KalkNum::new_with_imaginary(exp_a.clone() * b.clone().cos(), &x.unit, exp_a * b.sin())
} else {
KalkNum::new(x.value.exp(), &x.unit)
}
}
pub fn floor(x: KalkNum) -> KalkNum {
KalkNum::new_with_imaginary(x.value.floor(), &x.unit, x.imaginary_value.floor())
}
pub fn frac(x: KalkNum) -> KalkNum {
KalkNum::new_with_imaginary(x.value.fract(), &x.unit, x.imaginary_value.fract())
}
pub fn log(x: KalkNum) -> KalkNum {
if x.has_imaginary() {
// ln(z) / ln(10)
ln(x).div_without_unit(KalkNum::from(10f64.ln()))
} else {
KalkNum::new(x.value.log10(), &x.unit)
}
}
pub fn logx(x: KalkNum, y: KalkNum) -> KalkNum {
if x.has_imaginary() || y.has_imaginary() {
// ln(z) / ln(n)
ln(x).div_without_unit(ln(y))
} else {
KalkNum::new(x.value.log10() / y.value.log10(), &x.unit)
}
}
pub fn ln(x: KalkNum) -> KalkNum {
if x.has_imaginary() {
let r = abs(x.clone());
// ln|z| + i * arg z
ln(r).add_without_unit(multiply_with_i(arg(x)))
} else {
KalkNum::new(x.value.ln(), &x.unit)
}
}
pub fn nth_root(x: KalkNum, n: KalkNum) -> KalkNum {
x.pow_without_unit(KalkNum::from(1f64).div_without_unit(n))
}
pub fn round(x: KalkNum) -> KalkNum {
KalkNum::new_with_imaginary(x.value.round(), &x.unit, x.imaginary_value.round())
}
pub fn sec(x: KalkNum) -> KalkNum {
KalkNum::from(1f64).div_without_unit(cos(x))
}
pub fn sech(x: KalkNum) -> KalkNum {
KalkNum::from(1f64).div_without_unit(cosh(x))
}
pub fn sin(x: KalkNum) -> KalkNum {
KalkNum::new_with_imaginary(
x.value.clone().sin() * x.imaginary_value.clone().cosh(),
&x.unit,
x.value.cos() * x.imaginary_value.sinh(),
)
}
pub fn sinh(x: KalkNum) -> KalkNum {
KalkNum::new_with_imaginary(
x.value.clone().sinh() * x.imaginary_value.clone().cos(),
&x.unit,
x.value.cosh() * x.imaginary_value.sin(),
)
}
pub fn sqrt(x: KalkNum) -> KalkNum {
if x.has_imaginary() {
let abs = abs(x.clone());
let r = abs.value;
let a = x.value;
let b = x.imaginary_value;
// sqrt((|z| + a) / 2) + i * (b / |b|) * sqrt((|z| - a) / 2)
KalkNum::new_with_imaginary(
((r.clone() + a.clone()) / 2f64).sqrt(),
&abs.unit,
(b.clone() / b.abs()) * ((r - a) / 2f64).sqrt(),
)
} else {
KalkNum::new(x.value.sqrt(), &x.unit)
}
}
pub fn tan(x: KalkNum) -> KalkNum {
if x.has_imaginary() {
let a = x.value * 2f64;
let b = x.imaginary_value * 2f64;
KalkNum::new_with_imaginary(
a.clone().sin() / (a.clone().cos() + b.clone().cosh()),
&x.unit,
b.clone().sinh() / (a.cos() + b.cosh()),
)
} else {
KalkNum::new(x.value.tan(), &x.unit)
}
}
pub fn tanh(x: KalkNum) -> KalkNum {
if x.has_imaginary() {
let a = x.value * 2f64;
let b = x.imaginary_value * 2f64;
KalkNum::new_with_imaginary(
a.clone().sinh() / (a.clone().cosh() + b.clone().cos()),
&x.unit,
b.clone().sin() / (a.cosh() + b.cos()),
)
} else {
KalkNum::new(x.value.tanh(), &x.unit)
}
}
pub fn trunc(x: KalkNum) -> KalkNum {
KalkNum::new_with_imaginary(x.value.trunc(), &x.unit, x.imaginary_value.trunc())
}
fn multiply_with_i(z: KalkNum) -> KalkNum {
// iz = i(a + bi) = -b + ai
KalkNum::new_with_imaginary(-z.imaginary_value, &z.unit, z.value)
}
}

View File

@ -1,211 +1,34 @@
use super::*;
use crate::ast::Expr;
use crate::interpreter;
// Unary functions
pub struct UnaryFuncInfo(pub(super) fn(f64) -> f64, pub(super) FuncType);
pub struct BinaryFuncInfo(pub(super) fn(f64, f64) -> f64, pub(super) FuncType);
impl UnaryFuncInfo {
fn call(&self, context: &mut interpreter::Context, x: f64, angle_unit: &str) -> f64 {
let func = self.0;
match self.1 {
FuncType::Trig => func(from_angle_unit(context, x, angle_unit)),
FuncType::InverseTrig => to_angle_unit(context, func(x), angle_unit),
FuncType::Other => func(x),
}
}
}
impl BinaryFuncInfo {
fn call(&self, context: &mut interpreter::Context, x: f64, y: f64, angle_unit: &str) -> f64 {
let func = self.0;
match self.1 {
FuncType::Trig => func(
from_angle_unit(context, x, angle_unit),
from_angle_unit(context, y, angle_unit),
),
FuncType::InverseTrig => to_angle_unit(context, func(x, y), angle_unit),
FuncType::Other => func(x, y),
}
}
}
pub fn is_prelude_func(identifier: &str) -> bool {
identifier == "sum"
|| identifier == "Σ"
|| identifier == "integrate"
|| identifier == ""
|| UNARY_FUNCS.contains_key(identifier)
|| BINARY_FUNCS.contains_key(identifier)
}
pub fn call_unary_func(
context: &mut interpreter::Context,
name: &str,
x: f64,
angle_unit: &str,
) -> Option<(f64, String)> {
if let Some((func_info, func_unit)) = UNARY_FUNCS.get(name) {
Some((
func_info.call(context, x, &angle_unit),
func_unit.to_string(),
))
} else {
None
}
}
pub fn call_binary_func(
context: &mut interpreter::Context,
name: &str,
x: f64,
y: f64,
angle_unit: &str,
) -> Option<(f64, String)> {
if let Some((func_info, func_unit)) = BINARY_FUNCS.get(name) {
Some((
func_info.call(context, x, y, angle_unit),
func_unit.to_string(),
))
} else {
None
}
}
fn to_angle_unit(context: &mut interpreter::Context, x: f64, angle_unit: &str) -> f64 {
match angle_unit {
"rad" => x,
_ => {
interpreter::convert_unit(context, &Expr::Literal(x), "rad", angle_unit)
.unwrap()
.value
}
}
}
fn from_angle_unit(context: &mut interpreter::Context, x: f64, angle_unit: &str) -> f64 {
match angle_unit {
"rad" => x,
_ => {
interpreter::convert_unit(context, &Expr::Literal(x), angle_unit, "rad")
.unwrap()
.value
}
}
}
pub mod special_funcs {
pub fn factorial(x: f64) -> f64 {
super::funcs::gamma(x + 1f64)
use crate::kalk_num::KalkNum;
pub fn factorial(x: KalkNum) -> KalkNum {
// Round it a bit, to prevent floating point errors.
KalkNum::new(
(super::funcs::precise_gamma(x.value + 1f64) * 10e6f64).round() / 10e6f64,
&x.unit,
)
}
}
pub(super) mod funcs {
pub fn abs(x: f64) -> f64 {
x.abs()
pub(crate) mod funcs {
use crate::kalk_num::KalkNum;
use crate::prelude::funcs::abs;
pub fn arg(x: KalkNum) -> KalkNum {
// i(ln|x| - ln(x))
KalkNum::new(x.imaginary_value.atan2(x.value), &x.unit)
}
pub fn acos(x: f64) -> f64 {
x.acos()
}
pub fn acosh(x: f64) -> f64 {
x.acosh()
}
pub fn acot(x: f64) -> f64 {
(1f64 / x).atan()
}
pub fn acoth(x: f64) -> f64 {
(1f64 / x).atanh()
}
pub fn acosec(x: f64) -> f64 {
(1f64 / x).asin()
}
pub fn acosech(x: f64) -> f64 {
(1f64 / x).asinh()
}
pub fn asec(x: f64) -> f64 {
(1f64 / x).acos()
}
pub fn asech(x: f64) -> f64 {
(1f64 / x).acosh()
}
pub fn asin(x: f64) -> f64 {
x.asin()
}
pub fn asinh(x: f64) -> f64 {
x.asinh()
}
pub fn atan(x: f64) -> f64 {
x.atan()
}
pub fn atanh(x: f64) -> f64 {
x.atanh()
}
pub fn cbrt(x: f64) -> f64 {
x.cbrt()
}
pub fn ceil(x: f64) -> f64 {
x.ceil()
}
pub fn cos(x: f64) -> f64 {
x.cos()
}
pub fn cosh(x: f64) -> f64 {
x.cos()
}
pub fn cosec(x: f64) -> f64 {
1f64 / x.sin()
}
pub fn cosech(x: f64) -> f64 {
1f64 / x.sinh()
}
pub fn cot(x: f64) -> f64 {
x.clone().cos() / x.sin()
}
pub fn coth(x: f64) -> f64 {
x.clone().cosh() / x.sinh()
}
pub fn exp(x: f64) -> f64 {
x.exp()
}
pub fn floor(x: f64) -> f64 {
x.floor()
}
pub fn frac(x: f64) -> f64 {
x.fract()
}
pub fn gamma(x: f64) -> f64 {
pub fn gamma(x: KalkNum) -> KalkNum {
// Round it a bit, to prevent floating point errors.
(precise_gamma(x) * 10e6f64).round() / 10e6f64
KalkNum::new(
(precise_gamma(x.value) * 10e6f64).round() / 10e6f64,
&x.unit,
)
}
// Matthias Eiholzer - https://gitlab.com/matthiaseiholzer/mathru/-/tree/master
fn precise_gamma(x: f64) -> f64 {
pub(super) fn precise_gamma(x: f64) -> f64 {
let pi = 3.1415926535897932384626433832795028841971693993751058209749445923f64;
if x == 0f64 {
return f64::NAN;
@ -227,67 +50,26 @@ pub(super) mod funcs {
2f64.sqrt() * pi.sqrt() * t.powf(x - 0.5f64) * (-t).exp() * a
}
pub fn hyp(x: f64, y: f64) -> f64 {
x.hypot(y)
pub fn hypot(x: KalkNum, y: KalkNum) -> KalkNum {
if x.has_imaginary() || y.has_imaginary() {
let abs_x = abs(x);
let abs_y = abs(y);
crate::prelude::funcs::sqrt(
abs_x
.clone()
.mul_without_unit(abs_x)
.add_without_unit(abs_y.clone().mul_without_unit(abs_y)),
)
} else {
KalkNum::new(x.value.hypot(y.value), &x.unit)
}
}
pub fn log(x: f64) -> f64 {
x.log10()
pub fn max(x: KalkNum, y: KalkNum) -> KalkNum {
KalkNum::new(x.value.max(y.value), &x.unit)
}
pub fn logx(x: f64, y: f64) -> f64 {
x.log10() / y.log10()
}
pub fn ln(x: f64) -> f64 {
x.ln()
}
pub fn max(x: f64, y: f64) -> f64 {
x.max(y)
}
pub fn min(x: f64, y: f64) -> f64 {
x.min(y)
}
pub fn round(x: f64) -> f64 {
x.round()
}
pub fn sec(x: f64) -> f64 {
1f64 / x.cos()
}
pub fn sech(x: f64) -> f64 {
1f64 / x.cosh()
}
pub fn sin(x: f64) -> f64 {
x.sin()
}
pub fn sinh(x: f64) -> f64 {
x.sinh()
}
pub fn sqrt(x: f64) -> f64 {
x.sqrt()
}
pub fn nth_root(x: f64, n: f64) -> f64 {
x.powf(1f64 / n)
}
pub fn tan(x: f64) -> f64 {
x.tan()
}
pub fn tanh(x: f64) -> f64 {
x.tanh()
}
pub fn trunc(x: f64) -> f64 {
x.trunc()
pub fn min(x: KalkNum, y: KalkNum) -> KalkNum {
KalkNum::new(x.value.min(y.value), &x.unit)
}
}

View File

@ -1,291 +1,44 @@
use super::*;
use crate::ast::Expr;
use crate::interpreter;
use rug::Float;
// Unary functions
pub struct UnaryFuncInfo(pub(super) fn(Float) -> Float, pub(super) FuncType);
pub struct BinaryFuncInfo(pub(super) fn(Float, Float) -> Float, pub(super) FuncType);
impl UnaryFuncInfo {
fn call(&self, context: &mut interpreter::Context, x: Float, angle_unit: &str) -> Float {
let func = self.0;
match self.1 {
FuncType::Trig => func(from_angle_unit(context, x, angle_unit)),
FuncType::InverseTrig => to_angle_unit(context, func(x), angle_unit),
FuncType::Other => func(x),
}
}
}
impl BinaryFuncInfo {
fn call(
&self,
context: &mut interpreter::Context,
x: Float,
y: Float,
angle_unit: &str,
) -> Float {
let func = self.0;
match self.1 {
FuncType::Trig => func(
from_angle_unit(context, x, angle_unit),
from_angle_unit(context, y, angle_unit),
),
FuncType::InverseTrig => to_angle_unit(context, func(x, y), angle_unit),
FuncType::Other => func(x, y),
}
}
}
pub fn is_prelude_func(identifier: &str) -> bool {
identifier == "sum"
|| identifier == "Σ"
|| identifier == "integrate"
|| identifier == ""
|| UNARY_FUNCS.contains_key(identifier)
|| BINARY_FUNCS.contains_key(identifier)
}
pub fn call_unary_func(
context: &mut interpreter::Context,
name: &str,
x: Float,
angle_unit: &str,
) -> Option<(Float, String)> {
if let Some((func_info, func_unit)) = UNARY_FUNCS.get(name) {
Some((
func_info.call(context, x, &angle_unit),
func_unit.to_string(),
))
} else {
None
}
}
pub fn call_binary_func(
context: &mut interpreter::Context,
name: &str,
x: Float,
y: Float,
angle_unit: &str,
) -> Option<(Float, String)> {
if let Some((func_info, func_unit)) = BINARY_FUNCS.get(name) {
Some((
func_info.call(context, x, y, angle_unit),
func_unit.to_string(),
))
} else {
None
}
}
fn to_angle_unit(context: &mut interpreter::Context, x: Float, angle_unit: &str) -> Float {
match angle_unit {
"rad" => x,
_ => {
interpreter::convert_unit(
context,
&Expr::Literal(x.to_f64_round(rug::float::Round::Nearest)),
"rad",
angle_unit,
)
.unwrap()
.value
}
}
}
fn from_angle_unit(context: &mut interpreter::Context, x: Float, angle_unit: &str) -> Float {
match angle_unit {
"rad" => x,
_ => {
interpreter::convert_unit(
context,
&Expr::Literal(x.to_f64_round(rug::float::Round::Nearest)),
angle_unit,
"rad",
)
.unwrap()
.value
}
}
}
pub mod special_funcs {
use rug::Float;
use crate::prelude::KalkNum;
pub fn factorial(x: Float) -> Float {
((x + 1) as Float).gamma()
pub fn factorial(x: KalkNum) -> KalkNum {
KalkNum::new((x.value + 1f64).gamma(), &x.unit)
}
}
pub(super) mod funcs {
use rug::ops::Pow;
use rug::Float;
pub(crate) mod funcs {
use crate::kalk_num::KalkNum;
use crate::prelude::funcs::abs;
pub fn abs(x: Float) -> Float {
x.abs()
pub fn arg(x: KalkNum) -> KalkNum {
// i(ln|x| - ln(x))
KalkNum::new(x.imaginary_value.atan2(&x.value), &x.unit)
}
pub fn acos(x: Float) -> Float {
x.acos()
pub fn gamma(x: KalkNum) -> KalkNum {
KalkNum::new(x.value.gamma(), &x.unit)
}
pub fn acosh(x: Float) -> Float {
x.acosh()
pub fn hypot(x: KalkNum, y: KalkNum) -> KalkNum {
if x.has_imaginary() || y.has_imaginary() {
let abs_x = abs(x);
let abs_y = abs(y);
crate::prelude::funcs::sqrt(
abs_x
.clone()
.mul_without_unit(abs_x)
.add_without_unit(abs_y.clone().mul_without_unit(abs_y)),
)
} else {
KalkNum::new(x.value.hypot(&y.value), &x.unit)
}
}
pub fn acot(x: Float) -> Float {
(1f64 / x).atan()
pub fn max(x: KalkNum, y: KalkNum) -> KalkNum {
KalkNum::new(x.value.max(&y.value), &x.unit)
}
pub fn acoth(x: Float) -> Float {
(1f64 / x).atanh()
}
pub fn acosec(x: Float) -> Float {
(1f64 / x).asin()
}
pub fn acosech(x: Float) -> Float {
(1f64 / x).asinh()
}
pub fn asec(x: Float) -> Float {
(1f64 / x).acos()
}
pub fn asech(x: Float) -> Float {
(1f64 / x).acosh()
}
pub fn asin(x: Float) -> Float {
x.asin()
}
pub fn asinh(x: Float) -> Float {
x.asinh()
}
pub fn atan(x: Float) -> Float {
x.atan()
}
pub fn atanh(x: Float) -> Float {
x.atanh()
}
pub fn cbrt(x: Float) -> Float {
x.cbrt()
}
pub fn ceil(x: Float) -> Float {
x.ceil()
}
pub fn cos(x: Float) -> Float {
x.cos()
}
pub fn cosh(x: Float) -> Float {
x.cos()
}
pub fn cosec(x: Float) -> Float {
1f64 / x.sin()
}
pub fn cosech(x: Float) -> Float {
1f64 / x.sinh()
}
pub fn cot(x: Float) -> Float {
x.clone().cos() / x.sin()
}
pub fn coth(x: Float) -> Float {
x.clone().cosh() / x.sinh()
}
pub fn exp(x: Float) -> Float {
x.exp()
}
pub fn floor(x: Float) -> Float {
x.floor()
}
pub fn frac(x: Float) -> Float {
x.fract()
}
pub fn gamma(x: Float) -> Float {
x.gamma()
}
pub fn hyp(x: Float, y: Float) -> Float {
x.hypot(&y)
}
pub fn log(x: Float) -> Float {
x.log10()
}
pub fn logx(x: Float, y: Float) -> Float {
x.log10() / y.log10()
}
pub fn ln(x: Float) -> Float {
x.ln()
}
pub fn max(x: Float, y: Float) -> Float {
x.max(&y)
}
pub fn min(x: Float, y: Float) -> Float {
x.min(&y)
}
pub fn round(x: Float) -> Float {
x.round()
}
pub fn sec(x: Float) -> Float {
1f64 / x.cos()
}
pub fn sech(x: Float) -> Float {
1f64 / x.cosh()
}
pub fn sin(x: Float) -> Float {
x.sin()
}
pub fn sinh(x: Float) -> Float {
x.sinh()
}
pub fn sqrt(x: Float) -> Float {
x.sqrt()
}
pub fn nth_root(x: Float, n: Float) -> Float {
x.pow(Float::with_val(1, 1) / n)
}
pub fn tan(x: Float) -> Float {
x.tan()
}
pub fn tanh(x: Float) -> Float {
x.tanh()
}
pub fn trunc(x: Float) -> Float {
x.trunc()
pub fn min(x: KalkNum, y: KalkNum) -> KalkNum {
KalkNum::new(x.value.min(&y.value), &x.unit)
}
}

View File

@ -40,14 +40,14 @@ They are used like this: name(arg1, arg2, etc.)
Example: f(3) + 3 A(2, 3)
Predefined functions
sin, cos, tan, cot, cosec, sec
sinh, cosh, tanh, coth, cosech, sech
asin, acos, atan, acot, acosec, asec
asinh, acosh, atanh, acoth, acosech, asech
sin, cos, tan, cot, csc, sec
sinh, cosh, tanh, coth, csch, sech
asin, acos, atan, acot, acsc, asec
asinh, acosh, atanh, acoth, acsch, asech
abs, ceil or ⌈⌉, floor or ⌊⌋, frac, round, trunc
sqrt or √, cbrt, exp, log, ln
gamma or Γ
asinh, acosh, atanh, acoth, acosech, asech
asinh, acosh, atanh, acoth, acsch, asech
min, max, hyp
log Eg. log(1000, 10) is the same as log10(1000)
root Eg. root(16, 3) is the same as 3√16