use crate::double::{
acos64, asin64, exp64, k_cos64, k_sin64, ln64, log10_64, log2_64, reduce_pio2_64,
};
use crate::ops::sqrt;
pub const PIO2_1: f32 = 1.570_312_5;
pub const PIO2_2: f32 = 4.837_512_969_970_703_125e-4;
pub const PIO2_3: f32 = 7.549_789_948_768_648e-8;
pub const PIO2_4: f32 = -1.715_124_510_005_881_9e-15;
pub const SPLIT_MASK: u32 = 0xFFFF_F000;
pub const FRAC_2_PI: f32 = core::f32::consts::FRAC_2_PI;
pub const SIN_P: [f32; 3] = [-1.951_529_589_1e-4, 8.332_160_873_6e-3, -1.666_665_461_1e-1];
pub const COS_P: [f32; 3] = [
2.443_315_711_809_948e-5,
-1.388_731_625_493_765e-3,
4.166_664_568_298_827e-2,
];
#[inline(always)]
fn sin_poly(r: f32) -> f32 {
let z = r * r;
let p = ((SIN_P[0] * z + SIN_P[1]) * z + SIN_P[2]) * z * r;
r + p
}
#[inline(always)]
fn cos_poly(r: f32) -> f32 {
let z = r * r;
let p = ((COS_P[0] * z + COS_P[1]) * z + COS_P[2]) * z * z;
1.0 - 0.5 * z + p
}
#[inline(always)]
pub(crate) fn nearest_int(t: f32) -> f32 {
let half = if t < 0.0 { -0.5 } else { 0.5 };
((t + half) as i32) as f32
}
#[inline(always)]
pub(crate) fn split_hi(a: f32) -> f32 {
f32::from_bits(a.to_bits() & SPLIT_MASK)
}
#[inline(always)]
pub(crate) fn two_prod(a: f32, b: f32) -> (f32, f32) {
let p = a * b;
let (ah, bh) = (split_hi(a), split_hi(b));
let (al, bl) = (a - ah, b - bh);
(p, (((ah * bh - p) + ah * bl) + al * bh) + al * bl)
}
#[inline(always)]
fn reduce_pio2(x: f32) -> (i32, f32) {
let kf = nearest_int(x * FRAC_2_PI);
let (p3, e3) = two_prod(kf, PIO2_3);
let r = ((((x - kf * PIO2_1) - kf * PIO2_2) - p3) - e3) - kf * PIO2_4;
let k = (kf as i32) & 3;
(k, r)
}
#[inline(always)]
pub(crate) fn canon(v: f32) -> f32 {
if v.is_nan() {
f32::NAN
} else {
v
}
}
#[inline]
#[must_use]
pub fn sin(x: f32) -> f32 {
if !x.is_finite() {
return f32::NAN;
}
if x == 0.0 {
return x; }
let (k, r) = reduce_pio2(x);
canon(match k {
0 => sin_poly(r),
1 => cos_poly(r),
2 => -sin_poly(r),
_ => -cos_poly(r),
})
}
#[inline]
#[must_use]
pub fn cos(x: f32) -> f32 {
if !x.is_finite() {
return f32::NAN;
}
let (k, r) = reduce_pio2(x);
canon(match k {
0 => cos_poly(r),
1 => -sin_poly(r),
2 => -cos_poly(r),
_ => sin_poly(r),
})
}
#[inline]
#[must_use]
pub fn sin_cos(x: f32) -> (f32, f32) {
if !x.is_finite() {
return (f32::NAN, f32::NAN);
}
if x == 0.0 {
return (x, 1.0); }
let (k, r) = reduce_pio2(x);
let s = sin_poly(r);
let c = cos_poly(r);
let (s, c) = match k {
0 => (s, c),
1 => (c, -s),
2 => (-s, -c),
_ => (-c, s),
};
(canon(s), canon(c))
}
pub const LOG2E: f32 = core::f32::consts::LOG2_E;
pub const LN2_HI: f32 = 0.693_145_751_953_125;
pub const LN2_LO: f32 = 1.428_606_765_330_187e-6;
pub const EXP_P: [f32; 5] = [
1.987_569_150_0e-4,
1.398_199_950_7e-3,
8.333_451_907_3e-3,
4.166_579_589_4e-2,
1.666_666_545_9e-1,
];
pub const EXP_HI: f32 = 88.722_84;
pub const EXP_LO: f32 = -103.972_08;
#[inline(always)]
fn pow2i(k: i32) -> f32 {
debug_assert!((-126..=127).contains(&k));
f32::from_bits(((k + 127) as u32) << 23)
}
#[inline]
#[must_use]
pub fn exp(x: f32) -> f32 {
if x.is_nan() {
return f32::NAN;
}
if x > EXP_HI {
return f32::INFINITY;
}
if x < EXP_LO {
return 0.0;
}
let kf = nearest_int(x * LOG2E);
let k = kf as i32;
let r = (x - kf * LN2_HI) - kf * LN2_LO;
let p = (((((EXP_P[0] * r + EXP_P[1]) * r + EXP_P[2]) * r + EXP_P[3]) * r + EXP_P[4]) * r
+ 0.5)
* r
* r
+ r
+ 1.0;
if k > 127 {
p * pow2i(127) * pow2i(k - 127)
} else if k < -126 {
p * pow2i(-126) * pow2i(k + 126)
} else {
p * pow2i(k)
}
}
pub const LG1: f32 = 0.666_666_626_93;
pub const LG2: f32 = 0.400_009_721_52;
pub const LG3: f32 = 0.284_987_866_88;
pub const LG4: f32 = 0.242_790_788_41;
pub const SQRT2_BITS: u32 = 0x3fb5_04f3;
#[inline]
#[must_use]
pub fn ln(x: f32) -> f32 {
if x.is_nan() || x < 0.0 {
return f32::NAN;
}
if x == 0.0 {
return f32::NEG_INFINITY;
}
if x.is_infinite() {
return f32::INFINITY;
}
let mut bits = x.to_bits();
let mut k: i32 = 0;
if bits < 0x0080_0000 {
let scaled = x * 33_554_432.0; bits = scaled.to_bits();
k -= 25;
}
k += ((bits >> 23) as i32) - 127;
bits = (bits & 0x007f_ffff) | 0x3f80_0000; if bits >= SQRT2_BITS {
bits -= 0x0080_0000;
k += 1;
}
let m = f32::from_bits(bits);
let f = m - 1.0;
let s = f / (2.0 + f);
let z = s * s;
let w = z * z;
let t1 = w * (LG2 + w * LG4);
let t2 = z * (LG1 + w * LG3);
let r = t2 + t1;
let hfsq = 0.5 * f * f;
let kf = k as f32;
kf * LN2_HI - ((hfsq - (s * (hfsq + r) + kf * LN2_LO)) - f)
}
#[inline]
#[must_use]
pub fn log2(x: f32) -> f32 {
log2_64(f64::from(x)) as f32
}
#[inline]
#[must_use]
pub fn log10(x: f32) -> f32 {
log10_64(f64::from(x)) as f32
}
#[inline]
#[must_use]
pub fn powf(x: f32, y: f32) -> f32 {
if y == 0.0 || x == 1.0 {
return 1.0;
}
if x.is_nan() || y.is_nan() || x < 0.0 {
return f32::NAN;
}
if x == 0.0 {
return if y > 0.0 { 0.0 } else { f32::INFINITY };
}
exp64(f64::from(y) * ln64(f64::from(x))) as f32
}
#[inline]
#[must_use]
pub fn powi(x: f32, n: i32) -> f32 {
if n == 0 {
return 1.0;
}
if x.is_nan() {
return f32::NAN;
}
let mut base = if n < 0 { 1.0 / x } else { x };
let mut e = n.unsigned_abs();
let mut acc = 1.0f32;
while e > 0 {
if e & 1 == 1 {
acc *= base;
}
e >>= 1;
if e > 0 {
base *= base;
}
}
acc
}
#[inline]
#[must_use]
pub fn cbrt(x: f32) -> f32 {
if x.is_nan() {
return f32::NAN;
}
if x == 0.0 || x.is_infinite() {
return x;
}
let ax = x.abs();
let (ax, post) = if ax.to_bits() < 0x0080_0000 {
(ax * 16_777_216.0, 1.0 / 256.0)
} else if ax.to_bits() >= 0x6f80_0000 {
(ax * (1.0 / 16_777_216.0), 256.0)
} else {
(ax, 1.0)
};
let mut y = f32::from_bits(ax.to_bits() / 3 + 0x2a51_37a0);
for _ in 0..3 {
let y2 = y * y;
y -= (y2 * y - ax) / (3.0 * y2);
}
let y = y * post;
if x < 0.0 {
-y
} else {
y
}
}
#[inline]
#[must_use]
pub fn hypot(x: f32, y: f32) -> f32 {
let ax = x.abs();
let ay = y.abs();
if ax.is_infinite() || ay.is_infinite() {
return f32::INFINITY;
}
if ax.is_nan() || ay.is_nan() {
return f32::NAN;
}
let m = if ax > ay { ax } else { ay };
if m == 0.0 {
return 0.0;
}
let (scale, unscale) = if m > 1.0e18 {
(
1.0 / 18_446_744_073_709_551_616.0,
18_446_744_073_709_551_616.0,
) } else if m < 1.0e-18 {
(
18_446_744_073_709_551_616.0,
1.0 / 18_446_744_073_709_551_616.0,
)
} else {
(1.0, 1.0)
};
let sx = ax * scale;
let sy = ay * scale;
sqrt(sx * sx + sy * sy) * unscale
}
const ATANHI_F: [f32; 4] = [
4.636_476_040e-01,
7.853_981_256_5e-01,
9.827_936_887_7e-01,
1.570_796_251_3e+00,
];
const ATANLO_F: [f32; 4] = [
5.012_158_244e-09,
3.774_894_707_9e-08,
3.447_321_717e-08,
7.549_789_415_9e-08,
];
const AT_F: [f32; 5] = [
3.333_332_836_6e-01,
-1.999_915_838_2e-01,
1.425_363_570_5e-01,
-1.064_801_737_7e-01,
6.168_760_731_8e-02,
];
#[inline]
#[must_use]
pub fn atan(x: f32) -> f32 {
let ix = x.to_bits() & 0x7fff_ffff;
if ix >= 0x4c80_0000 {
if x.is_nan() {
return f32::NAN;
}
return if x > 0.0 {
ATANHI_F[3] + ATANLO_F[3]
} else {
-ATANHI_F[3] - ATANLO_F[3]
};
}
let (id, t): (i32, f32) = if ix < 0x3ee0_0000 {
if ix < 0x3980_0000 {
return x;
}
(-1, x)
} else {
let ax = x.abs();
if ix < 0x3f98_0000 {
if ix < 0x3f30_0000 {
(0, (2.0 * ax - 1.0) / (2.0 + ax))
} else {
(1, (ax - 1.0) / (ax + 1.0))
}
} else if ix < 0x401c_0000 {
(2, (ax - 1.5) / (1.0 + 1.5 * ax))
} else {
(3, -1.0 / ax)
}
};
let z = t * t;
let w = z * z;
let s1 = z * (AT_F[0] + w * (AT_F[2] + w * AT_F[4]));
let s2 = w * (AT_F[1] + w * AT_F[3]);
if id < 0 {
return t - t * (s1 + s2);
}
let i = id as usize;
let r = ATANHI_F[i] - ((t * (s1 + s2) - ATANLO_F[i]) - t);
if x < 0.0 {
-r
} else {
r
}
}
const PI_F: f32 = core::f32::consts::PI;
const PI_LO_F: f32 = -8.742_277_657_3e-08;
const PIO2_HI_F: f32 = core::f32::consts::FRAC_PI_2;
const PIO4_HI_F: f32 = core::f32::consts::FRAC_PI_4;
#[inline]
#[must_use]
pub fn atan2(y: f32, x: f32) -> f32 {
if x.is_nan() || y.is_nan() {
return f32::NAN;
}
if x == 1.0 {
return atan(y);
}
let m = u32::from(y.is_sign_negative()) | (u32::from(x.is_sign_negative()) << 1);
if y == 0.0 {
return match m {
0 | 1 => y, 2 => PI_F, _ => -PI_F, };
}
if x == 0.0 {
return if y < 0.0 { -PIO2_HI_F } else { PIO2_HI_F };
}
if x.is_infinite() {
if y.is_infinite() {
return match m {
0 => PIO4_HI_F,
1 => -PIO4_HI_F,
2 => 3.0 * PIO4_HI_F,
_ => -3.0 * PIO4_HI_F,
};
}
return match m {
0 => 0.0,
1 => -0.0,
2 => PI_F,
_ => -PI_F,
};
}
if y.is_infinite() {
return if y < 0.0 { -PIO2_HI_F } else { PIO2_HI_F };
}
let ex = ((x.to_bits() >> 23) & 0xff) as i32;
let ey = ((y.to_bits() >> 23) & 0xff) as i32;
let k = ey - ex;
let z = if k > 26 {
PIO2_HI_F + 0.5 * PI_LO_F
} else if x < 0.0 && k < -26 {
0.0
} else {
atan((y / x).abs())
};
match m {
0 => z,
1 => -z,
2 => PI_F - (z - PI_LO_F),
_ => (z - PI_LO_F) - PI_F,
}
}
#[inline]
#[must_use]
pub fn asin(x: f32) -> f32 {
asin64(f64::from(x)) as f32
}
#[inline]
#[must_use]
pub fn acos(x: f32) -> f32 {
acos64(f64::from(x)) as f32
}
#[inline]
#[must_use]
pub fn tan(x: f32) -> f32 {
if !x.is_finite() {
return f32::NAN;
}
if x == 0.0 {
return x; }
let (k, r) = reduce_pio2_64(f64::from(x));
let (s, c) = (k_sin64(r), k_cos64(r));
let t = if k & 1 == 0 { s / c } else { -c / s };
canon(t as f32)
}
#[inline]
#[must_use]
pub fn tanh(x: f32) -> f32 {
if x.is_nan() {
return f32::NAN;
}
let ax = x.abs();
if ax < 6.103_515_625e-5 {
return x;
}
if ax > 10.0 {
return if x < 0.0 { -1.0 } else { 1.0 };
}
let e = exp64(2.0 * f64::from(ax));
let t = ((e - 1.0) / (e + 1.0)) as f32;
if x < 0.0 {
-t
} else {
t
}
}
#[cfg(test)]
mod two_prod_tests {
use super::{two_prod, PIO2_3};
fn exact(a: f32) {
let (p, e) = two_prod(a, PIO2_3);
let want = f64::from(a) * f64::from(PIO2_3);
assert_eq!(f64::from(p) + f64::from(e), want, "k = {a}");
assert_eq!(p, a * PIO2_3, "k = {a}");
}
#[test]
fn k_times_pio2_3_is_split_exactly() {
let mut n = 0;
for k in -(1 << 14)..=(1 << 14) {
exact(k as f32);
n += 1;
}
for k in ((1 << 14)..=(1 << 24)).step_by(4093) {
exact(k as f32);
exact(-(k as f32));
n += 2;
}
for k in [f32::MAX, -f32::MAX, 2.0e38, 1.0e35, 8.3e34] {
let (p, e) = two_prod(k, PIO2_3);
assert!(p.is_finite() && e.is_finite(), "k = {k}: {p} {e}");
n += 1;
}
assert!(n > 1 << 15);
}
}