mirror of
https://github.com/PaddiM8/kalker.git
synced 2024-12-12 17:40:52 +01:00
Implemented complex variants of prelude functions
This commit is contained in:
parent
7898f40021
commit
637c3e331b
@ -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
|
||||
}
|
||||
|
||||
|
@ -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");
|
||||
|
@ -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(),
|
||||
|
@ -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)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
@ -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
|
||||
|
Loading…
Reference in New Issue
Block a user