Skip to main content

scratch_test_value/math/
modulo.rs

1use std::ops::Rem;
2
3use crate::{QuirkSink, SNumber, quirks::SNumberToFloatQ};
4
5impl SNumber {
6    /// This mimics the JS remainder operator <https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Remainder>
7    pub fn q_modulo<Q>(&self, other: &SNumber, sink: &mut Q) -> SNumber
8    where
9        Q: QuirkSink<SNumberToFloatQ>,
10    {
11        let dividend = self.q_as_float_nan_is_zero(sink);
12        let divisor = other.q_as_float_nan_is_zero(sink);
13        let remainder = dividend.rem(divisor);
14
15        let diff_sign = (dividend < 0.0 && divisor > 0.0) || (dividend > 0.0 && divisor < 0.0);
16        SNumber::Float(
17            if diff_sign && (divisor != 1.0 && divisor != -1.0 && divisor.is_finite()) {
18                remainder + divisor
19            } else {
20                remainder
21            },
22        )
23    }
24    #[allow(unused)]
25    pub(crate) fn modulo(&self, other: &SNumber) -> SNumber {
26        self.q_modulo(other, &mut ())
27    }
28}
29
30#[cfg(test)]
31mod tests {
32    use std::ops::Sub;
33
34    use crate::SNumber;
35    const ZEROS: [SNumber; 3] = [SNumber::POS_ZERO, SNumber::NEG_ZERO, SNumber::Int(0)];
36
37    #[test]
38    fn test_normal() {
39        assert_eq!(SNumber::Int(1), SNumber::Int(101).modulo(&SNumber::Int(2)));
40    }
41
42    #[test]
43    fn test_floating() {
44        assert_eq!(
45            SNumber::Float(2.5),
46            SNumber::Float(7.5).modulo(&SNumber::Int(5))
47        );
48        assert_eq!(
49            SNumber::Int(2),
50            SNumber::Float(7.5).modulo(&SNumber::Float(5.5))
51        );
52
53        assert!(
54            SNumber::Float(12.25)
55                .modulo(&SNumber::Float(1.45))
56                .q_as_float(&mut ())
57                .sub(0.65)
58                .abs()
59                < 0.0001
60        );
61        assert!(
62            SNumber::Float(21.3)
63                .modulo(&SNumber::Float(0.7))
64                .q_as_float(&mut ())
65                .sub(0.3)
66                .abs()
67                < 0.0001
68        );
69    }
70
71    #[test]
72    fn test_negative() {
73        assert_eq!(
74            SNumber::Int(4),
75            SNumber::Int(-1001).modulo(&SNumber::Int(5))
76        );
77        assert_eq!(
78            SNumber::Int(-1),
79            SNumber::Int(101).modulo(&SNumber::Int(-2))
80        );
81        assert_eq!(
82            SNumber::Int(-4),
83            SNumber::Int(1001).modulo(&SNumber::Int(-5))
84        );
85        assert_eq!(SNumber::Int(1), SNumber::Int(1001).modulo(&SNumber::Int(5)));
86        assert_eq!(
87            SNumber::Int(-1),
88            SNumber::Int(-1001).modulo(&SNumber::Int(-5))
89        );
90        assert_eq!(
91            SNumber::Float(-3.5),
92            SNumber::Float(7.5).modulo(&SNumber::Float(-5.5))
93        );
94    }
95
96    #[test]
97    fn test_mod_pm1() {
98        for one in [-1, 1] {
99            for i in [10, -10, 100, 0] {
100                assert_eq!(SNumber::Int(0), SNumber::Int(i).modulo(&SNumber::Int(one)));
101            }
102            assert_eq!(SNumber::Int(0), SNumber::NAN.modulo(&SNumber::Int(one)));
103            assert_eq!(SNumber::NAN, SNumber::INFINITY.modulo(&SNumber::Int(one)));
104            assert_eq!(
105                SNumber::NAN,
106                SNumber::NEG_INFINITY.modulo(&SNumber::Int(one))
107            );
108        }
109    }
110
111    #[test]
112    fn test_mod_special() {
113        for zero1 in ZEROS {
114            for zero2 in ZEROS {
115                assert_eq!(SNumber::NAN, zero1.modulo(&zero2));
116            }
117            assert_eq!(SNumber::Int(0), zero1.modulo(&SNumber::INFINITY));
118            assert_eq!(SNumber::Int(0), zero1.modulo(&SNumber::NEG_INFINITY));
119            assert_eq!(SNumber::NAN, zero1.modulo(&SNumber::NAN));
120        }
121        for zero in ZEROS {
122            assert_eq!(SNumber::NAN, SNumber::NAN.modulo(&zero));
123            assert_eq!(SNumber::NAN, SNumber::INFINITY.modulo(&zero));
124            assert_eq!(SNumber::NAN, SNumber::NEG_INFINITY.modulo(&zero));
125            assert_eq!(SNumber::NAN, SNumber::Int(10).modulo(&zero));
126            assert_eq!(SNumber::NAN, SNumber::Int(-10).modulo(&zero));
127        }
128        assert_eq!(
129            SNumber::NAN,
130            SNumber::NEG_INFINITY.modulo(&SNumber::NEG_INFINITY)
131        );
132        assert_eq!(
133            SNumber::NAN,
134            SNumber::NEG_INFINITY.modulo(&SNumber::INFINITY)
135        );
136        assert_eq!(SNumber::NAN, SNumber::INFINITY.modulo(&SNumber::INFINITY));
137        assert_eq!(
138            SNumber::NAN,
139            SNumber::INFINITY.modulo(&SNumber::NEG_INFINITY)
140        );
141        assert_eq!(SNumber::NAN, SNumber::NAN.modulo(&SNumber::NAN));
142    }
143
144    #[test]
145    fn test_number_mod_inf() {
146        assert_eq!(
147            SNumber::Int(-1001),
148            SNumber::Int(-1001).modulo(&SNumber::NEG_INFINITY)
149        );
150
151        assert_eq!(
152            SNumber::Int(-1001),
153            SNumber::Int(-1001).modulo(&SNumber::INFINITY)
154        );
155
156        assert_eq!(
157            SNumber::Int(1001),
158            SNumber::Int(1001).modulo(&SNumber::INFINITY)
159        );
160
161        assert_eq!(
162            SNumber::Int(1001),
163            SNumber::Int(1001).modulo(&SNumber::NEG_INFINITY)
164        );
165    }
166}