Skip to main content

reifydb_value/value/number/safe/
mul.rs

1// SPDX-License-Identifier: MIT
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, int::Int, uint::Uint};
31
32impl SafeMul for Int {
33	fn checked_mul(&self, r: &Self) -> Option<Self> {
34		Some(Int::from(&self.0 * &r.0))
35	}
36
37	fn saturating_mul(&self, r: &Self) -> Self {
38		Int::from(&self.0 * &r.0)
39	}
40
41	fn wrapping_mul(&self, r: &Self) -> Self {
42		Int::from(&self.0 * &r.0)
43	}
44}
45
46impl SafeMul for Uint {
47	fn checked_mul(&self, r: &Self) -> Option<Self> {
48		Some(Uint::from(&self.0 * &r.0))
49	}
50
51	fn saturating_mul(&self, r: &Self) -> Self {
52		Uint::from(&self.0 * &r.0)
53	}
54
55	fn wrapping_mul(&self, r: &Self) -> Self {
56		Uint::from(&self.0 * &r.0)
57	}
58}
59
60impl SafeMul for Decimal {
61	fn checked_mul(&self, r: &Self) -> Option<Self> {
62		let result = self.inner() * r.inner();
63		Some(Decimal::from(result))
64	}
65
66	fn saturating_mul(&self, r: &Self) -> Self {
67		let result = self.inner() * r.inner();
68		Decimal::from(result)
69	}
70
71	fn wrapping_mul(&self, r: &Self) -> Self {
72		let result = self.inner() * r.inner();
73		Decimal::from(result)
74	}
75}
76
77impl SafeMul for f32 {
78	fn checked_mul(&self, r: &Self) -> Option<Self> {
79		let result = *self * *r;
80		if result.is_finite() {
81			Some(result)
82		} else {
83			None
84		}
85	}
86
87	fn saturating_mul(&self, r: &Self) -> Self {
88		let result = *self * *r;
89		if result.is_infinite() {
90			if result.is_sign_positive() {
91				f32::MAX
92			} else {
93				f32::MIN
94			}
95		} else {
96			result
97		}
98	}
99
100	fn wrapping_mul(&self, r: &Self) -> Self {
101		let result = *self * *r;
102		if result.is_infinite() || result.is_nan() {
103			let sign = if (self.is_sign_positive() && r.is_sign_positive())
104				|| (self.is_sign_negative() && r.is_sign_negative())
105			{
106				1.0
107			} else {
108				-1.0
109			};
110
111			let wrapped_val = f32::MAX / 2.0;
112			wrapped_val * sign
113		} else {
114			result
115		}
116	}
117}
118
119impl SafeMul for f64 {
120	fn checked_mul(&self, r: &Self) -> Option<Self> {
121		let result = *self * *r;
122		if result.is_finite() {
123			Some(result)
124		} else {
125			None
126		}
127	}
128
129	fn saturating_mul(&self, r: &Self) -> Self {
130		let result = *self * *r;
131		if result.is_infinite() {
132			if result.is_sign_positive() {
133				f64::MAX
134			} else {
135				f64::MIN
136			}
137		} else {
138			result
139		}
140	}
141
142	fn wrapping_mul(&self, r: &Self) -> Self {
143		let result = *self * *r;
144		if result.is_infinite() || result.is_nan() {
145			let sign = if (self.is_sign_positive() && r.is_sign_positive())
146				|| (self.is_sign_negative() && r.is_sign_negative())
147			{
148				1.0
149			} else {
150				-1.0
151			};
152
153			let wrapped_val = f64::MAX / 2.0;
154			wrapped_val * sign
155		} else {
156			result
157		}
158	}
159}
160
161#[cfg(test)]
162pub mod tests {
163
164	macro_rules! signed_unsigned {
165        ($($t:ty => $mod:ident),*) => {
166            $(
167                mod $mod {
168                    use super::super::SafeMul;
169
170                    #[test]
171                    fn checked_mul_happy() {
172                        let x: $t = 10;
173                        let y: $t = 2;
174                        assert_eq!(SafeMul::checked_mul(&x, &y), Some(20));
175                    }
176
177                    #[test]
178                    fn checked_mul_unhappy() {
179                        let x: $t = <$t>::MAX;
180                        let y: $t = 2;
181                        assert_eq!(SafeMul::checked_mul(&x, &y), None);
182                    }
183
184                    #[test]
185                    fn saturating_mul_happy() {
186                        let x: $t = 10;
187                        let y: $t = 2;
188                        assert_eq!(SafeMul::saturating_mul(&x, &y), 20);
189                    }
190
191                    #[test]
192                    fn saturating_mul_unhappy() {
193                        let x: $t = <$t>::MAX;
194                        let y: $t = 2;
195                        assert_eq!(SafeMul::saturating_mul(&x, &y), <$t>::MAX);
196                    }
197
198                    #[test]
199                    fn wrapping_mul_happy() {
200                        let x: $t = 10;
201                        let y: $t = 2;
202                        assert_eq!(SafeMul::wrapping_mul(&x, &y), 20);
203                    }
204
205                    #[test]
206                    fn wrapping_mul_unhappy() {
207                        let x: $t = <$t>::MAX;
208                        let y: $t = 2;
209                        assert_eq!(SafeMul::wrapping_mul(&x, &y), <$t>::wrapping_mul(<$t>::MAX, 2));
210                    }
211                }
212            )*
213        };
214    }
215
216	signed_unsigned!(
217	    i8 => i8,
218	    i16 => i16,
219	    i32 => i32,
220	    i64 => i64,
221	    i128 => i128,
222	    u8 => u8,
223	    u16 => u16,
224	    u32 => u32,
225	    u64 => u64,
226	    u128 => u128
227	);
228
229	mod f32 {
230		use crate::value::number::safe::mul::SafeMul;
231
232		#[test]
233		fn checked_mul_happy() {
234			let x: f32 = 10.0;
235			let y: f32 = 2.0;
236			assert_eq!(SafeMul::checked_mul(&x, &y), Some(20.0));
237		}
238
239		#[test]
240		fn checked_mul_unhappy() {
241			let x: f32 = f32::MAX;
242			let y: f32 = 2.0;
243			assert_eq!(SafeMul::checked_mul(&x, &y), None);
244		}
245
246		#[test]
247		fn saturating_mul_happy() {
248			let x: f32 = 10.0;
249			let y: f32 = 2.0;
250			assert_eq!(SafeMul::saturating_mul(&x, &y), 20.0);
251		}
252
253		#[test]
254		fn saturating_mul_unhappy() {
255			let x: f32 = f32::MAX;
256			let y: f32 = 2.0;
257			assert_eq!(SafeMul::saturating_mul(&x, &y), f32::MAX);
258		}
259
260		#[test]
261		fn wrapping_mul_happy() {
262			let x: f32 = 10.0;
263			let y: f32 = 2.0;
264			assert_eq!(SafeMul::wrapping_mul(&x, &y), 20.0);
265		}
266
267		#[test]
268		fn wrapping_mul_unhappy() {
269			let x: f32 = f32::MAX;
270			let y: f32 = 2.0;
271			let result = SafeMul::wrapping_mul(&x, &y);
272			// Should wrap around instead of being infinite
273			assert!(result.is_finite());
274			// Should be positive since f32::MAX * 2.0 is positive
275			assert!(result > 0.0);
276			// With our simple wrapping, overflow results in
277			// f32::MAX / 2.0
278			assert_eq!(result, f32::MAX / 2.0);
279		}
280
281		#[test]
282		fn wrapping_mul_negative() {
283			let x: f32 = f32::MAX;
284			let y: f32 = -2.0;
285			let result = SafeMul::wrapping_mul(&x, &y);
286			// Should wrap around instead of being infinite
287			assert!(result.is_finite());
288			// Should be negative since f32::MAX * -2.0 is negative
289			assert!(result < 0.0);
290			// With our simple wrapping, overflow results in
291			// -(f32::MAX / 2.0)
292			assert_eq!(result, -(f32::MAX / 2.0));
293		}
294	}
295
296	mod f64 {
297		use crate::value::number::safe::mul::SafeMul;
298
299		#[test]
300		fn checked_mul_happy() {
301			let x: f64 = 10.0;
302			let y: f64 = 2.0;
303			assert_eq!(SafeMul::checked_mul(&x, &y), Some(20.0));
304		}
305
306		#[test]
307		fn checked_mul_unhappy() {
308			let x: f64 = f64::MAX;
309			let y: f64 = 2.0;
310			assert_eq!(SafeMul::checked_mul(&x, &y), None);
311		}
312
313		#[test]
314		fn saturating_mul_happy() {
315			let x: f64 = 10.0;
316			let y: f64 = 2.0;
317			assert_eq!(SafeMul::saturating_mul(&x, &y), 20.0);
318		}
319
320		#[test]
321		fn saturating_mul_unhappy() {
322			let x: f64 = f64::MAX;
323			let y: f64 = 2.0;
324			assert_eq!(SafeMul::saturating_mul(&x, &y), f64::MAX);
325		}
326
327		#[test]
328		fn wrapping_mul_happy() {
329			let x: f64 = 10.0;
330			let y: f64 = 2.0;
331			assert_eq!(SafeMul::wrapping_mul(&x, &y), 20.0);
332		}
333
334		#[test]
335		fn wrapping_mul_unhappy() {
336			let x: f64 = f64::MAX;
337			let y: f64 = 2.0;
338			let result = SafeMul::wrapping_mul(&x, &y);
339			// Should wrap around instead of being infinite
340			assert!(result.is_finite());
341			// Should be positive since f64::MAX * 2.0 is positive
342			assert!(result > 0.0);
343			// With our simple wrapping, overflow results in
344			// f64::MAX / 2.0
345			assert_eq!(result, f64::MAX / 2.0);
346		}
347
348		#[test]
349		fn wrapping_mul_negative() {
350			let x: f64 = f64::MAX;
351			let y: f64 = -2.0;
352			let result = SafeMul::wrapping_mul(&x, &y);
353			// Should wrap around instead of being infinite
354			assert!(result.is_finite());
355			// Should be negative since f64::MAX * -2.0 is negative
356			assert!(result < 0.0);
357			// With our simple wrapping, overflow results in
358			// -(f64::MAX / 2.0)
359			assert_eq!(result, -(f64::MAX / 2.0));
360		}
361	}
362}