trig_const/
lib.rs

1#![doc = include_str!("../README.md")]
2#![no_std]
3#![forbid(unsafe_code)]
4
5mod atan;
6mod atan2;
7mod ln;
8mod pow;
9pub use atan::atan;
10pub use atan2::atan2;
11pub use ln::ln;
12pub use pow::pow;
13
14use core::f64::{
15    self,
16    consts::{PI, TAU},
17};
18
19/// Number of sum iterations for Taylor series
20const TAYLOR_SERIES_SUMS: usize = 16;
21
22/// Cosine
23///
24/// ```
25/// # use trig_const::cos;
26/// # use core::f64::consts::PI;
27/// # fn float_eq(lhs: f64, rhs: f64) { assert!((lhs - rhs).abs() < 0.0001, "lhs: {}, rhs: {}", lhs, rhs); }
28/// const COS_PI: f64 = cos(PI);
29/// float_eq(COS_PI, -1.0);
30/// ```
31pub const fn cos(mut x: f64) -> f64 {
32    // If value is large, fold into smaller value
33    while x < TAU {
34        x += TAU;
35    }
36    while x > TAU {
37        x -= TAU;
38    }
39    let div = (x / PI) as u32;
40    x -= div as f64 * PI;
41    let sign = if div % 2 != 0 { -1.0 } else { 1.0 };
42
43    let mut result = 1.0;
44    let mut inter = 1.0;
45    let num = x * x;
46
47    let mut i = 1;
48    while i <= TAYLOR_SERIES_SUMS {
49        let comp = 2.0 * i as f64;
50        let den = comp * (comp - 1.0);
51        inter *= num / den;
52        if i % 2 == 0 {
53            result += inter;
54        } else {
55            result -= inter;
56        }
57        i += 1;
58    }
59
60    sign * result
61}
62
63/// Sine
64///
65/// ```
66/// # use trig_const::sin;
67/// # use core::f64::consts::PI;
68/// # fn float_eq(lhs: f64, rhs: f64) { assert!((lhs - rhs).abs() < 0.0001, "lhs: {}, rhs: {}", lhs, rhs); }
69/// const SIN_PI: f64 = sin(PI);
70/// float_eq(SIN_PI, 0.0);
71/// ```
72pub const fn sin(x: f64) -> f64 {
73    cos(x - PI / 2.0)
74}
75
76/// Tangent
77///
78/// ```
79/// # use trig_const::tan;
80/// # use core::f64::consts::PI;
81/// # fn float_eq(lhs: f64, rhs: f64) { assert!((lhs - rhs).abs() < 0.0001, "lhs: {}, rhs: {}", lhs, rhs); }
82/// const TAN_PI_4: f64 = tan(PI / 4.0);
83/// float_eq(TAN_PI_4, 1.0);
84/// ```
85pub const fn tan(x: f64) -> f64 {
86    sin(x) / cos(x)
87}
88
89/// Cotangent
90///
91/// ```
92/// # use trig_const::cot;
93/// # use core::f64::consts::PI;
94/// # fn float_eq(lhs: f64, rhs: f64) { assert!((lhs - rhs).abs() < 0.0001, "lhs: {}, rhs: {}", lhs, rhs); }
95/// const COT_PI_4: f64 = cot(PI / 4.0);
96/// float_eq(COT_PI_4, 1.0);
97/// ```
98pub const fn cot(x: f64) -> f64 {
99    let sin_calc = sin(x);
100    if sin_calc == 0.0 {
101        f64::INFINITY
102    } else {
103        cos(x) / sin_calc
104    }
105}
106
107/// Secant
108///
109/// ```
110/// # use trig_const::sec;
111/// # use core::f64::consts::PI;
112/// # fn float_eq(lhs: f64, rhs: f64) { assert!((lhs - rhs).abs() < 0.0001, "lhs: {}, rhs: {}", lhs, rhs); }
113/// const SEC_PI: f64 = sec(PI);
114/// float_eq(SEC_PI, -1.0);
115/// ```
116pub const fn sec(x: f64) -> f64 {
117    let cos_calc = cos(x);
118    if cos_calc == 0.0 {
119        f64::INFINITY
120    } else {
121        1.0 / cos_calc
122    }
123}
124
125/// Cosecant
126///
127/// ```
128/// # use trig_const::csc;
129/// # use core::f64::consts::PI;
130/// # fn float_eq(lhs: f64, rhs: f64) { assert!((lhs - rhs).abs() < 0.0001, "lhs: {}, rhs: {}", lhs, rhs); }
131/// const CSC_PI_2: f64 = csc(PI / 2.0);
132/// float_eq(CSC_PI_2, 1.0);
133/// ```
134pub const fn csc(x: f64) -> f64 {
135    let sin_calc = sin(x);
136    if sin_calc == 0.0 {
137        f64::INFINITY
138    } else {
139        1.0 / sin_calc
140    }
141}
142
143/// Hyperbolic Sine
144///
145/// ```
146/// # use trig_const::sinh;
147/// const SINH_0: f64 = sinh(0.0);
148/// assert_eq!(SINH_0, 0.0);
149/// ```
150pub const fn sinh(x: f64) -> f64 {
151    (exp(x) - exp(-x)) / 2.0
152}
153
154/// Hyperbolic Cosine
155///
156/// ```
157/// # use trig_const::cosh;
158/// const COSH_0: f64 = cosh(0.0);
159/// assert_eq!(COSH_0, 1.0);
160/// ```
161pub const fn cosh(x: f64) -> f64 {
162    (exp(x) + exp(-x)) / 2.0
163}
164
165/// Arcsine
166///
167/// ```
168/// # use trig_const::asin;
169/// const ASIN_PI: f64 = asin(0.0);
170/// assert_eq!(ASIN_PI, 0.0);
171/// ```
172pub const fn asin(x: f64) -> f64 {
173    if x.is_infinite() || x.abs() > 1.0 {
174        return f64::NAN;
175    } else if x == 1.0 {
176        return f64::consts::FRAC_PI_2;
177    } else if x == -1.0 {
178        return -f64::consts::FRAC_PI_2;
179    }
180
181    // As we start to get past 0.8, the number of summations needed for an accurate
182    // Taylor series approximation starts to get unweidy. We can use the property
183    // that arcsin(x) = pi/2 - 2*arcsin(sqrt((1 - x) / 2)) to reduce
184    const RANGE_REDUCTION_THRESHOLD: f64 = 0.5;
185    if x.abs() > RANGE_REDUCTION_THRESHOLD {
186        let sign = x.signum();
187        let abs_x = x.abs();
188
189        let y = sqrt((1.0 - abs_x) / 2.0);
190        return sign * (f64::consts::FRAC_PI_2 - 2.0 * asin(y));
191    }
192
193    let mut n = 1;
194    let mut s = x;
195
196    while n < TAYLOR_SERIES_SUMS {
197        let numer1 = factorial(2.0 * n as f64);
198        let numer2 = expi(x, 2 * n as isize + 1);
199
200        // Calculate all denom terms;
201        let denom1 = expi(4.0, n as isize);
202        let denom2 = factorial(n as f64) * factorial(n as f64);
203        let denom3 = 2.0 * n as f64 + 1.0;
204
205        // Try to match terms to divide to stop number getting too large
206        let f1 = numer1 / denom2;
207        let f2 = numer2 / denom1;
208
209        s += f1 * f2 / denom3;
210
211        n += 1;
212    }
213
214    s
215}
216
217/// Arccosine
218///
219/// ```
220/// # use trig_const::acos;
221/// # use core::f64::consts::PI;
222/// const ACOS_1: f64 = acos(1.0);
223/// assert_eq!(ACOS_1, 0.0);
224/// ```
225pub const fn acos(x: f64) -> f64 {
226    if x.is_infinite() || x.abs() > 1.0 {
227        f64::NAN
228    } else {
229        f64::consts::FRAC_PI_2 - asin(x)
230    }
231}
232
233/// Inverse hyperbolic sine
234///
235/// ```
236/// # use trig_const::asinh;
237/// const ASINH_1: f64 = asinh(0.0);
238/// assert_eq!(ASINH_1, 0.0);
239/// ```
240pub const fn asinh(x: f64) -> f64 {
241    if x.is_nan() {
242        f64::NAN
243    } else if x.is_infinite() {
244        x
245    } else {
246        ln(x + sqrt(x * x + 1.0))
247    }
248}
249
250/// Inverse hyperbolic cosine
251///
252/// ```
253/// # use trig_const::acosh;
254/// const ACOSH_1: f64 = acosh(1.0);
255/// assert_eq!(ACOSH_1, 0.0);
256/// ```
257pub const fn acosh(x: f64) -> f64 {
258    if x.is_nan() {
259        f64::NAN
260    } else if x.is_infinite() {
261        x
262    } else if x < 1.0 {
263        f64::NAN
264    } else {
265        ln(x + sqrt(x * x - 1.0))
266    }
267}
268
269/// e^x
270pub const fn exp(x: f64) -> f64 {
271    let mut i = 1;
272    let mut s = 1.0;
273
274    while i < 16 {
275        s += expi(x, i) / factorial(i as f64);
276        i += 1;
277    }
278
279    s
280}
281
282/// x^pow
283pub const fn expi(x: f64, mut pow: isize) -> f64 {
284    let mut o = 1.0;
285
286    while pow > 0 {
287        o *= x;
288        pow -= 1;
289    }
290    while pow < 0 {
291        o /= x;
292        pow += 1;
293    }
294
295    o
296}
297
298/// Factorial (x!)
299pub const fn factorial(mut x: f64) -> f64 {
300    if x == 0.0 || x == 1.0 {
301        1.0
302    } else {
303        let mut s = 1.0;
304        while x > 1.0 {
305            s *= x;
306            x -= 1.0;
307        }
308        s
309    }
310}
311
312/// Const sqrt function using Newton's method
313pub const fn sqrt(x: f64) -> f64 {
314    if x.is_nan() || x < 0.0 {
315        return f64::NAN;
316    } else if x.is_infinite() || x == 0.0 {
317        return x;
318    }
319
320    // Use Newton's method for sqrt calculation
321    let mut current_guess = 1.0;
322
323    let mut i = 0;
324    while i < TAYLOR_SERIES_SUMS {
325        current_guess = 0.5 * (current_guess + x / current_guess);
326        i += 1;
327    }
328
329    current_guess
330}
331
332pub const fn fabs(x: f64) -> f64 {
333    if x > 0.0 {
334        x
335    } else {
336        -x
337    }
338}
339
340#[cfg(test)]
341mod tests {
342    use core::f64::consts::{E, PI};
343
344    use crate::{cos, cosh, exp, expi, factorial, ln, sin, sinh, sqrt};
345
346    macro_rules! float_eq {
347        ($lhs:expr, $rhs:expr) => {
348            assert!(($lhs - $rhs).abs() < 0.0001, "lhs: {}, rhs: {}", $lhs, $rhs);
349        };
350    }
351
352    #[test]
353    fn test_factorial() {
354        assert_eq!(factorial(0.0), 1.0);
355        assert_eq!(factorial(1.0), 1.0);
356        assert_eq!(factorial(2.0), 2.0);
357        assert_eq!(factorial(3.0), 6.0);
358        assert_eq!(factorial(4.0), 24.0);
359        assert_eq!(factorial(5.0), 120.0);
360    }
361
362    #[test]
363    fn test_expi() {
364        assert_eq!(expi(2.0, 0), 1.0);
365        assert_eq!(expi(2.0, 4), 16.0);
366        assert_eq!(expi(2.0, 5), 32.0);
367        assert_eq!(expi(3.0, 3), 27.0);
368    }
369
370    #[test]
371    fn test_exp() {
372        float_eq!(exp(0.0), 1.0);
373        float_eq!(exp(1.0), E);
374    }
375
376    #[test]
377    fn test_sqrt() {
378        float_eq!(sqrt(0.0), 0.0);
379        float_eq!(sqrt(1.0), 1.0);
380        float_eq!(sqrt(4.0), 2.0);
381        float_eq!(sqrt(9.0), 3.0);
382        float_eq!(sqrt(16.0), 4.0);
383        float_eq!(sqrt(25.0), 5.0);
384    }
385
386    #[test]
387    fn test_cos() {
388        float_eq!(cos(0.0), 0.0_f64.cos());
389        float_eq!(cos(1.0), 1.0_f64.cos());
390        float_eq!(cos(PI), PI.cos());
391        float_eq!(cos(PI * 8.0), (PI * 8.0).cos());
392    }
393
394    #[test]
395    fn test_sin() {
396        float_eq!(sin(0.0), 0.0_f64.sin());
397        float_eq!(sin(1.0), 1.0_f64.sin());
398        float_eq!(sin(PI), PI.sin());
399        float_eq!(sin(PI * 8.0), (PI * 8.0).sin());
400    }
401
402    #[test]
403    fn test_sinh() {
404        for x in [0.0, 0.5, 1.0, 1.5, 2.0, 2.5] {
405            float_eq!(sinh(x), x.sinh());
406        }
407    }
408
409    #[test]
410    fn test_cosh() {
411        for x in [0.0, 0.5, 1.0, 1.5, 2.0, 2.5] {
412            float_eq!(cosh(x), x.cosh());
413        }
414    }
415
416    #[test]
417    fn test_ln() {
418        // float_eq!(ln(0.01), 0.01_f64.ln());
419        // float_eq!(ln(0.5), 0.5_f64.ln());
420        float_eq!(ln(1.0), 1.0_f64.ln());
421        float_eq!(ln(2.0), 2.0_f64.ln());
422        float_eq!(ln(10.0), 10.0_f64.ln());
423        float_eq!(ln(1_000.0), 1_000.0_f64.ln());
424    }
425}