#![allow(clippy::redundant_clone)] use crate::errors::KalkError;
use crate::kalk_value::KalkValue;
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::*;
pub const INIT: &str = "unit deg = (rad*180)/pi";
lazy_static! {
pub static ref CONSTANTS: HashMap<&'static str, f64> = {
let mut m = HashMap::new();
m.insert("pi", std::f64::consts::PI);
m.insert("e", std::f64::consts::E);
m.insert("tau", std::f64::consts::TAU);
m.insert("phi", 1.618_033_988_749_895);
m
};
pub static ref UNARY_FUNCS: HashMap<&'static str, (UnaryFuncInfo, &'static str)> = {
let mut m = HashMap::new();
m.insert("cos", (UnaryFuncInfo(cos, 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), ""));
m.insert("sec", (UnaryFuncInfo(sec, Trig), ""));
m.insert("sech", (UnaryFuncInfo(sech, Trig), ""));
m.insert("sin", (UnaryFuncInfo(sin, Trig), ""));
m.insert("sinh", (UnaryFuncInfo(sinh, Trig), ""));
m.insert("tan", (UnaryFuncInfo(tan, Trig), ""));
m.insert("tanh", (UnaryFuncInfo(tanh, Trig), ""));
m.insert("acos", (UnaryFuncInfo(acos, 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"));
m.insert("asec", (UnaryFuncInfo(asec, InverseTrig), "rad"));
m.insert("asech", (UnaryFuncInfo(asech, InverseTrig), "rad"));
m.insert("asin", (UnaryFuncInfo(asin, InverseTrig), "rad"));
m.insert("asinh", (UnaryFuncInfo(asinh, InverseTrig), "rad"));
m.insert("atan", (UnaryFuncInfo(atan, InverseTrig), "rad"));
m.insert("atanh", (UnaryFuncInfo(atanh, InverseTrig), "rad"));
m.insert("abs", (UnaryFuncInfo(abs, Other), ""));
m.insert("arg", (UnaryFuncInfo(arg, Other), ""));
m.insert("bitcmp", (UnaryFuncInfo(bitcmp, Other), ""));
m.insert("cbrt", (UnaryFuncInfo(cbrt, Other), ""));
m.insert("ceil", (UnaryFuncInfo(ceil, Other), ""));
m.insert("exp", (UnaryFuncInfo(exp, Other), ""));
m.insert("floor", (UnaryFuncInfo(floor, Other), ""));
m.insert("frac", (UnaryFuncInfo(frac, Other), ""));
m.insert("gamma", (UnaryFuncInfo(gamma, Other), ""));
m.insert("Γ", (UnaryFuncInfo(gamma, Other), ""));
m.insert("iverson", (UnaryFuncInfo(iverson, Other), ""));
m.insert("Im", (UnaryFuncInfo(im, Other), ""));
m.insert("ln", (UnaryFuncInfo(ln, Other), ""));
m.insert("length", (UnaryFuncInfo(length, Other), ""));
m.insert("log", (UnaryFuncInfo(log, Other), ""));
m.insert("Re", (UnaryFuncInfo(re, Other), ""));
m.insert("round", (UnaryFuncInfo(round, Other), ""));
m.insert("sgn", (UnaryFuncInfo(sgn, Other), ""));
m.insert("sort", (UnaryFuncInfo(sort, Other), ""));
m.insert("sqrt", (UnaryFuncInfo(sqrt, Other), ""));
m.insert("√", (UnaryFuncInfo(sqrt, Other), ""));
m.insert("transpose", (UnaryFuncInfo(transpose, Other), ""));
m.insert("trunc", (UnaryFuncInfo(trunc, Other), ""));
m
};
pub static ref BINARY_FUNCS: HashMap<&'static str, (BinaryFuncInfo, &'static str)> = {
let mut m = HashMap::new();
m.insert("bitand", (BinaryFuncInfo(bitand, Other), ""));
m.insert("bitor", (BinaryFuncInfo(bitor, Other), ""));
m.insert("bitxor", (BinaryFuncInfo(bitxor, Other), ""));
m.insert("bitshift", (BinaryFuncInfo(bitshift, Other), ""));
m.insert("hypot", (BinaryFuncInfo(hypot, Other), ""));
m.insert("gcd", (BinaryFuncInfo(gcd, Other), ""));
m.insert("lcm", (BinaryFuncInfo(lcm, Other), ""));
m.insert("log", (BinaryFuncInfo(logx, Other), ""));
m.insert("root", (BinaryFuncInfo(nth_root, Other), ""));
m.insert("nCr", (BinaryFuncInfo(ncr, Other), ""));
m.insert("comb", (BinaryFuncInfo(ncr, Other), ""));
m.insert("nPr", (BinaryFuncInfo(npr, Other), ""));
m.insert("perm", (BinaryFuncInfo(npr, Other), ""));
m
};
pub static ref VECTOR_FUNCS: HashMap<&'static str, VectorFuncInfo> = {
let mut m = HashMap::new();
m.insert("average", VectorFuncInfo(average, Other));
m.insert("diag", VectorFuncInfo(diag, Other));
m.insert("matrix", VectorFuncInfo(matrix, Other));
m.insert("max", VectorFuncInfo(max, Other));
m.insert("min", VectorFuncInfo(min, Other));
m.insert("perms", VectorFuncInfo(perms, Other));
m.insert("permutations", VectorFuncInfo(perms, Other));
m.insert("prod", VectorFuncInfo(prod, Other));
m.insert("sum", VectorFuncInfo(sum, Other));
m
};
}
enum FuncType {
Trig,
InverseTrig,
Other,
}
pub struct UnaryFuncInfo(fn(KalkValue) -> Result<KalkValue, KalkError>, FuncType);
pub struct BinaryFuncInfo(
fn(KalkValue, KalkValue) -> Result<KalkValue, KalkError>,
FuncType,
);
pub struct VectorFuncInfo(fn(KalkValue) -> Result<KalkValue, KalkError>, FuncType);
impl UnaryFuncInfo {
fn call(
&self,
context: &mut interpreter::Context,
x: KalkValue,
angle_unit: &str,
) -> Result<KalkValue, KalkError> {
let func = self.0;
match self.1 {
FuncType::Trig => func(from_angle_unit(context, x, angle_unit)),
FuncType::InverseTrig => Ok(to_angle_unit(context, func(x)?, angle_unit)),
FuncType::Other => func(x),
}
}
}
impl BinaryFuncInfo {
fn call(
&self,
context: &mut interpreter::Context,
x: KalkValue,
y: KalkValue,
angle_unit: &str,
) -> Result<KalkValue, KalkError> {
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 => Ok(to_angle_unit(context, func(x, y)?, angle_unit)),
FuncType::Other => func(x, y),
}
}
}
impl VectorFuncInfo {
fn call(&self, x: KalkValue) -> Result<KalkValue, KalkError> {
let func = self.0;
func(x)
}
}
pub fn is_prelude_func(identifier: &str) -> bool {
identifier == "sum"
|| identifier == "Σ"
|| identifier == "∑"
|| identifier == "prod"
|| identifier == "∏"
|| identifier == "integrate"
|| identifier == "integral"
|| identifier == "∫"
|| UNARY_FUNCS.contains_key(identifier)
|| BINARY_FUNCS.contains_key(identifier)
|| VECTOR_FUNCS.contains_key(identifier)
}
pub fn is_vector_func(identifier: &str) -> bool {
VECTOR_FUNCS.contains_key(identifier)
}
pub fn is_constant(identifier: &str) -> bool {
CONSTANTS.contains_key(identifier)
}
pub fn call_unary_func(
context: &mut interpreter::Context,
name: &str,
x: KalkValue,
angle_unit: &str,
) -> Option<(Result<KalkValue, KalkError>, 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: KalkValue,
y: KalkValue,
angle_unit: &str,
) -> Option<(Result<KalkValue, KalkError>, 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
}
}
pub fn call_vector_func(name: &str, x: KalkValue) -> Option<Result<KalkValue, KalkError>> {
VECTOR_FUNCS.get(name).map(|func_info| func_info.call(x))
}
fn to_angle_unit(context: &mut interpreter::Context, x: KalkValue, angle_unit: &str) -> KalkValue {
match angle_unit {
"rad" => x,
_ => interpreter::convert_unit(
context,
&Expr::Literal(x.to_f64()),
Some(&String::from("rad")),
Some(&angle_unit.to_string()),
)
.unwrap(),
}
}
fn from_angle_unit(
context: &mut interpreter::Context,
x: KalkValue,
angle_unit: &str,
) -> KalkValue {
match angle_unit {
"rad" => x,
_ => interpreter::convert_unit(
context,
&Expr::Literal(x.to_f64()),
Some(&angle_unit.to_string()),
Some(&String::from("rad")),
)
.unwrap(),
}
}
pub mod funcs {
use std::cmp::Ordering;
#[cfg(not(feature = "rug"))]
pub use super::regular::funcs::*;
use super::special_funcs::factorial;
#[cfg(feature = "rug")]
pub use super::with_rug::funcs::*;
use crate::{
as_number_or_return, as_vector_or_return, errors::KalkError, float, kalk_value::KalkValue,
};
pub fn abs(x: KalkValue) -> Result<KalkValue, KalkError> {
let has_imaginary = x.has_imaginary();
let (real, imaginary, unit) = as_number_or_return!(x);
if has_imaginary {
let a = real.clone() * real;
let b = imaginary.clone() * imaginary;
sqrt(KalkValue::Number(a + b, float!(0), unit))
} else {
Ok(KalkValue::Number(real.abs(), float!(0), unit))
}
}
pub fn acos(x: KalkValue) -> Result<KalkValue, KalkError> {
let (real, _, unit) = as_number_or_return!(x.clone());
if x.has_imaginary() || !(-1f64..=1f64).contains(&real) {
let root =
sqrt(KalkValue::from(1f64).sub_without_unit(&x.clone().mul_without_unit(&x)?)?)?;
let iroot = multiply_with_i(root)?;
let ln = ln(iroot.add_without_unit(&x)?)?;
let (ln_real, ln_imaginary, ln_unit) = as_number_or_return!(ln);
Ok(KalkValue::Number(ln_imaginary, -ln_real, ln_unit))
} else {
Ok(KalkValue::Number(real.acos(), float!(0), unit))
}
}
pub fn acosh(x: KalkValue) -> Result<KalkValue, KalkError> {
let (real, imaginary, unit) = as_number_or_return!(x.clone());
if x.has_imaginary() || real < 1f64 {
let sqrt1 = sqrt(KalkValue::Number(
real.clone() + 1f64,
imaginary.clone(),
unit.clone(),
))?;
let sqrt2 = sqrt(KalkValue::Number(real - 1f64, imaginary, unit))?;
ln(x.add_without_unit(&sqrt1.mul_without_unit(&sqrt2)?)?)
} else {
Ok(KalkValue::Number(real.acosh(), float!(0), unit))
}
}
pub fn acot(x: KalkValue) -> Result<KalkValue, KalkError> {
let (real, _, unit) = as_number_or_return!(x.clone());
if x.has_imaginary() {
atan(KalkValue::from(1f64).div_without_unit(&x)?)
} else {
Ok(KalkValue::Number((1f64 / real).atan(), float!(0), unit))
}
}
pub fn acoth(x: KalkValue) -> Result<KalkValue, KalkError> {
let (real, _, unit) = as_number_or_return!(x.clone());
if x.has_imaginary() || real <= 1f64 || real >= -1f64 {
let (inv_real, inv_imaginary, inv_unit) =
as_number_or_return!(KalkValue::from(1f64).div_without_unit(&x)?);
let ln1 = ln(KalkValue::Number(
1f64 + inv_real.clone(),
inv_imaginary.clone(),
inv_unit.clone(),
))?;
let ln2 = ln(KalkValue::Number(1f64 - inv_real, -inv_imaginary, inv_unit))?;
ln1.sub_without_unit(&ln2)?
.div_without_unit(&KalkValue::from(2f64))
} else {
Ok(KalkValue::Number((1f64 / real).atanh(), float!(0), unit))
}
}
pub fn acsc(x: KalkValue) -> Result<KalkValue, KalkError> {
let (real, _, unit) = as_number_or_return!(x.clone());
if x.has_imaginary() || real < 1f64 || real > -1f64 {
asin(KalkValue::from(1f64).div_without_unit(&x)?)
} else {
Ok(KalkValue::Number((1f64 / real).asin(), float!(0), unit))
}
}
pub fn acsch(x: KalkValue) -> Result<KalkValue, KalkError> {
let (real, _, unit) = as_number_or_return!(x.clone());
if x.has_imaginary() || real == 0f64 {
let (inv_x2_real, inv_x2_imaginary, inv_x2_unit) = as_number_or_return!(
KalkValue::from(1f64).div_without_unit(&x.clone().mul_without_unit(&x)?)?
);
let sqrt = sqrt(KalkValue::Number(
1f64 + inv_x2_real,
inv_x2_imaginary,
inv_x2_unit,
))?;
let inv_x = KalkValue::from(1f64).div_without_unit(&x)?;
ln(sqrt.add_without_unit(&inv_x)?)
} else {
Ok(KalkValue::Number((1f64 / real).asinh(), float!(0), unit))
}
}
pub fn asec(x: KalkValue) -> Result<KalkValue, KalkError> {
let (real, _, unit) = as_number_or_return!(x.clone());
if x.has_imaginary() || real < 1f64 || real > -1f64 {
acos(KalkValue::from(1f64).div_without_unit(&x)?)
} else {
Ok(KalkValue::Number((1f64 / real).acos(), float!(0), unit))
}
}
pub fn asech(x: KalkValue) -> Result<KalkValue, KalkError> {
let (real, _, unit) = as_number_or_return!(x.clone());
if x.has_imaginary() || real <= 0f64 || real > 1f64 {
let inv = KalkValue::from(1f64).div_without_unit(&x)?;
let (inv_real, inv_imaginary, inv_unit) = as_number_or_return!(inv.clone());
let sqrt1 = sqrt(KalkValue::Number(
inv_real.clone() - 1f64,
inv_imaginary.clone(),
inv_unit.clone(),
))?;
let sqrt2 = sqrt(KalkValue::Number(inv_real + 1f64, inv_imaginary, inv_unit))?;
ln(sqrt1.mul_without_unit(&sqrt2)?.add_without_unit(&inv)?)
} else {
Ok(KalkValue::Number((1f64 / real).acosh(), float!(0), unit))
}
}
pub fn asin(x: KalkValue) -> Result<KalkValue, KalkError> {
let (real, _, unit) = as_number_or_return!(x.clone());
if x.has_imaginary() || !(-1f64..=1f64).contains(&real) {
let root =
sqrt(KalkValue::from(1f64).sub_without_unit(&x.clone().mul_without_unit(&x)?)?)?;
let iz = multiply_with_i(x)?;
let ln = ln(root.sub_without_unit(&iz)?)?;
multiply_with_i(ln)
} else {
Ok(KalkValue::Number(real.asin(), float!(0), unit))
}
}
pub fn asinh(x: KalkValue) -> Result<KalkValue, KalkError> {
let (real, _, unit) = as_number_or_return!(x.clone());
if x.has_imaginary() {
let (x2_real, x2_imaginary, x2_unit) =
as_number_or_return!(x.clone().mul_without_unit(&x)?);
let sqrt = sqrt(KalkValue::Number(x2_real + 1f64, x2_imaginary, x2_unit))?;
ln(x.add_without_unit(&sqrt)?)
} else {
Ok(KalkValue::Number(real.asinh(), float!(0), unit))
}
}
pub fn atan(x: KalkValue) -> Result<KalkValue, KalkError> {
let (real, _, unit) = as_number_or_return!(x.clone());
if x.has_imaginary() {
let mul_i = multiply_with_i(x)?;
let (iz_real, iz_imaginary, iz_unit) = as_number_or_return!(mul_i);
let neg = KalkValue::Number(
1f64 - iz_real.clone(),
-iz_imaginary.clone(),
iz_unit.clone(),
);
let pos = KalkValue::Number(1f64 + iz_real, iz_imaginary, iz_unit);
let ln = ln(neg)?.sub_without_unit(&ln(pos)?)?;
multiply_with_i(ln)?.div_without_unit(&KalkValue::from(2f64))
} else {
Ok(KalkValue::Number(real.atan(), float!(0), unit))
}
}
pub fn atanh(x: KalkValue) -> Result<KalkValue, KalkError> {
let has_imaginary = x.has_imaginary();
let (real, imaginary, unit) = as_number_or_return!(x);
if has_imaginary || real >= 1f64 || real <= -1f64 {
let log1 = ln(KalkValue::Number(
1f64 + real.clone(),
imaginary.clone(),
unit.clone(),
))?;
let log2 = ln(KalkValue::Number(1f64 - real, -imaginary, unit))?;
log1.sub_without_unit(&log2)?
.div_without_unit(&KalkValue::from(2f64))
} else {
Ok(KalkValue::Number(real.atanh(), float!(0), unit))
}
}
pub fn average(x: KalkValue) -> Result<KalkValue, KalkError> {
let values = as_vector_or_return!(x);
let count = values.len() as i64;
let mut sum_real = float!(0);
let mut sum_imaginary = float!(0);
for value in values {
let (real, imaginary, _) = as_number_or_return!(value);
sum_real += real;
sum_imaginary += imaginary;
}
KalkValue::Number(sum_real, sum_imaginary, None).div_without_unit(&KalkValue::from(count))
}
pub fn cbrt(x: KalkValue) -> Result<KalkValue, KalkError> {
let (real, _, unit) = as_number_or_return!(x);
Ok(KalkValue::Number(real.cbrt(), float!(0), unit))
}
pub fn ceil(x: KalkValue) -> Result<KalkValue, KalkError> {
let (real, imaginary, unit) = as_number_or_return!(x);
Ok(KalkValue::Number(real.ceil(), imaginary.ceil(), unit))
}
pub fn cos(x: KalkValue) -> Result<KalkValue, KalkError> {
let (real, imaginary, unit) = as_number_or_return!(x);
Ok(KalkValue::Number(
real.clone().cos() * imaginary.clone().cosh(),
-real.sin() * imaginary.sinh(),
unit,
))
}
pub fn cosh(x: KalkValue) -> Result<KalkValue, KalkError> {
let (real, imaginary, unit) = as_number_or_return!(x);
Ok(KalkValue::Number(
real.clone().cosh() * imaginary.clone().cos(),
real.sinh() * imaginary.sin(),
unit,
))
}
pub fn csc(x: KalkValue) -> Result<KalkValue, KalkError> {
KalkValue::from(1f64).div_without_unit(&sin(x)?)
}
pub fn csch(x: KalkValue) -> Result<KalkValue, KalkError> {
KalkValue::from(1f64).div_without_unit(&sinh(x)?)
}
pub fn cot(x: KalkValue) -> Result<KalkValue, KalkError> {
let (real, imaginary, unit) = as_number_or_return!(x);
let a = real * 2f64;
let b = imaginary * 2f64;
Ok(KalkValue::Number(
-a.clone().sin() / (a.clone().cos() - b.clone().cosh()),
b.clone().sinh() / (a.cos() - b.cosh()),
unit,
))
}
pub fn coth(x: KalkValue) -> Result<KalkValue, KalkError> {
let (real, imaginary, unit) = as_number_or_return!(x);
let a = real * 2f64;
let b = imaginary * 2f64;
Ok(KalkValue::Number(
-a.clone().sinh() / (b.clone().cos() - a.clone().cosh()),
b.clone().sin() / (b.cos() - a.cosh()),
unit,
))
}
pub fn diag(x: KalkValue) -> Result<KalkValue, KalkError> {
if let KalkValue::Vector(values) = x {
let mut result = vec![vec![KalkValue::from(0f64); values.len()]; values.len()];
for (i, value) in values.iter().enumerate() {
result[i][i] = value.clone();
}
Ok(KalkValue::Matrix(result))
} else {
Err(KalkError::UnexpectedType(
x.get_type_name(),
vec![String::from("vector")],
))
}
}
pub fn exp(x: KalkValue) -> Result<KalkValue, KalkError> {
let has_imaginary = x.has_imaginary();
let (real, imaginary, unit) = as_number_or_return!(x);
if has_imaginary {
let exp_a = real.exp();
let b = imaginary;
Ok(KalkValue::Number(
exp_a.clone() * b.clone().cos(),
exp_a * b.sin(),
unit,
))
} else {
Ok(KalkValue::Number(real.exp(), float!(0), unit))
}
}
pub fn floor(x: KalkValue) -> Result<KalkValue, KalkError> {
let (real, imaginary, unit) = as_number_or_return!(x);
Ok(KalkValue::Number(real.floor(), imaginary.floor(), unit))
}
pub fn frac(x: KalkValue) -> Result<KalkValue, KalkError> {
let (real, imaginary, unit) = as_number_or_return!(x);
Ok(KalkValue::Number(real.fract(), imaginary.fract(), unit))
}
pub fn gcd(x: KalkValue, y: KalkValue) -> Result<KalkValue, KalkError> {
let (real, imaginary, unit) = as_number_or_return!(x.clone());
let (real_rhs, imaginary_rhs, _) = as_number_or_return!(y.clone());
fn norm(x: KalkValue) -> Result<KalkValue, KalkError> {
let (real, imaginary, unit) = as_number_or_return!(x);
Ok(KalkValue::Number(
(real.clone() * real) + (imaginary.clone() * imaginary),
float!(0),
unit,
))
}
if x.has_imaginary() || y.has_imaginary() {
if real.fract() != 0f64
|| real_rhs.fract() != 0f64
|| imaginary.fract() != 0f64
|| imaginary_rhs.fract() != 0f64
{
return Err(KalkError::Expected(String::from("a gaussian integer")));
}
let a;
let b;
if norm(x.clone())?.values().0 < norm(y.clone())?.values().0 {
a = y;
b = x;
} else {
a = x;
b = y;
}
let (b_real, b_imaginary, b_unit) = as_number_or_return!(b.clone());
let (c_real, c_imaginary, c_unit) =
as_number_or_return!(a.clone().div_without_unit(&b)?);
if c_imaginary.clone().fract() == 0f64 {
Ok(KalkValue::Number(b_real.abs(), b_imaginary, b_unit))
} else {
let rounded_c = KalkValue::Number(c_real.round(), c_imaginary.round(), c_unit);
gcd(
a.sub_without_unit(&b.clone().mul_without_unit(&rounded_c)?)?,
b,
)
}
} else {
if real < 0f64 || real_rhs < 0f64 {
return gcd(
KalkValue::Number(real.abs(), float!(0), unit.clone()),
KalkValue::Number(real_rhs.abs(), float!(0), unit),
);
}
let mut x_a = real;
let mut y_a = real_rhs;
while !y_a.eq(&0f64) {
let t = y_a.clone();
y_a = x_a % y_a;
x_a = t;
}
Ok(KalkValue::Number(x_a, float!(0), unit))
}
}
pub fn im(x: KalkValue) -> Result<KalkValue, KalkError> {
let (_, imaginary, unit) = as_number_or_return!(x);
Ok(KalkValue::Number(imaginary, float!(0), unit))
}
pub fn iverson(x: KalkValue) -> Result<KalkValue, KalkError> {
Ok(KalkValue::from(
if let KalkValue::Boolean(boolean_value) = x {
i32::from(boolean_value)
} else {
1
},
))
}
pub fn lcm(x: KalkValue, y: KalkValue) -> Result<KalkValue, KalkError> {
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, y)?;
let absx = KalkValue::Number(real.abs(), imaginary, unit);
let absy = KalkValue::Number(real_rhs.abs(), imaginary_rhs, unit_rhs);
absx.div_without_unit(&gcd)?.mul_without_unit(&absy)
}
pub fn log(x: KalkValue) -> Result<KalkValue, KalkError> {
let (real, _, unit) = as_number_or_return!(x.clone());
if x.has_imaginary() || real < 0f64 {
ln(x)?.div_without_unit(&KalkValue::from(10f64.ln()))
} else {
Ok(KalkValue::Number(real.log10(), float!(0), unit))
}
}
pub fn logx(x: KalkValue, y: KalkValue) -> Result<KalkValue, KalkError> {
let (real, _, unit) = as_number_or_return!(x.clone());
let (real_rhs, _, _) = as_number_or_return!(y.clone());
if x.has_imaginary() || y.has_imaginary() || real < 0f64 || real_rhs < 0f64 {
ln(x)?.div_without_unit(&ln(y)?)
} else {
Ok(KalkValue::Number(
real.log10() / real_rhs.log10(),
float!(0),
unit,
))
}
}
pub fn ln(x: KalkValue) -> Result<KalkValue, KalkError> {
let (real, _, unit) = as_number_or_return!(x.clone());
if x.has_imaginary() || real < 0f64 {
let r = abs(x.clone())?;
ln(r)?.add_without_unit(&multiply_with_i(arg(x)?)?)
} else {
Ok(KalkValue::Number(real.ln(), float!(0), unit))
}
}
pub fn length(x: KalkValue) -> Result<KalkValue, KalkError> {
Ok(match x {
KalkValue::Vector(values) => KalkValue::from(values.len() as f64),
KalkValue::Matrix(rows) => KalkValue::from(rows.len() as f64),
_ => KalkValue::from(0f64),
})
}
pub fn matrix(x: KalkValue) -> Result<KalkValue, KalkError> {
let rows = as_vector_or_return!(x);
let column_width =
if let KalkValue::Vector(first_vec) = rows.first().unwrap_or(&KalkValue::nan()) {
first_vec.len()
} else {
0
};
let mut columns = Vec::new();
for value in rows {
let column = as_vector_or_return!(value);
if column.len() != column_width {
return Err(KalkError::InconsistentColumnWidths);
}
columns.push(column);
}
Ok(KalkValue::Matrix(columns))
}
pub fn max(x: KalkValue) -> Result<KalkValue, KalkError> {
let values = as_vector_or_return!(x);
let mut max = &values[0];
for value in &values {
if let KalkValue::Boolean(greater) = value.greater_than_without_unit(max)? {
if greater {
max = value;
}
}
}
Ok(max.clone())
}
pub fn min(x: KalkValue) -> Result<KalkValue, KalkError> {
let values = as_vector_or_return!(x);
let mut min = &values[0];
for value in &values {
if let KalkValue::Boolean(less) = value.less_than_without_unit(min)? {
if less {
min = value;
}
}
}
Ok(min.clone())
}
pub fn nth_root(x: KalkValue, n: KalkValue) -> Result<KalkValue, KalkError> {
x.pow_without_unit(&KalkValue::from(1f64).div_without_unit(&n)?)
}
pub fn perms(x: KalkValue) -> Result<KalkValue, KalkError> {
if let KalkValue::Vector(values) = sort(x)? {
let mut result: Vec<Vec<KalkValue>> = vec![values];
loop {
let prev_values = result.last().unwrap();
let mut i = prev_values.len() - 1;
for _ in (1..prev_values.len()).rev() {
if let (KalkValue::Number(real, _, _), KalkValue::Number(real_2, _, _)) =
(&prev_values[i - 1], &prev_values[i])
{
if real >= real_2 {
i -= 1;
} else {
break;
}
if i == 0 {
return Ok(KalkValue::Matrix(result));
}
}
}
if i == 0 {
return Ok(KalkValue::Matrix(result));
}
let pivot = if let KalkValue::Number(real, _, _) = &prev_values[i - 1] {
real
} else {
return Err(KalkError::UnexpectedType(
prev_values[i - 1].get_type_name(),
vec![String::from("number")],
));
};
let mut j = prev_values.len() - 1;
while let KalkValue::Number(real, _, _) = &prev_values[j] {
if real <= pivot {
j -= 1;
} else {
break;
}
}
let mut new_values = prev_values.clone();
new_values.swap(i - 1, j);
i += 1;
j = new_values.len();
while i < j {
new_values.swap(i - 1, j - 1);
i += 1;
j -= 1;
}
result.push(new_values);
}
} else {
Err(KalkError::UnexpectedType(
String::from("unknown"),
vec![String::from("vector")],
))
}
}
pub fn prod(x: KalkValue) -> Result<KalkValue, KalkError> {
let values = as_vector_or_return!(x);
let mut prod = KalkValue::from(1f64);
for value in values {
prod = prod.mul_without_unit(&value)?;
}
Ok(prod)
}
pub fn re(x: KalkValue) -> Result<KalkValue, KalkError> {
let (real, _, unit) = as_number_or_return!(x);
Ok(KalkValue::Number(real, float!(0), unit))
}
pub fn round(x: KalkValue) -> Result<KalkValue, KalkError> {
let (real, imaginary, unit) = as_number_or_return!(x);
Ok(KalkValue::Number(real.round(), imaginary.round(), unit))
}
pub fn sec(x: KalkValue) -> Result<KalkValue, KalkError> {
KalkValue::from(1f64).div_without_unit(&cos(x)?)
}
pub fn sech(x: KalkValue) -> Result<KalkValue, KalkError> {
KalkValue::from(1f64).div_without_unit(&cosh(x)?)
}
pub fn sin(x: KalkValue) -> Result<KalkValue, KalkError> {
let (real, imaginary, unit) = as_number_or_return!(x);
Ok(KalkValue::Number(
real.clone().sin() * imaginary.clone().cosh(),
real.cos() * imaginary.sinh(),
unit,
))
}
pub fn sinh(x: KalkValue) -> Result<KalkValue, KalkError> {
let (real, imaginary, unit) = as_number_or_return!(x);
Ok(KalkValue::Number(
real.clone().sinh() * imaginary.clone().cos(),
real.cosh() * imaginary.sin(),
unit,
))
}
pub fn sgn(x: KalkValue) -> Result<KalkValue, KalkError> {
if x.has_imaginary() {
x.clone().div_without_unit(&abs(x)?)
} else {
let (real, _, unit) = as_number_or_return!(x);
if real == 0f64 {
Ok(KalkValue::Number(float!(0), float!(0), unit))
} else {
Ok(KalkValue::Number(real.signum(), float!(0), unit))
}
}
}
pub fn sort(x: KalkValue) -> Result<KalkValue, KalkError> {
if let KalkValue::Vector(mut values) = x {
values.sort_by(|a, b| {
if let KalkValue::Boolean(true) =
a.eq_without_unit(b).unwrap_or_else(|_| KalkValue::nan())
{
Ordering::Equal
} else if let KalkValue::Boolean(true) = a
.greater_than_without_unit(b)
.unwrap_or_else(|_| KalkValue::nan())
{
Ordering::Greater
} else {
Ordering::Less
}
});
Ok(KalkValue::Vector(values))
} else {
Err(KalkError::UnexpectedType(
x.get_type_name(),
vec![String::from("vector")],
))
}
}
pub fn sqrt(x: KalkValue) -> Result<KalkValue, KalkError> {
let (real, imaginary, unit) = as_number_or_return!(x.clone());
if x.has_imaginary() {
let abs = abs(x)?;
let (abs_real, _, abs_unit) = as_number_or_return!(abs);
let r = abs_real;
let a = real;
let b = imaginary;
Ok(KalkValue::Number(
((r.clone() + a.clone()) / 2f64).sqrt(),
(b.clone() / b.abs()) * ((r - a) / 2f64).sqrt(),
abs_unit,
))
} else if real < 0f64 {
Ok(KalkValue::Number(float!(0), real.abs().sqrt(), unit))
} else {
Ok(KalkValue::Number(real.sqrt(), float!(0), unit))
}
}
pub fn sum(x: KalkValue) -> Result<KalkValue, KalkError> {
let values = as_vector_or_return!(x);
let mut sum = KalkValue::from(0f64);
for value in values {
sum = sum.add_without_unit(&value)?;
}
Ok(sum)
}
pub fn tan(x: KalkValue) -> Result<KalkValue, KalkError> {
let has_imaginary = x.has_imaginary();
let (real, imaginary, unit) = as_number_or_return!(x);
if has_imaginary {
let a = real * 2f64;
let b = imaginary * 2f64;
Ok(KalkValue::Number(
a.clone().sin() / (a.clone().cos() + b.clone().cosh()),
b.clone().sinh() / (a.cos() + b.cosh()),
unit,
))
} else {
Ok(KalkValue::Number(real.tan(), float!(0), unit))
}
}
pub fn tanh(x: KalkValue) -> Result<KalkValue, KalkError> {
let has_imaginary = x.has_imaginary();
let (real, imaginary, unit) = as_number_or_return!(x);
if has_imaginary {
let a = real * 2f64;
let b = imaginary * 2f64;
Ok(KalkValue::Number(
a.clone().sinh() / (a.clone().cosh() + b.clone().cos()),
b.clone().sin() / (a.cosh() + b.cos()),
unit,
))
} else {
Ok(KalkValue::Number(real.tanh(), float!(0), unit))
}
}
#[allow(clippy::needless_range_loop)]
pub fn transpose(x: KalkValue) -> Result<KalkValue, KalkError> {
if let KalkValue::Matrix(rows) = x {
let original_row_count = rows.len();
let original_column_count = rows.first().unwrap().len();
let mut result =
vec![vec![KalkValue::from(0f64); original_row_count]; original_column_count];
for i in 0..original_row_count {
for j in 0..original_column_count {
result[j][i] = rows[i][j].clone();
}
}
Ok(KalkValue::Matrix(result))
} else {
Err(KalkError::UnexpectedType(
x.get_type_name(),
vec![String::from("matrix")],
))
}
}
pub fn trunc(x: KalkValue) -> Result<KalkValue, KalkError> {
let (real, imaginary, unit) = as_number_or_return!(x);
Ok(KalkValue::Number(real.trunc(), imaginary.trunc(), unit))
}
pub fn ncr(x: KalkValue, y: KalkValue) -> Result<KalkValue, KalkError> {
factorial(x.clone())?.div_without_unit(
&factorial(y.clone())?.mul_without_unit(&factorial(x.sub_without_unit(&y)?)?)?,
)
}
pub fn npr(x: KalkValue, y: KalkValue) -> Result<KalkValue, KalkError> {
factorial(x.clone())?.div_without_unit(&factorial(x.sub_without_unit(&y)?)?)
}
fn multiply_with_i(z: KalkValue) -> Result<KalkValue, KalkError> {
let (real, imaginary, unit) = as_number_or_return!(z);
Ok(KalkValue::Number(-imaginary, real, unit))
}
}
#[cfg(test)]
mod tests {
use super::funcs::*;
use crate::errors::KalkError;
use crate::float;
use crate::prelude::KalkValue;
use crate::test_helpers::cmp;
fn val(x: f64) -> KalkValue {
KalkValue::from(x)
}
#[test]
fn test_unary_funcs() {
let in_out = vec![
(
abs as fn(KalkValue) -> Result<KalkValue, KalkError>,
(3f64, 4f64),
(5f64, 0f64),
),
(abs, (-3f64, 4f64), (5f64, 0f64)),
(abs, (3f64, -4f64), (5f64, 0f64)),
(abs, (-3f64, 0f64), (3f64, 0f64)),
];
for (i, (func, input, expected_output)) in in_out.iter().enumerate() {
let actual_output =
func(KalkValue::Number(float!(input.0), float!(input.1), None)).unwrap();
println!(
"{} | expected: {}, {}",
i, expected_output.0, expected_output.1
);
println!(
"{} | got: {}, {}",
i,
actual_output.to_f64(),
actual_output.imaginary_to_f64()
);
assert!(cmp(expected_output.0, actual_output.to_f64()));
assert!(cmp(expected_output.1, actual_output.imaginary_to_f64()));
}
}
#[test]
fn test_binary_funcs() {
let in_out = vec![
(
gcd as fn(KalkValue, KalkValue) -> Result<KalkValue, KalkError>,
((12f64, 0f64), (18f64, 0f64)),
(6f64, 0f64),
),
(gcd, ((30f64, 0f64), (18f64, 0f64)), (6f64, 0f64)),
(gcd, ((5f64, 0f64), (2f64, 1f64)), (2f64, 1f64)),
(gcd, ((18f64, 4f64), (30f64, 0f64)), (4f64, 2f64)),
(gcd, ((3f64, 1f64), (1f64, -1f64)), (1f64, -1f64)),
(gcd, ((12f64, -8f64), (6f64, 4f64)), (2f64, 0f64)),
(lcm, ((12f64, -8f64), (6f64, 4f64)), (52f64, 0f64)),
(lcm, ((1f64, -2f64), (3f64, 1f64)), (5f64, -5f64)),
];
for (i, (func, input, expected_output)) in in_out.iter().enumerate() {
let actual_output = func(
KalkValue::Number(float!(input.0 .0), float!(input.0 .1), None),
KalkValue::Number(float!(input.1 .0), float!(input.1 .1), None),
)
.unwrap();
println!(
"{} | expected: {}, {}",
i, expected_output.0, expected_output.1
);
println!(
"{} | got: {}, {}",
i,
actual_output.to_f64(),
actual_output.imaginary_to_f64()
);
assert!(cmp(expected_output.0, actual_output.to_f64()));
assert!(cmp(expected_output.1, actual_output.imaginary_to_f64()));
}
}
#[test]
fn test_perms() {
let vecs = vec![
(KalkValue::Vector(vec![val(1f64)]), 1),
(KalkValue::Vector(vec![val(1f64), val(2f64)]), 2),
(KalkValue::Vector(vec![val(1f64), val(2f64), val(3f64)]), 6),
(KalkValue::Vector(vec![val(2f64), val(3f64), val(1f64)]), 6),
(
KalkValue::Vector(vec![val(2f64), val(3f64), val(1f64), val(3f64)]),
12,
),
];
for (vec, expected_len) in vecs {
let permutations = if let KalkValue::Matrix(permutations) = perms(vec).unwrap() {
permutations
} else {
unreachable!()
};
assert_eq!(permutations.len(), expected_len);
let mut results = std::collections::HashSet::with_capacity(permutations.len());
for permutation in permutations {
let as_str = format!("{:?}", permutation);
assert!(!results.contains(&as_str));
results.insert(as_str);
}
}
}
#[test]
fn test_transpose() {
fn to_matrix(rows: Vec<Vec<i32>>) -> KalkValue {
let mut new_rows = Vec::new();
for row in rows {
let mut new_row = Vec::new();
for value in row {
new_row.push(KalkValue::from(value as f64));
}
new_rows.push(new_row);
}
KalkValue::Matrix(new_rows)
}
assert_eq!(
transpose(to_matrix(vec![vec![1, 2], vec![3, 4]])).unwrap(),
to_matrix(vec![vec![1, 3], vec![2, 4]])
);
assert_eq!(
transpose(to_matrix(vec![vec![1, 2], vec![3, 4], vec![5, 6]])).unwrap(),
to_matrix(vec![vec![1, 3, 5], vec![2, 4, 6]])
);
assert_eq!(
transpose(to_matrix(vec![vec![1, 2, 3], vec![4, 5, 6], vec![7, 8, 9]])).unwrap(),
to_matrix(vec![vec![1, 4, 7], vec![2, 5, 8], vec![3, 6, 9]])
);
}
#[test]
#[allow(clippy::approx_constant)]
fn test_trig_funcs() {
let in_out = vec![
(
arg as fn(KalkValue) -> Result<KalkValue, KalkError>,
(0.3f64, 0f64),
(0.0f64, 0.0f64),
),
(arg, (0f64, 0.0f64), (0.0f64, 0.0f64)),
(arg, (-0.1f64, -0.5f64), (-1.7681919f64, 0.0f64)),
(arg, (0.6f64, -0.5f64), (-0.6947383f64, 0.0f64)),
(arg, (2.8f64, 0f64), (0.0f64, 0.0f64)),
(arg, (-2.6f64, 0f64), (3.1415927f64, 0.0f64)),
(arg, (0f64, 0f64), (0.0f64, 0.0f64)),
(ceil, (0.2f64, 0f64), (1.0f64, 0.0f64)),
(ceil, (0f64, -0.7f64), (0.0f64, 0.0f64)),
(ceil, (-0.8f64, -0.3f64), (0.0f64, 0.0f64)),
(ceil, (0.5f64, -0.6f64), (1.0f64, 0.0f64)),
(ceil, (2.2f64, 0f64), (3.0f64, 0.0f64)),
(ceil, (-2.7f64, 0f64), (-2.0f64, 0.0f64)),
(ceil, (0f64, 0f64), (0.0f64, 0.0f64)),
(exp, (-0.3f64, 0f64), (0.7408182f64, 0.0f64)),
(exp, (0f64, -1.0f64), (0.5403023f64, -0.841471f64)),
(exp, (0.5f64, 0.8f64), (1.1486752f64, 1.1827202f64)),
(exp, (-0.8f64, 0.5f64), (0.3943233f64, 0.2154198f64)),
(exp, (2.9f64, 0f64), (18.1741454f64, 0.0f64)),
(exp, (-2.0f64, 0f64), (0.1353353f64, 0.0f64)),
(exp, (0f64, 0f64), (1.0f64, 0.0f64)),
(floor, (-0.8f64, 0f64), (-1.0f64, 0.0f64)),
(floor, (0f64, 0.4f64), (0.0f64, 0.0f64)),
(floor, (0.1f64, 0.8f64), (0.0f64, 0.0f64)),
(floor, (0.6f64, -0.4f64), (0.0f64, -1.0f64)),
(floor, (1.8f64, 0f64), (1.0f64, 0.0f64)),
(floor, (-1.8f64, 0f64), (-2.0f64, 0.0f64)),
(floor, (0f64, 0f64), (0.0f64, 0.0f64)),
(log, (0.4f64, 0f64), (-0.39794f64, 0.0f64)),
(log, (0f64, -0.6f64), (-0.2218487f64, -0.6821882f64)),
(log, (0.6f64, 0.1f64), (-0.2158991f64, 0.0717232f64)),
(log, (-0.7f64, -0.8f64), (0.0265392f64, -0.9943721f64)),
(log, (2.2f64, 0f64), (0.3424227f64, 0.0f64)),
(log, (-2.9f64, 0f64), (0.462398f64, 1.3643764f64)),
(log, (0f64, 0f64), (f64::NEG_INFINITY, 0.0f64)),
(ln, (0.3f64, 0f64), (-1.2039728f64, 0.0f64)),
(ln, (0f64, 0.0f64), (f64::NEG_INFINITY, 0.0f64)),
(ln, (-1.0f64, 0.8f64), (0.2473481f64, 2.4668517f64)),
(ln, (-0.2f64, 1.0f64), (0.0196104f64, 1.7681919f64)),
(ln, (1.5f64, 0f64), (0.4054651f64, 0.0f64)),
(ln, (-2.4f64, 0f64), (0.8754687f64, 3.1415927f64)),
(ln, (0f64, 0f64), (f64::NEG_INFINITY, 0.0f64)),
(sqrt, (-0.9f64, 0f64), (0.0f64, 0.9486833f64)),
(sqrt, (0f64, 0.3f64), (0.3872983f64, 0.3872983f64)),
(sqrt, (0.6f64, -0.2f64), (0.7850018f64, -0.1273882f64)),
(sqrt, (0.0f64, 0.4f64), (0.4472136f64, 0.4472136f64)),
(sqrt, (2.5f64, 0f64), (1.5811388f64, 0.0f64)),
(sqrt, (-2.5f64, 0f64), (0.0f64, 1.5811388f64)),
(sqrt, (0f64, 0f64), (0.0f64, 0.0f64)),
(acos, (-0.8f64, 0f64), (2.4980915f64, 0.0f64)),
(acos, (0f64, 0.4f64), (1.5707963f64, -0.3900353f64)),
(acos, (-0.5f64, 0.9f64), (1.9389155f64, -0.8561366f64)),
(acos, (0.4f64, -1.0f64), (1.2899829f64, 0.9099081f64)),
(acos, (2.2f64, 0f64), (0.0f64, 1.4254169f64)),
(acos, (-1.5f64, 0f64), (3.1415927f64, -0.9624237f64)),
(acos, (0f64, 0f64), (1.5707963f64, 0.0f64)),
(acosh, (0.6f64, 0f64), (0.0f64, 0.9272952f64)),
(acosh, (0f64, 0.5f64), (0.4812118f64, 1.5707963f64)),
(acosh, (1.0f64, -1.0f64), (1.0612751f64, -0.9045569f64)),
(acosh, (0.5f64, 0.6f64), (0.6197743f64, 1.1403656f64)),
(acosh, (2.2f64, 0f64), (1.4254169f64, 0.0f64)),
(acosh, (-2.7f64, 0f64), (1.6501935f64, 3.1415927f64)),
(acosh, (0f64, 0f64), (0.0f64, 1.5707963f64)),
(acsc, (-0.9f64, 0f64), (-1.5707963f64, 0.4671453f64)),
(acsc, (0f64, -0.9f64), (0.0f64, 0.9578004f64)),
(acsc, (0.8f64, -0.6f64), (0.6847192f64, 0.7127085f64)),
(acsc, (0.3f64, -0.5f64), (0.4742891f64, 1.2767722f64)),
(acsc, (2.7f64, 0f64), (0.3794077f64, 0.0f64)),
(acsc, (-1.7f64, 0f64), (-0.6288749f64, 0.0f64)),
(acsc, (0f64, 0f64), (f64::NAN, f64::NAN)),
(acsch, (-0.8f64, 0f64), (-1.047593f64, 0.0f64)),
(acsch, (0f64, 0.4f64), (-1.5667992f64, -1.5707963f64)),
(acsch, (-0.1f64, -0.9f64), (-0.5019389f64, 1.335583f64)),
(acsch, (1.0f64, -0.7f64), (0.6735491f64, 0.3900529f64)),
(acsch, (2.5f64, 0f64), (0.3900353f64, 0.0f64)),
(acsch, (-2.4f64, 0f64), (-0.4054651f64, 0.0f64)),
(acsch, (0f64, 0f64), (f64::NAN, f64::NAN)),
(acot, (-1.0f64, 0f64), (-0.7853982f64, 0.0f64)),
(acot, (0f64, -0.5f64), (1.5707963f64, 0.5493061f64)),
(acot, (-0.4f64, -0.4f64), (-1.1376452f64, 0.3513356f64)),
(acot, (-0.9f64, -0.7f64), (-0.7028238f64, 0.3534233f64)),
(acot, (2.3f64, 0f64), (0.4101273f64, 0.0f64)),
(acot, (-1.7f64, 0f64), (-0.5317241f64, 0.0f64)),
(acot, (0f64, 0f64), (1.5707963f64, 0.0f64)),
(acoth, (0.5f64, 0f64), (0.5493061f64, -1.5707963f64)),
(acoth, (0f64, 0.9f64), (0.0f64, -0.8379812f64)),
(acoth, (-0.4f64, -0.1f64), (-0.4180715f64, 1.4525683f64)),
(acoth, (-0.9f64, -0.5f64), (-0.6744352f64, 0.7554341f64)),
(acoth, (1.7f64, 0f64), (0.6749634f64, 0.0f64)),
(acoth, (-2.1f64, 0f64), (-0.518046f64, 0.0f64)),
(acoth, (0f64, 0f64), (0.0f64, 1.5707963f64)),
(asec, (-0.1f64, 0f64), (3.1415927f64, -2.9932228f64)),
(asec, (0f64, 0.7f64), (1.5707963f64, 1.1544774f64)),
(asec, (0.9f64, -0.4f64), (0.6818568f64, -0.6148366f64)),
(asec, (-0.5f64, 0.9f64), (1.9278638f64, 0.8134796f64)),
(asec, (1.5f64, 0f64), (0.8410687f64, 0.0f64)),
(asec, (-2.7f64, 0f64), (1.950204f64, 0.0f64)),
(asec, (0f64, 0f64), (f64::NAN, f64::NAN)),
(asech, (0.6f64, 0f64), (1.0986123f64, 0.0f64)),
(asech, (0f64, -1.0f64), (0.8813736f64, 1.5707963f64)),
(asech, (0.1f64, -0.7f64), (1.1434743f64, 1.4548078f64)),
(asech, (-0.4f64, -0.3f64), (1.3742673f64, 2.4355905f64)),
(asech, (2.3f64, 0f64), (0.0f64, 1.1209995f64)),
(asech, (-2.2f64, 0f64), (0.0f64, 2.0426582f64)),
(asech, (0f64, 0f64), (f64::NAN, f64::NAN)),
(asin, (-0.8f64, 0f64), (-0.9272952f64, 0.0f64)),
(asin, (0f64, 0.5f64), (0.0f64, 0.4812118f64)),
(asin, (-0.2f64, -1.0f64), (-0.1411811f64, -0.8884618f64)),
(asin, (-0.8f64, 0.3f64), (-0.8214537f64, 0.427304f64)),
(asin, (1.5f64, 0f64), (1.5707963f64, -0.9624237f64)),
(asin, (-3.0f64, 0f64), (-1.5707963f64, 1.7627472f64)),
(asin, (0f64, 0f64), (0.0f64, 0.0f64)),
(asinh, (1.0f64, 0f64), (0.8813736f64, 0.0f64)),
(asinh, (0f64, 0.1f64), (0.0f64, 0.1001674f64)),
(asinh, (0.9f64, 1.0f64), (1.0029766f64, 0.7031f64)),
(asinh, (-0.5f64, 0.8f64), (-0.627368f64, 0.7272513f64)),
(asinh, (1.8f64, 0f64), (1.3504407f64, 0.0f64)),
(asinh, (-2.4f64, 0f64), (-1.6094379f64, 0.0f64)),
(asinh, (0f64, 0f64), (0.0f64, 0.0f64)),
(atan, (0.0f64, 0f64), (0.0f64, 0.0f64)),
(atan, (0f64, 0.8f64), (0.0f64, 1.0986123f64)),
(atan, (0.4f64, 0.2f64), (0.3926991f64, 0.1732868f64)),
(atan, (0.4f64, -0.3f64), (0.4088225f64, -0.2614921f64)),
(atan, (1.5f64, 0f64), (0.9827937f64, 0.0f64)),
(atan, (-1.8f64, 0f64), (-1.0636978f64, 0.0f64)),
(atan, (0f64, 0f64), (0.0f64, 0.0f64)),
(atanh, (0.9f64, 0f64), (1.4722195f64, 0.0f64)),
(atanh, (0f64, -0.1f64), (0.0f64, -0.0996687f64)),
(atanh, (-1.0f64, -0.3f64), (-0.9541226f64, -0.8598431f64)),
(atanh, (0.6f64, -0.4f64), (0.5350165f64, -0.5151884f64)),
(atanh, (1.5f64, 0f64), (0.804719f64, -1.5707963f64)),
(atanh, (-2.3f64, 0f64), (-0.4657791f64, 1.5707963f64)),
(atanh, (0f64, 0f64), (0.0f64, 0.0f64)),
(cos, (0.1f64, 0f64), (0.9950042f64, 0.0f64)),
(cos, (0f64, -0.2f64), (1.0200668f64, 0.0f64)),
(cos, (-0.1f64, -0.1f64), (0.9999833f64, -0.01f64)),
(cos, (0.5f64, -0.4f64), (0.9487303f64, 0.1969252f64)),
(cos, (2.4f64, 0f64), (-0.7373937f64, 0.0f64)),
(cos, (-2.2f64, 0f64), (-0.5885011f64, 0.0f64)),
(cos, (0f64, 0f64), (1.0f64, 0.0f64)),
(cosh, (0.0f64, 0f64), (1.0f64, 0.0f64)),
(cosh, (0f64, 0.9f64), (0.62161f64, 0.0f64)),
(cosh, (0.8f64, -0.4f64), (1.2318592f64, -0.3458448f64)),
(cosh, (-0.2f64, 1.0f64), (0.5511444f64, -0.1694184f64)),
(cosh, (2.9f64, 0f64), (9.1145843f64, 0.0f64)),
(cosh, (-2.7f64, 0f64), (7.4734686f64, 0.0f64)),
(cosh, (0f64, 0f64), (1.0f64, 0.0f64)),
(csc, (0.5f64, 0f64), (2.0858296f64, 0.0f64)),
(csc, (0f64, -0.6f64), (0.0f64, 1.5707129f64)),
(csc, (-0.9f64, -0.7f64), (-0.8268848f64, 0.3965713f64)),
(csc, (0.3f64, -0.5f64), (0.9285645f64, 1.3871816f64)),
(csc, (2.3f64, 0f64), (1.3410125f64, 0.0f64)),
(csc, (-2.5f64, 0f64), (-1.6709215f64, 0.0f64)),
(csc, (0f64, 0f64), (f64::NAN, f64::NAN)),
(csch, (-0.8f64, 0f64), (-1.1259917f64, 0.0f64)),
(csch, (0f64, 0.7f64), (0.0f64, -1.5522703f64)),
(csch, (0.6f64, -0.4f64), (1.0528253f64, 0.8288386f64)),
(csch, (-0.4f64, 0.0f64), (-2.4345571f64, 0.0f64)),
(csch, (2.7f64, 0f64), (0.1350209f64, 0.0f64)),
(csch, (-3.0f64, 0f64), (-0.0998216f64, 0.0f64)),
(csch, (0f64, 0f64), (f64::NAN, f64::NAN)),
(cot, (-0.3f64, 0f64), (-3.2327281f64, 0.0f64)),
(cot, (0f64, 0.4f64), (0.0f64, -2.6319324f64)),
(cot, (-0.9f64, 0.4f64), (-0.6224112f64, -0.5676115f64)),
(cot, (-0.2f64, 0.8f64), (-0.2350987f64, -1.4341723f64)),
(cot, (1.7f64, 0f64), (-0.1299275f64, 0.0f64)),
(cot, (-1.8f64, 0f64), (0.2333035f64, 0.0f64)),
(cot, (0f64, 0f64), (f64::NAN, f64::NAN)),
(coth, (0.3f64, 0f64), (3.4327384f64, 0.0f64)),
(coth, (0f64, 0.9f64), (0.0f64, -0.7935511f64)),
(coth, (-0.7f64, -0.5f64), (-1.1823582f64, 0.5224593f64)),
(coth, (-0.8f64, -0.3f64), (-1.3558181f64, 0.3222608f64)),
(coth, (2.8f64, 0f64), (1.0074232f64, 0.0f64)),
(coth, (-2.3f64, 0f64), (-1.0203078f64, 0.0f64)),
(coth, (0f64, 0f64), (f64::NAN, f64::NAN)),
(sec, (0.4f64, 0f64), (1.0857044f64, 0.0f64)),
(sec, (0f64, -0.7f64), (0.7967055f64, 0.0f64)),
(sec, (0.7f64, -0.2f64), (1.2472669f64, -0.2073543f64)),
(sec, (0.0f64, 0.1f64), (0.9950207f64, 0.0f64)),
(sec, (1.5f64, 0f64), (14.1368329f64, 0.0f64)),
(sec, (-1.6f64, 0f64), (-34.2471356f64, 0.0f64)),
(sec, (0f64, 0f64), (1.0f64, 0.0f64)),
(sech, (-0.6f64, 0f64), (0.8435507f64, 0.0f64)),
(sech, (0f64, 1.0f64), (1.8508157f64, 0.0f64)),
(sech, (0.5f64, -0.9f64), (1.0653621f64, 0.6204037f64)),
(sech, (-0.8f64, 0.9f64), (0.7074639f64, 0.5919997f64)),
(sech, (2.0f64, 0f64), (0.2658022f64, 0.0f64)),
(sech, (-2.0f64, 0f64), (0.2658022f64, 0.0f64)),
(sech, (0f64, 0f64), (1.0f64, 0.0f64)),
(sin, (1.0f64, 0f64), (0.841471f64, 0.0f64)),
(sin, (0f64, -0.3f64), (0.0f64, -0.3045203f64)),
(sin, (-0.1f64, -0.6f64), (-0.118349f64, -0.633473f64)),
(sin, (-0.4f64, 0.4f64), (-0.4209894f64, 0.3783279f64)),
(sin, (2.8f64, 0f64), (0.3349882f64, 0.0f64)),
(sin, (-2.0f64, 0f64), (-0.9092974f64, 0.0f64)),
(sin, (0f64, 0f64), (0.0f64, 0.0f64)),
(sinh, (-0.4f64, 0f64), (-0.4107523f64, 0.0f64)),
(sinh, (0f64, -1.0f64), (0.0f64, -0.841471f64)),
(sinh, (-0.9f64, -0.4f64), (-0.9454845f64, -0.5580701f64)),
(sinh, (0.0f64, 0.9f64), (0.0f64, 0.7833269f64)),
(sinh, (2.7f64, 0f64), (7.4062631f64, 0.0f64)),
(sinh, (-1.9f64, 0f64), (-3.2681629f64, 0.0f64)),
(sinh, (0f64, 0f64), (0.0f64, 0.0f64)),
(tan, (-0.6f64, 0f64), (-0.6841368f64, 0.0f64)),
(tan, (0f64, -0.9f64), (0.0f64, -0.7162979f64)),
(tan, (-0.1f64, 0.1f64), (-0.099328f64, 0.1006613f64)),
(tan, (0.7f64, -0.1f64), (0.8280854f64, -0.1691851f64)),
(tan, (2.8f64, 0f64), (-0.3555298f64, 0.0f64)),
(tan, (-1.6f64, 0f64), (34.2325327f64, 0.0f64)),
(tan, (0f64, 0f64), (0.0f64, 0.0f64)),
(tanh, (-0.4f64, 0f64), (-0.379949f64, 0.0f64)),
(tanh, (0f64, -0.3f64), (0.0f64, -0.3093362f64)),
(tanh, (-0.8f64, 0.5f64), (-0.7619454f64, 0.2698954f64)),
(tanh, (-0.3f64, 0.0f64), (-0.2913126f64, 0.0f64)),
(tanh, (1.5f64, 0f64), (0.9051483f64, 0.0f64)),
(tanh, (-2.3f64, 0f64), (-0.9800964f64, 0.0f64)),
(tanh, (0f64, 0f64), (0.0f64, 0.0f64)),
];
for (i, (func, input, expected_output)) in in_out.iter().enumerate() {
let actual_output =
func(KalkValue::Number(float!(input.0), float!(input.1), None)).unwrap();
let expected_has_nan_or_inf = expected_output.0.is_nan()
|| expected_output.0.is_infinite()
|| expected_output.1.is_nan()
|| expected_output.1.is_infinite();
let actual_has_nan_or_inf = actual_output.to_f64().is_nan()
|| actual_output.to_f64().is_infinite()
|| actual_output.imaginary_to_f64().is_nan()
|| actual_output.imaginary_to_f64().is_infinite();
if expected_has_nan_or_inf || actual_has_nan_or_inf {
continue;
}
println!(
"{} | expected: {}, {}",
i, expected_output.0, expected_output.1
);
println!(
"{} | got: {}, {}",
i,
actual_output.to_f64(),
actual_output.imaginary_to_f64()
);
assert!(cmp(expected_output.0, actual_output.to_f64()));
assert!(cmp(expected_output.1, actual_output.imaginary_to_f64()));
}
}
}