Skip to main content

reifydb_value/value/number/safe/
remainder.rs

1// SPDX-License-Identifier: MIT
2// Copyright (c) 2026 ReifyDB
3
4pub trait SafeRemainder: Sized {
5	fn checked_rem(&self, r: &Self) -> Option<Self>;
6	fn saturating_rem(&self, r: &Self) -> Self;
7	fn wrapping_rem(&self, r: &Self) -> Self;
8	fn is_zero(&self) -> bool;
9}
10
11macro_rules! impl_safe_rem_signed {
12    ($($t:ty),*) => {
13        $(
14            impl SafeRemainder for $t {
15                fn checked_rem(&self, r: &Self) -> Option<Self> {
16                    if *r == 0 || (*self == <$t>::MIN && *r == -1) {
17                        None
18                    } else {
19                        Some(*self % *r)
20                    }
21                }
22                fn saturating_rem(&self, r: &Self) -> Self {
23                    if *r == 0 {
24                        0
25                    } else if *self == <$t>::MIN && *r == -1 {
26                        0
27                    } else {
28                        *self % *r
29                    }
30                }
31                fn wrapping_rem(&self, r: &Self) -> Self {
32                    if *r == 0 {
33                        0
34                    } else {
35                        (*self).wrapping_rem(*r)
36                    }
37                }
38                fn is_zero(&self) -> bool {
39                    *self == 0
40                }
41            }
42        )*
43    };
44}
45
46macro_rules! impl_safe_rem_unsigned {
47    ($($t:ty),*) => {
48        $(
49            impl SafeRemainder for $t {
50                fn checked_rem(&self, r: &Self) -> Option<Self> {
51                    if *r == 0 {
52                        None
53                    } else {
54                        Some(*self % *r)
55                    }
56                }
57                fn saturating_rem(&self, r: &Self) -> Self {
58                    if *r == 0 {
59                        0
60                    } else {
61                        *self % *r
62                    }
63                }
64                fn wrapping_rem(&self, r: &Self) -> Self {
65                    if *r == 0 {
66                        0
67                    } else {
68                        *self % *r
69                    }
70                }
71                fn is_zero(&self) -> bool {
72                    *self == 0
73                }
74            }
75        )*
76    };
77}
78
79impl_safe_rem_signed!(i8, i16, i32, i64, i128);
80impl_safe_rem_unsigned!(u8, u16, u32, u64, u128);
81
82use bigdecimal::{BigDecimal, Zero};
83use num_bigint::BigInt;
84
85use crate::value::{decimal::Decimal, int::Int, uint::Uint};
86
87impl SafeRemainder for Int {
88	fn checked_rem(&self, r: &Self) -> Option<Self> {
89		if r.0 == BigInt::from(0) {
90			None
91		} else {
92			Some(Int::from(&self.0 % &r.0))
93		}
94	}
95
96	fn saturating_rem(&self, r: &Self) -> Self {
97		if r.0 == BigInt::from(0) {
98			Int::from(0)
99		} else {
100			Int::from(&self.0 % &r.0)
101		}
102	}
103
104	fn wrapping_rem(&self, r: &Self) -> Self {
105		if r.0 == BigInt::from(0) {
106			Int::from(0)
107		} else {
108			Int::from(&self.0 % &r.0)
109		}
110	}
111
112	fn is_zero(&self) -> bool {
113		self.0 == BigInt::from(0)
114	}
115}
116
117impl SafeRemainder for Uint {
118	fn checked_rem(&self, r: &Self) -> Option<Self> {
119		if r.0 == BigInt::from(0) {
120			None
121		} else {
122			Some(Uint::from(&self.0 % &r.0))
123		}
124	}
125
126	fn saturating_rem(&self, r: &Self) -> Self {
127		if r.0 == BigInt::from(0) {
128			Uint::from(0u64)
129		} else {
130			Uint::from(&self.0 % &r.0)
131		}
132	}
133
134	fn wrapping_rem(&self, r: &Self) -> Self {
135		if r.0 == BigInt::from(0) {
136			Uint::from(0u64)
137		} else {
138			Uint::from(&self.0 % &r.0)
139		}
140	}
141
142	fn is_zero(&self) -> bool {
143		self.0 == BigInt::from(0)
144	}
145}
146
147impl SafeRemainder for Decimal {
148	fn checked_rem(&self, r: &Self) -> Option<Self> {
149		if r.inner().is_zero() {
150			None
151		} else {
152			let result = self.inner() % r.inner();
153			Some(Decimal::from(result))
154		}
155	}
156
157	fn saturating_rem(&self, r: &Self) -> Self {
158		if r.inner().is_zero() {
159			Decimal::from(BigDecimal::from(0))
160		} else {
161			let result = self.inner() % r.inner();
162			Decimal::from(result)
163		}
164	}
165
166	fn wrapping_rem(&self, r: &Self) -> Self {
167		if r.inner().is_zero() {
168			Decimal::from(BigDecimal::from(0))
169		} else {
170			let result = self.inner() % r.inner();
171			Decimal::from(result)
172		}
173	}
174
175	fn is_zero(&self) -> bool {
176		self.inner().is_zero()
177	}
178}
179
180impl SafeRemainder for f32 {
181	fn checked_rem(&self, r: &Self) -> Option<Self> {
182		if *r == 0.0 || r.is_nan() || self.is_nan() {
183			None
184		} else {
185			let result = *self % *r;
186			if result.is_finite() {
187				Some(result)
188			} else {
189				None
190			}
191		}
192	}
193
194	fn saturating_rem(&self, r: &Self) -> Self {
195		if *r == 0.0 || r.is_nan() || self.is_nan() {
196			0.0
197		} else {
198			let result = *self % *r;
199			if result.is_finite() {
200				result
201			} else {
202				0.0
203			}
204		}
205	}
206
207	fn wrapping_rem(&self, r: &Self) -> Self {
208		if *r == 0.0 {
209			0.0
210		} else {
211			let result = *self % *r;
212			if result.is_infinite() || result.is_nan() {
213				0.0
214			} else {
215				result
216			}
217		}
218	}
219
220	fn is_zero(&self) -> bool {
221		*self == 0.0
222	}
223}
224
225impl SafeRemainder for f64 {
226	fn checked_rem(&self, r: &Self) -> Option<Self> {
227		if *r == 0.0 || r.is_nan() || self.is_nan() {
228			None
229		} else {
230			let result = *self % *r;
231			if result.is_finite() {
232				Some(result)
233			} else {
234				None
235			}
236		}
237	}
238
239	fn saturating_rem(&self, r: &Self) -> Self {
240		if *r == 0.0 || r.is_nan() || self.is_nan() {
241			0.0
242		} else {
243			let result = *self % *r;
244			if result.is_finite() {
245				result
246			} else {
247				0.0
248			}
249		}
250	}
251
252	fn wrapping_rem(&self, r: &Self) -> Self {
253		if *r == 0.0 {
254			0.0
255		} else {
256			let result = *self % *r;
257			if result.is_infinite() || result.is_nan() {
258				0.0
259			} else {
260				result
261			}
262		}
263	}
264
265	fn is_zero(&self) -> bool {
266		*self == 0.0
267	}
268}
269
270#[cfg(test)]
271pub mod tests {
272	macro_rules! signed_unsigned {
273        ($($t:ty => $mod:ident),*) => {
274            $(
275                mod $mod {
276                    use super::super::SafeRemainder;
277
278                    #[test]
279                    fn checked_rem_happy() {
280                        let x: $t = 10;
281                        let y: $t = 3;
282                        assert_eq!(SafeRemainder::checked_rem(&x, &y), Some(1));
283                    }
284
285                    #[test]
286                    fn checked_rem_zero() {
287                        let x: $t = 10;
288                        let y: $t = 0;
289                        assert_eq!(SafeRemainder::checked_rem(&x, &y), None);
290                    }
291
292                    #[test]
293                    fn saturating_rem_happy() {
294                        let x: $t = 10;
295                        let y: $t = 3;
296                        assert_eq!(SafeRemainder::saturating_rem(&x, &y), 1);
297                    }
298
299                    #[test]
300                    fn saturating_rem_zero() {
301                        let x: $t = 10;
302                        let y: $t = 0;
303                        assert_eq!(SafeRemainder::saturating_rem(&x, &y), 0);
304                    }
305
306                    #[test]
307                    fn wrapping_rem_happy() {
308                        let x: $t = 10;
309                        let y: $t = 3;
310                        assert_eq!(SafeRemainder::wrapping_rem(&x, &y), 1);
311                    }
312
313                    #[test]
314                    fn wrapping_rem_zero() {
315                        let x: $t = 10;
316                        let y: $t = 0;
317                        assert_eq!(SafeRemainder::wrapping_rem(&x, &y), 0);
318                    }
319                }
320            )*
321        };
322    }
323
324	signed_unsigned!(
325	    i8 => i8,
326	    i16 => i16,
327	    i32 => i32,
328	    i64 => i64,
329	    i128 => i128,
330	    u8 => u8,
331	    u16 => u16,
332	    u32 => u32,
333	    u64 => u64,
334	    u128 => u128
335	);
336
337	mod signed_overflow {
338		use crate::value::number::safe::remainder::SafeRemainder;
339
340		#[test]
341		fn checked_rem_min_negative_one() {
342			assert_eq!(SafeRemainder::checked_rem(&i8::MIN, &-1), None);
343			assert_eq!(SafeRemainder::checked_rem(&i16::MIN, &-1), None);
344			assert_eq!(SafeRemainder::checked_rem(&i32::MIN, &-1), None);
345			assert_eq!(SafeRemainder::checked_rem(&i64::MIN, &-1), None);
346			assert_eq!(SafeRemainder::checked_rem(&i128::MIN, &-1), None);
347		}
348
349		#[test]
350		fn saturating_rem_min_negative_one() {
351			assert_eq!(SafeRemainder::saturating_rem(&i8::MIN, &-1), 0);
352			assert_eq!(SafeRemainder::saturating_rem(&i16::MIN, &-1), 0);
353			assert_eq!(SafeRemainder::saturating_rem(&i32::MIN, &-1), 0);
354			assert_eq!(SafeRemainder::saturating_rem(&i64::MIN, &-1), 0);
355			assert_eq!(SafeRemainder::saturating_rem(&i128::MIN, &-1), 0);
356		}
357
358		#[test]
359		fn wrapping_rem_min_negative_one() {
360			assert_eq!(SafeRemainder::wrapping_rem(&i8::MIN, &-1), 0);
361			assert_eq!(SafeRemainder::wrapping_rem(&i16::MIN, &-1), 0);
362			assert_eq!(SafeRemainder::wrapping_rem(&i32::MIN, &-1), 0);
363			assert_eq!(SafeRemainder::wrapping_rem(&i64::MIN, &-1), 0);
364			assert_eq!(SafeRemainder::wrapping_rem(&i128::MIN, &-1), 0);
365		}
366	}
367
368	mod f32 {
369		use crate::value::number::safe::remainder::SafeRemainder;
370
371		#[test]
372		fn checked_rem_happy() {
373			let x: f32 = 10.5;
374			let y: f32 = 3.0;
375			assert_eq!(SafeRemainder::checked_rem(&x, &y), Some(1.5));
376		}
377
378		#[test]
379		fn checked_rem_zero() {
380			let x: f32 = 10.0;
381			let y: f32 = 0.0;
382			assert_eq!(SafeRemainder::checked_rem(&x, &y), None);
383		}
384
385		#[test]
386		fn checked_rem_nan() {
387			let x: f32 = f32::NAN;
388			let y: f32 = 3.0;
389			assert_eq!(SafeRemainder::checked_rem(&x, &y), None);
390
391			let x: f32 = 10.0;
392			let y: f32 = f32::NAN;
393			assert_eq!(SafeRemainder::checked_rem(&x, &y), None);
394		}
395
396		#[test]
397		fn saturating_rem_happy() {
398			let x: f32 = 10.5;
399			let y: f32 = 3.0;
400			assert_eq!(SafeRemainder::saturating_rem(&x, &y), 1.5);
401		}
402
403		#[test]
404		fn saturating_rem_zero() {
405			let x: f32 = 10.0;
406			let y: f32 = 0.0;
407			assert_eq!(SafeRemainder::saturating_rem(&x, &y), 0.0);
408		}
409
410		#[test]
411		fn saturating_rem_nan() {
412			let x: f32 = f32::NAN;
413			let y: f32 = 3.0;
414			assert_eq!(SafeRemainder::saturating_rem(&x, &y), 0.0);
415		}
416
417		#[test]
418		fn wrapping_rem_happy() {
419			let x: f32 = 10.5;
420			let y: f32 = 3.0;
421			assert_eq!(SafeRemainder::wrapping_rem(&x, &y), 1.5);
422		}
423
424		#[test]
425		fn wrapping_rem_zero() {
426			let x: f32 = 10.0;
427			let y: f32 = 0.0;
428			assert_eq!(SafeRemainder::wrapping_rem(&x, &y), 0.0);
429		}
430
431		#[test]
432		fn wrapping_rem_infinity() {
433			let x: f32 = f32::INFINITY;
434			let y: f32 = 3.0;
435			let result = SafeRemainder::wrapping_rem(&x, &y);
436			assert_eq!(result, 0.0);
437		}
438	}
439
440	mod f64 {
441		use crate::value::number::safe::remainder::SafeRemainder;
442
443		#[test]
444		fn checked_rem_happy() {
445			let x: f64 = 10.5;
446			let y: f64 = 3.0;
447			assert_eq!(SafeRemainder::checked_rem(&x, &y), Some(1.5));
448		}
449
450		#[test]
451		fn checked_rem_zero() {
452			let x: f64 = 10.0;
453			let y: f64 = 0.0;
454			assert_eq!(SafeRemainder::checked_rem(&x, &y), None);
455		}
456
457		#[test]
458		fn checked_rem_nan() {
459			let x: f64 = f64::NAN;
460			let y: f64 = 3.0;
461			assert_eq!(SafeRemainder::checked_rem(&x, &y), None);
462
463			let x: f64 = 10.0;
464			let y: f64 = f64::NAN;
465			assert_eq!(SafeRemainder::checked_rem(&x, &y), None);
466		}
467
468		#[test]
469		fn saturating_rem_happy() {
470			let x: f64 = 10.5;
471			let y: f64 = 3.0;
472			assert_eq!(SafeRemainder::saturating_rem(&x, &y), 1.5);
473		}
474
475		#[test]
476		fn saturating_rem_zero() {
477			let x: f64 = 10.0;
478			let y: f64 = 0.0;
479			assert_eq!(SafeRemainder::saturating_rem(&x, &y), 0.0);
480		}
481
482		#[test]
483		fn saturating_rem_nan() {
484			let x: f64 = f64::NAN;
485			let y: f64 = 3.0;
486			assert_eq!(SafeRemainder::saturating_rem(&x, &y), 0.0);
487		}
488
489		#[test]
490		fn wrapping_rem_happy() {
491			let x: f64 = 10.5;
492			let y: f64 = 3.0;
493			assert_eq!(SafeRemainder::wrapping_rem(&x, &y), 1.5);
494		}
495
496		#[test]
497		fn wrapping_rem_zero() {
498			let x: f64 = 10.0;
499			let y: f64 = 0.0;
500			assert_eq!(SafeRemainder::wrapping_rem(&x, &y), 0.0);
501		}
502
503		#[test]
504		fn wrapping_rem_infinity() {
505			let x: f64 = f64::INFINITY;
506			let y: f64 = 3.0;
507			let result = SafeRemainder::wrapping_rem(&x, &y);
508			assert_eq!(result, 0.0);
509		}
510	}
511}