use crate::eval::coercion::to_number;
use crate::eval::functions::check_arity;
use crate::types::{ErrorKind, Value};
fn erf(x: f64) -> f64 {
if x < 0.0 {
return -erf(-x);
}
if x > 6.0 {
return 1.0;
}
let t = 1.0 / (1.0 + 0.3275911 * x);
let poly = t * (0.254_829_592
+ t * (-0.284_496_736
+ t * (1.421_413_741
+ t * (-1.453_152_027
+ t * 1.061_405_429))));
1.0 - poly * (-x * x).exp()
}
fn erfc(x: f64) -> f64 {
1.0 - erf(x)
}
pub fn erf_fn(args: &[Value]) -> Value {
if let Some(err) = check_arity(args, 1, 2) {
return err;
}
let x = match to_number(args[0].clone()) {
Err(e) => return e,
Ok(v) => v,
};
if args.len() == 1 {
Value::Number(erf(x))
} else {
let y = match to_number(args[1].clone()) {
Err(e) => return e,
Ok(v) => v,
};
Value::Number(erf(y) - erf(x))
}
}
pub fn erf_precise_fn(args: &[Value]) -> Value {
if let Some(err) = check_arity(args, 1, 1) {
return err;
}
let x = match to_number(args[0].clone()) {
Err(e) => return e,
Ok(v) => v,
};
Value::Number(erf(x))
}
pub fn erfc_fn(args: &[Value]) -> Value {
if let Some(err) = check_arity(args, 1, 1) {
return err;
}
let x = match to_number(args[0].clone()) {
Err(e) => return e,
Ok(v) => v,
};
Value::Number(erfc(x))
}
pub fn erfc_precise_fn(args: &[Value]) -> Value {
erfc_fn(args)
}
fn gammaln(x: f64) -> f64 {
const G: f64 = 7.0;
const C: [f64; 9] = [
0.999_999_999_999_809_3,
676.520_368_121_885_1,
-1_259.139_216_722_402_9,
771.323_428_777_653_1,
-176.615_029_162_140_6,
12.507_343_278_686_905,
-0.138_571_095_265_720_12,
9.984_369_578_019_572e-6,
1.505_632_735_149_311_6e-7,
];
let x = x - 1.0;
let t = x + G + 0.5;
let mut ser = C[0];
let mut xp = x;
for c in &C[1..] {
xp += 1.0;
ser += c / xp;
}
use std::f64::consts::PI;
(2.0 * PI).sqrt().ln() + ser.ln() + (x + 0.5) * t.ln() - t
}
pub fn gammaln_fn(args: &[Value]) -> Value {
if let Some(err) = check_arity(args, 1, 1) {
return err;
}
let x = match to_number(args[0].clone()) {
Err(e) => return e,
Ok(v) => v,
};
if x <= 0.0 {
return Value::Error(ErrorKind::Num);
}
let result = gammaln(x);
if !result.is_finite() {
return Value::Error(ErrorKind::Num);
}
Value::Number(result)
}
pub fn gammaln_precise_fn(args: &[Value]) -> Value {
gammaln_fn(args)
}
#[cfg(test)]
mod tests;