injective_math/fp_decimal/
trigonometry.rs

1use crate::fp_decimal::FPDecimal;
2
3impl FPDecimal {
4    pub(self) fn _cos(mut x: FPDecimal) -> FPDecimal {
5        x = FPDecimal::_change_range(x);
6        FPDecimal::_sin(FPDecimal::PI / FPDecimal::TWO - x)
7    }
8
9    fn _sine_taylor_expansion(x: FPDecimal) -> FPDecimal {
10        x - (x.pow(FPDecimal::THREE).unwrap() / FPDecimal::THREE.factorial()) + (x.pow(FPDecimal::FIVE).unwrap() / FPDecimal::FIVE.factorial())
11            - (x.pow(FPDecimal::SEVEN).unwrap() / FPDecimal::SEVEN.factorial())
12            + (x.pow(FPDecimal::NINE).unwrap() / FPDecimal::NINE.factorial())
13            - (x.pow(FPDecimal::ELEVEN).unwrap() / (FPDecimal::ELEVEN).factorial())
14            + (x.pow(FPDecimal::THREE + FPDecimal::TEN).unwrap() / (FPDecimal::THREE + FPDecimal::TEN).factorial())
15    }
16
17    pub(self) fn _sin(mut x: FPDecimal) -> FPDecimal {
18        x = FPDecimal::_change_range(x);
19        let pi_by_2 = FPDecimal::PI / FPDecimal::TWO;
20        let pi_plus_pi_by_2 = FPDecimal::PI + FPDecimal::PI / FPDecimal::TWO;
21
22        if (FPDecimal::ZERO == x) || (FPDecimal::PI == x) {
23            return FPDecimal::ZERO;
24        }
25
26        if pi_by_2 == x {
27            return FPDecimal::ONE;
28        }
29
30        if pi_plus_pi_by_2 == x {
31            return FPDecimal::ZERO - FPDecimal::ONE;
32        }
33
34        if FPDecimal::ZERO < x && x < pi_by_2 {
35            return FPDecimal::_sine_taylor_expansion(x);
36        }
37
38        if pi_by_2 < x && x < FPDecimal::PI {
39            return FPDecimal::_sine_taylor_expansion(FPDecimal::PI - x);
40        }
41
42        if FPDecimal::PI < x && x < pi_plus_pi_by_2 {
43            let mut output = FPDecimal::_sine_taylor_expansion(x - FPDecimal::PI);
44            output.sign = 0;
45            return output;
46        }
47
48        let mut output = FPDecimal::_sine_taylor_expansion(FPDecimal::PI * FPDecimal::TWO - x);
49        output.sign = 0;
50        output
51    }
52
53    fn _change_range(x: FPDecimal) -> FPDecimal {
54        if x.is_zero() {
55            return x;
56        }
57        let mut output = x;
58        let two_pi = FPDecimal::PI * FPDecimal::TWO;
59        match x < FPDecimal::ZERO {
60            true => {
61                while output < FPDecimal::ZERO {
62                    output += two_pi;
63                }
64            }
65            false => {
66                while output > two_pi {
67                    output -= two_pi;
68                }
69            }
70        }
71        output
72    }
73
74    pub fn imprecise_cos(&self) -> FPDecimal {
75        FPDecimal::_cos(*self)
76    }
77
78    pub fn imprecise_sin(&self) -> FPDecimal {
79        FPDecimal::_sin(*self)
80    }
81}
82
83#[cfg(test)]
84mod tests {
85    use crate::FPDecimal;
86    use std::str::FromStr;
87    fn almost_eq(x: FPDecimal, target: FPDecimal) {
88        assert!(((x - target) / x).abs() <= FPDecimal::from_str("0.01").unwrap());
89    }
90
91    #[test]
92    fn test_cosine_zero() {
93        assert_eq!(FPDecimal::ZERO.imprecise_cos(), FPDecimal::ONE);
94    }
95
96    #[test]
97    fn test_cosine_one() {
98        almost_eq(FPDecimal::ONE.imprecise_cos(), FPDecimal::from_str("0.54030230586").unwrap());
99    }
100
101    #[test]
102    fn test_cosine_negative_one() {
103        almost_eq(
104            (FPDecimal::ZERO - FPDecimal::ONE).imprecise_cos(),
105            FPDecimal::from_str("0.54030230586").unwrap(),
106        );
107    }
108
109    #[test]
110    fn test_sine_zero() {
111        assert_eq!(FPDecimal::ZERO.imprecise_sin(), FPDecimal::ZERO);
112    }
113
114    #[test]
115    fn test_sine_one() {
116        //0.84147098480789650666
117        almost_eq(FPDecimal::ONE.imprecise_sin(), FPDecimal::from_str("0.8414709848").unwrap());
118    }
119
120    #[test]
121    fn test_sine_negative_one() {
122        almost_eq((-FPDecimal::ONE).imprecise_sin(), FPDecimal::from_str("-0.8414709848").unwrap());
123    }
124}