1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
use crate::fp_decimal::FPDecimal;

impl FPDecimal {
    pub(self) fn _cos(mut x: FPDecimal) -> FPDecimal {
        x = FPDecimal::_change_range(x);
        FPDecimal::_sin(FPDecimal::PI / FPDecimal::TWO - x)
    }

    fn _sine_taylor_expansion(x: FPDecimal) -> FPDecimal {
        x - (x.pow(FPDecimal::THREE).unwrap() / FPDecimal::THREE.factorial()) + (x.pow(FPDecimal::FIVE).unwrap() / FPDecimal::FIVE.factorial())
            - (x.pow(FPDecimal::SEVEN).unwrap() / FPDecimal::SEVEN.factorial())
            + (x.pow(FPDecimal::NINE).unwrap() / FPDecimal::NINE.factorial())
            - (x.pow(FPDecimal::ELEVEN).unwrap() / (FPDecimal::ELEVEN).factorial())
            + (x.pow(FPDecimal::THREE + FPDecimal::TEN).unwrap() / (FPDecimal::THREE + FPDecimal::TEN).factorial())
    }

    pub(self) fn _sin(mut x: FPDecimal) -> FPDecimal {
        x = FPDecimal::_change_range(x);
        let pi_by_2 = FPDecimal::PI / FPDecimal::TWO;
        let pi_plus_pi_by_2 = FPDecimal::PI + FPDecimal::PI / FPDecimal::TWO;

        if (FPDecimal::ZERO == x) || (FPDecimal::PI == x) {
            return FPDecimal::ZERO;
        }

        if pi_by_2 == x {
            return FPDecimal::ONE;
        }

        if pi_plus_pi_by_2 == x {
            return FPDecimal::ZERO - FPDecimal::ONE;
        }

        if FPDecimal::ZERO < x && x < pi_by_2 {
            return FPDecimal::_sine_taylor_expansion(x);
        }

        if pi_by_2 < x && x < FPDecimal::PI {
            return FPDecimal::_sine_taylor_expansion(FPDecimal::PI - x);
        }

        if FPDecimal::PI < x && x < pi_plus_pi_by_2 {
            let mut output = FPDecimal::_sine_taylor_expansion(x - FPDecimal::PI);
            output.sign = 0;
            return output;
        }

        let mut output = FPDecimal::_sine_taylor_expansion(FPDecimal::PI * FPDecimal::TWO - x);
        output.sign = 0;
        output
    }

    fn _change_range(x: FPDecimal) -> FPDecimal {
        if x.is_zero() {
            return x;
        }
        let mut output = x;
        let two_pi = FPDecimal::PI * FPDecimal::TWO;
        match x < FPDecimal::ZERO {
            true => {
                while output < FPDecimal::ZERO {
                    output += two_pi;
                }
            }
            false => {
                while output > two_pi {
                    output -= two_pi;
                }
            }
        }
        output
    }

    pub fn imprecise_cos(&self) -> FPDecimal {
        FPDecimal::_cos(*self)
    }

    pub fn imprecise_sin(&self) -> FPDecimal {
        FPDecimal::_sin(*self)
    }
}

#[cfg(test)]
mod tests {
    use crate::FPDecimal;
    use std::str::FromStr;
    fn almost_eq(x: FPDecimal, target: FPDecimal) {
        assert!(((x - target) / x).abs() <= FPDecimal::from_str("0.01").unwrap());
    }

    #[test]
    fn test_cosine_zero() {
        assert_eq!(FPDecimal::ZERO.imprecise_cos(), FPDecimal::ONE);
    }

    #[test]
    fn test_cosine_one() {
        almost_eq(FPDecimal::ONE.imprecise_cos(), FPDecimal::from_str("0.54030230586").unwrap());
    }

    #[test]
    fn test_cosine_negative_one() {
        almost_eq(
            (FPDecimal::ZERO - FPDecimal::ONE).imprecise_cos(),
            FPDecimal::from_str("0.54030230586").unwrap(),
        );
    }

    #[test]
    fn test_sine_zero() {
        assert_eq!(FPDecimal::ZERO.imprecise_sin(), FPDecimal::ZERO);
    }

    #[test]
    fn test_sine_one() {
        //0.84147098480789650666
        almost_eq(FPDecimal::ONE.imprecise_sin(), FPDecimal::from_str("0.8414709848").unwrap());
    }

    #[test]
    fn test_sine_negative_one() {
        almost_eq((-FPDecimal::ONE).imprecise_sin(), FPDecimal::from_str("-0.8414709848").unwrap());
    }
}