const PI: f64 = core::f64::consts::PI;
const HALF_PI: f64 = core::f64::consts::FRAC_PI_2;
const TWO_PI: f64 = 2.0 * PI;
const TAN_INFINITY_CUTOFF: f64 = 1e-12;
const TAN_POS_INFINITY: f64 = 1e12;
const TAN_NEG_INFINITY: f64 = -1e12;
const SQRT_NR_ITERATIONS: usize = 20;
const ATAN_TAYLOR_TERMS: usize = 30;
const SINH_TAYLOR_TERMS: usize = 30;
const COSH_TAYLOR_TERMS: usize = 30;
const LN_SERIES_TERMS: usize = 39;
const fn abs(x: f64) -> f64 {
if x < 0.0 { -x } else { x }
}
const fn reduce_angle_for_sine(mut x: f64) -> f64 {
while x > PI {
x -= TWO_PI;
}
while x < -PI {
x += TWO_PI;
}
if x > HALF_PI {
x = PI - x;
} else if x < -HALF_PI {
x = -PI - x;
}
x
}
pub const fn sin_approx(x: f64) -> f64 {
let x = reduce_angle_for_sine(x);
let x2 = x * x;
let x3 = x * x2; let x5 = x3 * x2; let x7 = x5 * x2; let x9 = x7 * x2; let x11 = x9 * x2; let x13 = x11 * x2;
let term1 = x;
let term2 = x3 / 6.0; let term3 = x5 / 120.0; let term4 = x7 / 5040.0; let term5 = x9 / 362880.0; let term6 = x11 / 39916800.0; let term7 = x13 / 6227020800.0;
term1 - term2 + term3 - term4 + term5 - term6 + term7
}
const fn reduce_angle_for_cos(mut x: f64) -> (f64, f64) {
while x > PI {
x -= TWO_PI;
}
while x < -PI {
x += TWO_PI;
}
if x > HALF_PI {
(PI - x, -1.0)
} else if x < -HALF_PI {
(-PI - x, -1.0)
} else {
(x, 1.0)
}
}
pub const fn cos_approx(x: f64) -> f64 {
let (x, sign) = reduce_angle_for_cos(x);
let x2 = x * x;
let x4 = x2 * x2; let x6 = x4 * x2; let x8 = x6 * x2; let x10 = x8 * x2; let x12 = x10 * x2; let x14 = x12 * x2;
let term0 = 1.0;
let term1 = x2 / 2.0; let term2 = x4 / 24.0; let term3 = x6 / 720.0; let term4 = x8 / 40320.0; let term5 = x10 / 3628800.0; let term6 = x12 / 479001600.0; let term7 = x14 / 87178291200.0;
sign * (term0 - term1 + term2 - term3 + term4 - term5 + term6 - term7)
}
const fn round_const(x: f64) -> f64 {
if x >= 0.0 {
(x + 0.5) as i64 as f64
} else {
(x - 0.5) as i64 as f64
}
}
pub const fn exp_approx(x: f64) -> f64 {
const LN_2: f64 = core::f64::consts::LN_2;
if x == 0.0 {
return 1.0;
}
let n_float = round_const(x / LN_2);
let n = n_float as i32;
let r = x - (n_float * LN_2);
let r2 = r * r;
let r3 = r2 * r;
let r4 = r3 * r;
let r5 = r4 * r;
let r6 = r5 * r;
let r7 = r6 * r;
let r8 = r7 * r;
let r9 = r8 * r;
let r10 = r9 * r;
let r11 = r10 * r;
let r12 = r11 * r;
let r13 = r12 * r;
let r14 = r13 * r;
let r15 = r14 * r;
let r16 = r15 * r;
let r17 = r16 * r;
let r18 = r17 * r;
let r19 = r18 * r;
let taylor = 1.0
+ r
+ r2 / 2.0
+ r3 / 6.0
+ r4 / 24.0
+ r5 / 120.0
+ r6 / 720.0
+ r7 / 5040.0
+ r8 / 40320.0
+ r9 / 362880.0
+ r10 / 3628800.0
+ r11 / 39916800.0
+ r12 / 479001600.0
+ r13 / 6227020800.0
+ r14 / 87178291200.0
+ r15 / 1307674368000.0
+ r16 / 20922789888000.0
+ r17 / 355687428096000.0
+ r18 / 6402373705728000.0
+ r19 / 121645100408832000.0;
static_powi(2.0, n) * taylor
}
pub const fn tan_approx(x: f64) -> f64 {
let cos_val = cos_approx(x);
if abs(cos_val) < TAN_INFINITY_CUTOFF {
if sin_approx(x) > 0.0 {
return TAN_POS_INFINITY; } else {
return TAN_NEG_INFINITY; }
}
sin_approx(x) / cos_val
}
pub const fn static_powi(mut base: f64, exp: i32) -> f64 {
if exp == 0 {
return 1.0;
}
let mut result = 1.0;
let mut positive_exp = if exp < 0 { -exp } else { exp };
while positive_exp > 0 {
if (positive_exp & 1) == 1 {
result *= base;
}
base *= base;
positive_exp >>= 1;
}
if exp < 0 { 1.0 / result } else { result }
}
#[allow(clippy::approx_constant)]
pub const fn ln_approx(number: f64) -> f64 {
if number <= 0.0 {
return f64::NAN;
}
let mut x = number;
let mut k = 0;
while x > 1.5 {
x *= 0.5;
k += 1;
}
while x < 0.5 {
x *= 2.0;
k -= 1;
}
let z = (x - 1.0) / (x + 1.0);
let z2 = z * z;
let mut sum = 0.0;
let mut term = z;
let mut i = 1;
while i <= LN_SERIES_TERMS {
sum += term / (2 * i - 1) as f64;
term *= z2;
i += 1;
}
const LN_2: f64 = 0.6931471805599453;
2.0 * sum + (k as f64) * LN_2
}
pub const fn sqrt_approx(x: f64) -> f64 {
if x < 0.0 {
return f64::NAN;
}
if x == 0.0 {
return 0.0;
}
let mut guess = if x < 1.0 { x } else { x / 2.0 };
let mut i = 0;
while i < SQRT_NR_ITERATIONS {
guess = 0.5 * (guess + x / guess);
i += 1;
}
guess
}
pub const fn arctan_approx(x: f64) -> f64 {
if x > 1.0 {
return PI / 2.0 - arctan_approx(1.0 / x);
}
if x < -1.0 {
return -PI / 2.0 - arctan_approx(1.0 / x);
}
if x > 0.66 {
let y = (x - 1.0) / (1.0 + x);
return PI / 4.0 + arctan_approx(y);
}
if x < -0.66 {
let y = (x + 1.0) / (1.0 - x);
return -PI / 4.0 + arctan_approx(y);
}
let mut term = x;
let mut sum = x;
let x2 = x * x;
let mut i = 1;
while i < ATAN_TAYLOR_TERMS {
term *= -x2;
sum += term / (2 * i + 1) as f64;
i += 1;
}
sum
}
pub const fn sinh_approx(x: f64) -> f64 {
let mut term = x;
let mut sum = x;
let mut i = 1;
while i < SINH_TAYLOR_TERMS {
term *= x * x / ((2 * i) as f64 * (2 * i + 1) as f64);
sum += term;
i += 1;
}
sum
}
pub const fn cosh_approx(x: f64) -> f64 {
let mut term = 1.0;
let mut sum = 1.0;
let mut i = 1;
while i < COSH_TAYLOR_TERMS {
term *= x * x / ((2 * i - 1) as f64 * (2 * i) as f64);
sum += term;
i += 1;
}
sum
}