trig_const/
lib.rs

1#![doc = include_str!("../README.md")]
2#![no_std]
3#![forbid(unsafe_code)]
4#![allow(clippy::excessive_precision)]
5
6mod acos;
7mod acosh;
8mod asin;
9mod asinh;
10mod atan;
11mod atan2;
12mod atanh;
13mod cos;
14mod exp;
15mod floor;
16mod k_cos;
17mod k_sin;
18pub(crate) mod k_tan;
19mod ln;
20pub(crate) mod log1p;
21mod pow;
22mod rem_pio2;
23mod rem_pio2_large;
24pub(crate) mod scalbn;
25mod sin;
26mod tan;
27pub use acos::acos;
28pub use acosh::acosh;
29pub use asin::asin;
30pub use asinh::asinh;
31pub use atan::atan;
32pub use atan2::atan2;
33pub use atanh::atanh;
34pub use cos::cos;
35pub use exp::exp;
36pub use floor::floor;
37pub use ln::ln;
38pub use pow::pow;
39pub use sin::sin;
40pub use tan::tan;
41
42/// Number of sum iterations for Taylor series
43const TAYLOR_SERIES_SUMS: usize = 16;
44
45/// Cotangent
46///
47/// ```
48/// # use trig_const::cot;
49/// # use core::f64::consts::PI;
50/// # fn float_eq(lhs: f64, rhs: f64) { assert!((lhs - rhs).abs() < 0.0001, "lhs: {}, rhs: {}", lhs, rhs); }
51/// const COT_PI_4: f64 = cot(PI / 4.0);
52/// float_eq(COT_PI_4, 1.0);
53/// ```
54pub const fn cot(x: f64) -> f64 {
55    let sin_calc = sin(x);
56    if sin_calc == 0.0 {
57        f64::INFINITY
58    } else {
59        cos(x) / sin_calc
60    }
61}
62
63/// Secant
64///
65/// ```
66/// # use trig_const::sec;
67/// # use core::f64::consts::PI;
68/// const SEC_PI: f64 = sec(PI);
69/// assert_eq!(SEC_PI, -1.0);
70/// ```
71pub const fn sec(x: f64) -> f64 {
72    let cos_calc = cos(x);
73    if cos_calc == 0.0 {
74        f64::INFINITY
75    } else {
76        1.0 / cos_calc
77    }
78}
79
80/// Cosecant
81///
82/// ```
83/// # use trig_const::csc;
84/// # use core::f64::consts::PI;
85/// const CSC_PI_2: f64 = csc(PI / 2.0);
86/// assert_eq!(CSC_PI_2, 1.0);
87/// ```
88pub const fn csc(x: f64) -> f64 {
89    let sin_calc = sin(x);
90    if sin_calc == 0.0 {
91        f64::INFINITY
92    } else {
93        1.0 / sin_calc
94    }
95}
96
97/// Hyperbolic Sine
98///
99/// ```
100/// # use trig_const::sinh;
101/// const SINH_0: f64 = sinh(0.0);
102/// assert_eq!(SINH_0, 0.0);
103/// ```
104pub const fn sinh(x: f64) -> f64 {
105    (exp(x) - exp(-x)) / 2.0
106}
107
108/// Hyperbolic Cosine
109///
110/// ```
111/// # use trig_const::cosh;
112/// const COSH_0: f64 = cosh(0.0);
113/// assert_eq!(COSH_0, 1.0);
114/// ```
115pub const fn cosh(x: f64) -> f64 {
116    (exp(x) + exp(-x)) / 2.0
117}
118
119/// x^pow
120pub const fn expi(x: f64, mut pow: isize) -> f64 {
121    let mut o = 1.0;
122
123    while pow > 0 {
124        o *= x;
125        pow -= 1;
126    }
127    while pow < 0 {
128        o /= x;
129        pow += 1;
130    }
131
132    o
133}
134
135/// Factorial (x!)
136pub const fn factorial(mut x: f64) -> f64 {
137    if x == 0.0 || x == 1.0 {
138        1.0
139    } else {
140        let mut s = 1.0;
141        while x > 1.0 {
142            s *= x;
143            x -= 1.0;
144        }
145        s
146    }
147}
148
149/// Const sqrt function using Newton's method
150pub const fn sqrt(x: f64) -> f64 {
151    if x.is_nan() || x < 0.0 {
152        return f64::NAN;
153    } else if x.is_infinite() || x == 0.0 {
154        return x;
155    }
156
157    // Use Newton's method for sqrt calculation
158    let mut current_guess = 1.0;
159
160    let mut i = 0;
161    while i < TAYLOR_SERIES_SUMS {
162        current_guess = 0.5 * (current_guess + x / current_guess);
163        i += 1;
164    }
165
166    current_guess
167}
168
169pub const fn fabs(x: f64) -> f64 {
170    if x > 0.0 {
171        x
172    } else {
173        -x
174    }
175}
176
177const fn with_set_high_word(f: f64, hi: u32) -> f64 {
178    let mut tmp = f.to_bits();
179    tmp &= 0x00000000_ffffffff;
180    tmp |= (hi as u64) << 32;
181    f64::from_bits(tmp)
182}
183const fn with_set_low_word(f: f64, lo: u32) -> f64 {
184    let mut tmp = f.to_bits();
185    tmp &= 0xffffffff_00000000;
186    tmp |= lo as u64;
187    f64::from_bits(tmp)
188}
189const fn get_high_word(x: f64) -> u32 {
190    (x.to_bits() >> 32) as u32
191}
192
193const fn get_low_word(x: f64) -> u32 {
194    x.to_bits() as u32
195}
196
197#[cfg(test)]
198mod tests {
199    use core::f64::consts::{E, PI};
200
201    use crate::{cos, cosh, exp, expi, factorial, ln, sin, sinh, sqrt};
202
203    macro_rules! float_eq {
204        ($lhs:expr, $rhs:expr) => {
205            assert!(($lhs - $rhs).abs() < 0.0001, "lhs: {}, rhs: {}", $lhs, $rhs);
206        };
207    }
208
209    #[test]
210    fn test_factorial() {
211        assert_eq!(factorial(0.0), 1.0);
212        assert_eq!(factorial(1.0), 1.0);
213        assert_eq!(factorial(2.0), 2.0);
214        assert_eq!(factorial(3.0), 6.0);
215        assert_eq!(factorial(4.0), 24.0);
216        assert_eq!(factorial(5.0), 120.0);
217    }
218
219    #[test]
220    fn test_expi() {
221        assert_eq!(expi(2.0, 0), 1.0);
222        assert_eq!(expi(2.0, 4), 16.0);
223        assert_eq!(expi(2.0, 5), 32.0);
224        assert_eq!(expi(3.0, 3), 27.0);
225    }
226
227    #[test]
228    fn test_exp() {
229        float_eq!(exp(0.0), 1.0);
230        float_eq!(exp(1.0), E);
231    }
232
233    #[test]
234    fn test_sqrt() {
235        float_eq!(sqrt(0.0), 0.0);
236        float_eq!(sqrt(1.0), 1.0);
237        float_eq!(sqrt(4.0), 2.0);
238        float_eq!(sqrt(9.0), 3.0);
239        float_eq!(sqrt(16.0), 4.0);
240        float_eq!(sqrt(25.0), 5.0);
241    }
242
243    #[test]
244    fn test_cos() {
245        float_eq!(cos(0.0), 0.0_f64.cos());
246        float_eq!(cos(1.0), 1.0_f64.cos());
247        float_eq!(cos(PI), PI.cos());
248        float_eq!(cos(PI * 8.0), (PI * 8.0).cos());
249    }
250
251    #[test]
252    fn test_sin() {
253        float_eq!(sin(0.0), 0.0_f64.sin());
254        float_eq!(sin(1.0), 1.0_f64.sin());
255        float_eq!(sin(PI), PI.sin());
256        float_eq!(sin(PI * 8.0), (PI * 8.0).sin());
257    }
258
259    #[test]
260    fn test_sinh() {
261        for x in [0.0, 0.5, 1.0, 1.5, 2.0, 2.5] {
262            float_eq!(sinh(x), x.sinh());
263        }
264    }
265
266    #[test]
267    fn test_cosh() {
268        for x in [0.0, 0.5, 1.0, 1.5, 2.0, 2.5] {
269            float_eq!(cosh(x), x.cosh());
270        }
271    }
272
273    #[test]
274    fn test_ln() {
275        // float_eq!(ln(0.01), 0.01_f64.ln());
276        // float_eq!(ln(0.5), 0.5_f64.ln());
277        float_eq!(ln(1.0), 1.0_f64.ln());
278        float_eq!(ln(2.0), 2.0_f64.ln());
279        float_eq!(ln(10.0), 10.0_f64.ln());
280        float_eq!(ln(1_000.0), 1_000.0_f64.ln());
281    }
282}