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, 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			// Floats have no natural wrap, so overflow is defined as MAX/2 with the sign of
270			// the product; the result must stay finite.
271			let x: f32 = f32::MAX;
272			let y: f32 = 2.0;
273			let result = SafeMul::wrapping_mul(&x, &y);
274			assert!(result.is_finite());
275			assert!(result > 0.0);
276			assert_eq!(result, f32::MAX / 2.0);
277		}
278
279		#[test]
280		fn wrapping_mul_negative() {
281			// The wrap keeps the sign the true product would have had.
282			let x: f32 = f32::MAX;
283			let y: f32 = -2.0;
284			let result = SafeMul::wrapping_mul(&x, &y);
285			assert!(result.is_finite());
286			assert!(result < 0.0);
287			assert_eq!(result, -(f32::MAX / 2.0));
288		}
289	}
290
291	mod f64 {
292		use crate::value::number::safe::mul::SafeMul;
293
294		#[test]
295		fn checked_mul_happy() {
296			let x: f64 = 10.0;
297			let y: f64 = 2.0;
298			assert_eq!(SafeMul::checked_mul(&x, &y), Some(20.0));
299		}
300
301		#[test]
302		fn checked_mul_unhappy() {
303			let x: f64 = f64::MAX;
304			let y: f64 = 2.0;
305			assert_eq!(SafeMul::checked_mul(&x, &y), None);
306		}
307
308		#[test]
309		fn saturating_mul_happy() {
310			let x: f64 = 10.0;
311			let y: f64 = 2.0;
312			assert_eq!(SafeMul::saturating_mul(&x, &y), 20.0);
313		}
314
315		#[test]
316		fn saturating_mul_unhappy() {
317			let x: f64 = f64::MAX;
318			let y: f64 = 2.0;
319			assert_eq!(SafeMul::saturating_mul(&x, &y), f64::MAX);
320		}
321
322		#[test]
323		fn wrapping_mul_happy() {
324			let x: f64 = 10.0;
325			let y: f64 = 2.0;
326			assert_eq!(SafeMul::wrapping_mul(&x, &y), 20.0);
327		}
328
329		#[test]
330		fn wrapping_mul_unhappy() {
331			// Floats have no natural wrap, so overflow is defined as MAX/2 with the sign of
332			// the product; the result must stay finite.
333			let x: f64 = f64::MAX;
334			let y: f64 = 2.0;
335			let result = SafeMul::wrapping_mul(&x, &y);
336			assert!(result.is_finite());
337			assert!(result > 0.0);
338			assert_eq!(result, f64::MAX / 2.0);
339		}
340
341		#[test]
342		fn wrapping_mul_negative() {
343			// The wrap keeps the sign the true product would have had.
344			let x: f64 = f64::MAX;
345			let y: f64 = -2.0;
346			let result = SafeMul::wrapping_mul(&x, &y);
347			assert!(result.is_finite());
348			assert!(result < 0.0);
349			assert_eq!(result, -(f64::MAX / 2.0));
350		}
351	}
352}