scratch_test_value/math/
modulo.rs1use std::ops::Rem;
2
3use crate::{QuirkSink, SNumber, quirks::SNumberToFloatQ};
4
5impl SNumber {
6 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}