1use crate::{Float, buffer::FloatWidth, facts::Repair, float::Native, recipe::{NativeOps, Ops}};
3use std::f64::consts::FRAC_1_PI;
4
5#[derive(Clone, Copy, Debug, PartialEq)]
6pub(crate) enum Kind { Sin, Cos, Tan }
7
8impl Kind {
9 #[inline]
10 fn scale(self) -> f64 { if self == Self::Tan { 2.0 * FRAC_1_PI } else { FRAC_1_PI } }
11 #[inline]
12 fn offset(self) -> f64 { if self == Self::Cos { 0.5 } else { 0.0 } }
13 #[inline]
14 pub(crate) fn predicate<F: Float, O: Ops<F> + ?Sized>(self, o: &O, x: O::V) -> O::B {
15 let a = o.magnitude(x);
16 let limit = o.number(F::narrow(if o.width() == FloatWidth::F64 { 8388608.0 } else { 1048576.0 }));
17 o.and(o.less(limit, a), o.less(a, o.number(F::infinity())))
18 }
19 pub(crate) fn routine(self) -> Repair {
20 match self {
21 Self::Sin => Repair { single: sin_single, double: sin_double }, Self::Cos => Repair { single: cos_single, double: cos_double },
22 Self::Tan => Repair { single: tan_single, double: tan_double }
23 }
24 }
25}
26
27extern "C" fn sin_single(x: f32) -> f32 { repair::<{ crate::FUSED }>(x as f64, Kind::Sin) as f32 }
28extern "C" fn sin_double(x: f64) -> f64 { repair::<{ crate::FUSED }>(x, Kind::Sin) }
29extern "C" fn cos_single(x: f32) -> f32 { repair::<{ crate::FUSED }>(x as f64, Kind::Cos) as f32 }
30extern "C" fn cos_double(x: f64) -> f64 { repair::<{ crate::FUSED }>(x, Kind::Cos) }
31extern "C" fn tan_single(x: f32) -> f32 { repair::<{ crate::FUSED }>(x as f64, Kind::Tan) as f32 }
32extern "C" fn tan_double(x: f64) -> f64 { repair::<{ crate::FUSED }>(x, Kind::Tan) }
33
34#[derive(Clone, Copy, Debug)]
35pub struct Sin;
36#[derive(Clone, Copy, Debug)]
37pub struct Cos;
38#[derive(Clone, Copy, Debug)]
39pub struct Tan;
40
41#[inline]
42fn scalar<R: Native, const FUSED: bool>(x: R, kind: Kind) -> R {
43 let o = NativeOps::<FUSED>::DEFAULT;
44 if kind.predicate(&o, x) { R::narrow(repair::<FUSED>(x.wide(), kind)) } else { formula(&o, x, kind) }
45}
46#[inline]
47pub(crate) fn sin<R: Native, const FUSED: bool>(x: R) -> R { scalar::<R, FUSED>(x, Kind::Sin) }
48#[inline]
49pub(crate) fn cos<R: Native, const FUSED: bool>(x: R) -> R { scalar::<R, FUSED>(x, Kind::Cos) }
50#[inline]
51pub(crate) fn tan<R: Native, const FUSED: bool>(x: R) -> R { scalar::<R, FUSED>(x, Kind::Tan) }
52
53#[inline]
54pub(crate) fn formula<F: Float, O: Ops<F> + ?Sized>(o: &O, x: O::V, kind: Kind) -> O::V {
55 let zero = o.number(F::zero());
56 let finite = o.less(o.magnitude(x), o.number(F::infinity()));
57 let safe = o.select(o.and(finite, o.not(kind.predicate(o, x))), x, zero);
58 let (r, n) = if o.fused() { reduce_fused(o, safe, kind) } else { reduce_split(o, safe, kind) };
59 let (half, shift) = (o.multiply(n, o.number(F::narrow(0.5))), crate::recipe::rounding_shift(o));
61 let even = o.subtract(o.add(half, shift), shift);
62 let odd = o.not(o.equal(n, o.add(even, even)));
63 let y = if kind == Kind::Tan { tangent(o, r, odd) } else {
64 let y = sine(o, r);
65 let y = o.select(odd, o.negate(y), y);
66 o.select(o.equal(x, zero), if kind == Kind::Cos { o.number(F::one()) } else { x }, y)
67 };
68 o.select(finite, y, o.number(F::nan()))
69}
70
71const PI_FUSED_64: [f64; 3] = [3.141592653589793, 1.224646799147353e-16, 2.165713347843828e-32];
72const PI_FUSED_32: [f64; 3] = [3.141592502593994, 1.5099578831723193e-7, 1.0780605906948477e-14];
73const PI_SPLIT_64: [f64; 4] = [3.141592651605606, 1.9841871583270443e-9, 1.03403659577805e-18, 5.807711947958721e-28];
74const PI_SPLIT_32: [f64; 2] = [3.141592651605606, 1.984187159361081e-9];
75
76#[inline]
77fn reduce_fused<F: Float, O: Ops<F> + ?Sized>(o: &O, x: O::V, kind: Kind) -> (O::V, O::V) {
78 let shift = crate::recipe::rounding_shift(o);
79 let z = if kind.offset() == 0.0 { o.multiply_add(x, o.number(F::narrow(kind.scale())), shift) }
80 else { o.add(o.multiply_add(x, o.number(F::narrow(kind.scale())), o.number(F::narrow(kind.offset()))), shift) };
81 let n = o.subtract(z, shift);
82 let m = if kind == Kind::Tan { o.multiply(n, o.number(F::narrow(0.5))) } else { o.subtract(n, o.number(F::narrow(kind.offset()))) };
83 let mut r = x;
84 for p in if o.width() == FloatWidth::F64 { PI_FUSED_64 } else { PI_FUSED_32 } { r = o.multiply_add(m, o.number(F::narrow(-p)), r); }
85 (r, n)
86}
87
88#[inline]
89fn reduce_split<F: Float, O: Ops<F> + ?Sized>(o: &O, x: O::V, kind: Kind) -> (O::V, O::V) {
90 let wide = o.widen(x);
91 let z = o.wide_multiply_add(wide, o.wide_number(kind.scale()), o.wide_number(kind.offset()));
92 let z = o.wide_multiply_add(z, o.wide_number(1.0), o.wide_number(6755399441055744.0));
93 let n = o.wide_multiply_add(z, o.wide_number(1.0), o.wide_number(-6755399441055744.0));
94 let m = if kind == Kind::Tan { o.wide_multiply_add(n, o.wide_number(0.5), o.wide_number(0.0)) }
95 else { o.wide_multiply_add(n, o.wide_number(1.0), o.wide_number(-kind.offset())) };
96 let mut r = wide;
97 let parts: &[f64] = if o.width() == FloatWidth::F64 { &PI_SPLIT_64 } else { &PI_SPLIT_32 };
98 for &p in parts { r = o.wide_multiply_add(m, o.wide_number(-p), r); }
99 (o.narrow(r), o.narrow(n))
100}
101
102const SIN_64: [f64; 7] = [
103 -7.364473665132213e-13, 1.604733546032205e-10, -2.505185490163253e-8, 2.755731618671281e-6, -0.0001984126982169595, 0.008333333333273485,
104 -0.1666666666666606,
105];
106const SIN_32: [f64; 4] = [2.6105958568223286e-6, -0.00019811994570773095, 0.00833310279995203, -0.16666661202907562];
107#[inline]
108fn sine<F: Float, O: Ops<F> + ?Sized>(o: &O, r: O::V) -> O::V {
109 let r2 = o.multiply(r, r);
110 let p = crate::recipe::horner(o, r2, if o.width() == FloatWidth::F64 { &SIN_64 } else { &SIN_32 });
111 o.multiply_add(o.multiply(r, r2), p, r)
112}
113
114const TAN_64: [f64; 9] = [
115 0.0003287231044337369, 0.000559125460707824, 0.0014615495400569908, 0.0035914987665774814, 0.008863277952340431, 0.021869486815122453, 0.05396825400765754,
116 0.13333333333289177, 0.33333333333333504,
117];
118const TAN_32: [f64; 4] = [0.025565408170223236, 0.053473446518182755, 0.13335898518562317, 0.33333292603492737];
119#[inline]
120fn tangent<F: Float, O: Ops<F> + ?Sized>(o: &O, r: O::V, odd: O::B) -> O::V {
121 let u = o.multiply(r, o.number(F::narrow(0.5)));
122 let u2 = o.multiply(u, u);
123 let p = crate::recipe::horner(o, u2, if o.width() == FloatWidth::F64 { &TAN_64 } else { &TAN_32 });
124 let t = o.multiply_add(o.multiply(u, u2), p, u);
125 let below = o.multiply_add(t, t, o.number(-F::one()));
126 let twice = o.add(t, t);
127 o.divide(o.select(odd, below, o.negate(twice)), o.select(odd, twice, below))
128}
129
130#[inline(never)]
131fn repair<const FUSED: bool>(x: f64, kind: Kind) -> f64 {
132 let (r, odd) = exact(x, kind);
133 let o = NativeOps::<FUSED>::DEFAULT;
134 let y = if kind == Kind::Tan { tangent(&o, r, odd) } else { sine(&o, r) };
135 let negate = if kind == Kind::Tan { x < 0.0 } else { odd ^ (kind == Kind::Sin && x < 0.0) };
136 if negate { -y } else { y }
137}
138const INV_2PI: [u64; 20] = [
140 0x28be60db9391054a, 0x7f09d5f47d4d3770, 0x36d8a5664f10e410, 0x7f9458eaf7aef158, 0x6dc91b8e909374b8, 0x01924bba82746487, 0x3f877ac72c4a69cf,
141 0xba208d7d4baed121, 0x3a671c09ad17df90, 0x4e64758e60d4ce7d, 0x272117e2ef7e4a0e, 0xc7fe25fff7816603, 0xfbcbc462d6829b47, 0xdb4d9fb3c9f2c26d,
142 0xd3d18fd9a797fa8b, 0x5d49eeb1faf97c5e, 0xcf41ce7de294a4ba, 0x9afed7ec47e35742, 0x1580cc11bf1edaea, 0xfc33ef0826bd0d87,
143];
144const PI_FIXED: u128 = 0xc90fdaa22168c234c4c6628b80dc1cd1;
146
147fn exact(x: f64, kind: Kind) -> (f64, bool) {
149 let bits = x.abs().to_bits();
150 let (m, k) = ((bits & ((1 << 52) - 1)) | 1 << 52, (bits >> 52) as i64 - 1075);
151 let skip = k.max(0) as usize;
153 let word = |i: usize| ((((INV_2PI[i] as u128) << 64 | INV_2PI[i + 1] as u128) << (skip % 64)) >> 64) as u64;
154 let w = skip / 64;
155 let low = m as u128 * word(w + 2) as u128;
156 let mid = m as u128 * word(w + 1) as u128 + (low >> 64);
157 let high = m as u128 * word(w) as u128 + (mid >> 64);
158 let below = (-k).max(0) as u32;
160 let fraction = (((high as u64 as u128) << 64 | mid as u64 as u128) >> below) | (high >> 64).checked_shl(128 - below).unwrap_or(0);
161 let z = if kind == Kind::Tan { fraction << 1 } else { fraction };
163 let t = z.wrapping_add(if kind == Kind::Cos { 1 << 126 } else { 0 }).wrapping_add(1 << 126);
164 let d = (t & ((1 << 127) - 1)) as i128 - (1 << 126);
165 let (a, b) = (d.unsigned_abs(), PI_FIXED);
167 let product = (a >> 64) * (b >> 64) + ((a >> 64) * (b as u64 as u128) >> 64) + ((a as u64 as u128) * (b >> 64) >> 64);
168 let scale = if kind == Kind::Tan { 2f64.powi(-126) } else { 2f64.powi(-125) };
169 ((product as f64 * scale).copysign(d as f64), t >> 127 == 1)
170}