Skip to main content

reifydb_value/value/number/safe/
mul.rs

1// SPDX-License-Identifier: Apache-2.0
2// Copyright (c) 2026 ReifyDB
3
4pub trait SafeMul: Sized {
5	fn checked_mul(&self, r: &Self) -> Option<Self>;
6	fn saturating_mul(&self, r: &Self) -> Self;
7	fn wrapping_mul(&self, r: &Self) -> Self;
8}
9
10macro_rules! impl_safe_mul {
11    ($($t:ty),*) => {
12        $(
13            impl SafeMul for $t {
14                fn checked_mul(&self, r: &Self) -> Option<Self> {
15                    <$t>::checked_mul(*self, *r)
16                }
17                fn saturating_mul(&self, r: &Self) -> Self {
18                    <$t>::saturating_mul(*self, *r)
19                }
20                fn wrapping_mul(&self, r: &Self) -> Self {
21                    <$t>::wrapping_mul(*self, *r)
22                }
23            }
24        )*
25    };
26}
27
28impl_safe_mul!(i8, i16, i32, i64, i128, u8, u16, u32, u64, u128);
29
30use crate::value::decimal::Decimal;
31
32impl SafeMul for Decimal {
33	fn checked_mul(&self, r: &Self) -> Option<Self> {
34		Decimal::checked_mul(self, r)
35	}
36
37	fn saturating_mul(&self, r: &Self) -> Self {
38		Decimal::saturating_mul(self, r)
39	}
40
41	fn wrapping_mul(&self, r: &Self) -> Self {
42		Decimal::saturating_mul(self, r)
43	}
44}
45
46impl SafeMul for f32 {
47	fn checked_mul(&self, r: &Self) -> Option<Self> {
48		let result = *self * *r;
49		if result.is_infinite() && self.is_finite() && r.is_finite() {
50			None
51		} else {
52			Some(result)
53		}
54	}
55
56	fn saturating_mul(&self, r: &Self) -> Self {
57		let result = *self * *r;
58		if result.is_infinite() {
59			if result.is_sign_positive() {
60				f32::MAX
61			} else {
62				f32::MIN
63			}
64		} else {
65			result
66		}
67	}
68
69	fn wrapping_mul(&self, r: &Self) -> Self {
70		let result = *self * *r;
71		if result.is_infinite() || result.is_nan() {
72			let sign = if (self.is_sign_positive() && r.is_sign_positive())
73				|| (self.is_sign_negative() && r.is_sign_negative())
74			{
75				1.0
76			} else {
77				-1.0
78			};
79
80			let wrapped_val = f32::MAX / 2.0;
81			wrapped_val * sign
82		} else {
83			result
84		}
85	}
86}
87
88impl SafeMul for f64 {
89	fn checked_mul(&self, r: &Self) -> Option<Self> {
90		let result = *self * *r;
91		if result.is_infinite() && self.is_finite() && r.is_finite() {
92			None
93		} else {
94			Some(result)
95		}
96	}
97
98	fn saturating_mul(&self, r: &Self) -> Self {
99		let result = *self * *r;
100		if result.is_infinite() {
101			if result.is_sign_positive() {
102				f64::MAX
103			} else {
104				f64::MIN
105			}
106		} else {
107			result
108		}
109	}
110
111	fn wrapping_mul(&self, r: &Self) -> Self {
112		let result = *self * *r;
113		if result.is_infinite() || result.is_nan() {
114			let sign = if (self.is_sign_positive() && r.is_sign_positive())
115				|| (self.is_sign_negative() && r.is_sign_negative())
116			{
117				1.0
118			} else {
119				-1.0
120			};
121
122			let wrapped_val = f64::MAX / 2.0;
123			wrapped_val * sign
124		} else {
125			result
126		}
127	}
128}
129
130#[cfg(test)]
131pub mod tests {
132
133	macro_rules! signed_unsigned {
134        ($($t:ty => $mod:ident),*) => {
135            $(
136                mod $mod {
137                    use super::super::SafeMul;
138
139                    #[test]
140                    fn checked_mul_happy() {
141                        let x: $t = 10;
142                        let y: $t = 2;
143                        assert_eq!(SafeMul::checked_mul(&x, &y), Some(20));
144                    }
145
146                    #[test]
147                    fn checked_mul_unhappy() {
148                        let x: $t = <$t>::MAX;
149                        let y: $t = 2;
150                        assert_eq!(SafeMul::checked_mul(&x, &y), None);
151                    }
152
153                    #[test]
154                    fn saturating_mul_happy() {
155                        let x: $t = 10;
156                        let y: $t = 2;
157                        assert_eq!(SafeMul::saturating_mul(&x, &y), 20);
158                    }
159
160                    #[test]
161                    fn saturating_mul_unhappy() {
162                        let x: $t = <$t>::MAX;
163                        let y: $t = 2;
164                        assert_eq!(SafeMul::saturating_mul(&x, &y), <$t>::MAX);
165                    }
166
167                    #[test]
168                    fn wrapping_mul_happy() {
169                        let x: $t = 10;
170                        let y: $t = 2;
171                        assert_eq!(SafeMul::wrapping_mul(&x, &y), 20);
172                    }
173
174                    #[test]
175                    fn wrapping_mul_unhappy() {
176                        let x: $t = <$t>::MAX;
177                        let y: $t = 2;
178                        assert_eq!(SafeMul::wrapping_mul(&x, &y), <$t>::wrapping_mul(<$t>::MAX, 2));
179                    }
180                }
181            )*
182        };
183    }
184
185	signed_unsigned!(
186	    i8 => i8,
187	    i16 => i16,
188	    i32 => i32,
189	    i64 => i64,
190	    i128 => i128,
191	    u8 => u8,
192	    u16 => u16,
193	    u32 => u32,
194	    u64 => u64,
195	    u128 => u128
196	);
197
198	mod f32 {
199		use crate::value::number::safe::mul::SafeMul;
200
201		#[test]
202		fn checked_mul_happy() {
203			let x: f32 = 10.0;
204			let y: f32 = 2.0;
205			assert_eq!(SafeMul::checked_mul(&x, &y), Some(20.0));
206		}
207
208		#[test]
209		fn checked_mul_unhappy() {
210			let x: f32 = f32::MAX;
211			let y: f32 = 2.0;
212			assert_eq!(SafeMul::checked_mul(&x, &y), None);
213		}
214
215		#[test]
216		fn saturating_mul_happy() {
217			let x: f32 = 10.0;
218			let y: f32 = 2.0;
219			assert_eq!(SafeMul::saturating_mul(&x, &y), 20.0);
220		}
221
222		#[test]
223		fn saturating_mul_unhappy() {
224			let x: f32 = f32::MAX;
225			let y: f32 = 2.0;
226			assert_eq!(SafeMul::saturating_mul(&x, &y), f32::MAX);
227		}
228
229		#[test]
230		fn wrapping_mul_happy() {
231			let x: f32 = 10.0;
232			let y: f32 = 2.0;
233			assert_eq!(SafeMul::wrapping_mul(&x, &y), 20.0);
234		}
235
236		#[test]
237		fn wrapping_mul_unhappy() {
238			// Floats have no natural wrap, so overflow is defined as MAX/2 with the sign of
239			// the product; the result must stay finite.
240			let x: f32 = f32::MAX;
241			let y: f32 = 2.0;
242			let result = SafeMul::wrapping_mul(&x, &y);
243			assert!(result.is_finite());
244			assert!(result > 0.0);
245			assert_eq!(result, f32::MAX / 2.0);
246		}
247
248		#[test]
249		fn wrapping_mul_negative() {
250			// The wrap keeps the sign the true product would have had.
251			let x: f32 = f32::MAX;
252			let y: f32 = -2.0;
253			let result = SafeMul::wrapping_mul(&x, &y);
254			assert!(result.is_finite());
255			assert!(result < 0.0);
256			assert_eq!(result, -(f32::MAX / 2.0));
257		}
258	}
259
260	mod f64 {
261		use crate::value::number::safe::mul::SafeMul;
262
263		#[test]
264		fn checked_mul_happy() {
265			let x: f64 = 10.0;
266			let y: f64 = 2.0;
267			assert_eq!(SafeMul::checked_mul(&x, &y), Some(20.0));
268		}
269
270		#[test]
271		fn checked_mul_unhappy() {
272			let x: f64 = f64::MAX;
273			let y: f64 = 2.0;
274			assert_eq!(SafeMul::checked_mul(&x, &y), None);
275		}
276
277		#[test]
278		fn saturating_mul_happy() {
279			let x: f64 = 10.0;
280			let y: f64 = 2.0;
281			assert_eq!(SafeMul::saturating_mul(&x, &y), 20.0);
282		}
283
284		#[test]
285		fn saturating_mul_unhappy() {
286			let x: f64 = f64::MAX;
287			let y: f64 = 2.0;
288			assert_eq!(SafeMul::saturating_mul(&x, &y), f64::MAX);
289		}
290
291		#[test]
292		fn wrapping_mul_happy() {
293			let x: f64 = 10.0;
294			let y: f64 = 2.0;
295			assert_eq!(SafeMul::wrapping_mul(&x, &y), 20.0);
296		}
297
298		#[test]
299		fn wrapping_mul_unhappy() {
300			// Floats have no natural wrap, so overflow is defined as MAX/2 with the sign of
301			// the product; the result must stay finite.
302			let x: f64 = f64::MAX;
303			let y: f64 = 2.0;
304			let result = SafeMul::wrapping_mul(&x, &y);
305			assert!(result.is_finite());
306			assert!(result > 0.0);
307			assert_eq!(result, f64::MAX / 2.0);
308		}
309
310		#[test]
311		fn wrapping_mul_negative() {
312			// The wrap keeps the sign the true product would have had.
313			let x: f64 = f64::MAX;
314			let y: f64 = -2.0;
315			let result = SafeMul::wrapping_mul(&x, &y);
316			assert!(result.is_finite());
317			assert!(result < 0.0);
318			assert_eq!(result, -(f64::MAX / 2.0));
319		}
320	}
321}