ethereum_mysql/sql_uint/
operation.rs

1//! Arithmetic operations for SqlU256
2//!
3//! This module provides arithmetic operations (+, -, *, /, %) and other mathematical
4//! operations for SqlU256, following Rust's standard library patterns.
5
6use crate::{SqlU256, U256};
7use std::ops::{Add, BitAnd, BitOr, BitXor, Div, Mul, Not, Rem, Shl, Shr, Sub};
8
9/// Macro to implement binary arithmetic operations for all reference combinations
10macro_rules! impl_binary_op {
11    ($trait:ident, $method:ident, $op:tt) => {
12        impl $trait for SqlU256 {
13            type Output = Self;
14
15            fn $method(self, rhs: Self) -> Self::Output {
16                SqlU256::from(self.0 $op rhs.0)
17            }
18        }
19
20        impl $trait<&SqlU256> for SqlU256 {
21            type Output = Self;
22
23            fn $method(self, rhs: &Self) -> Self::Output {
24                SqlU256::from(self.0 $op rhs.0)
25            }
26        }
27
28        impl $trait<SqlU256> for &SqlU256 {
29            type Output = SqlU256;
30
31            fn $method(self, rhs: SqlU256) -> Self::Output {
32                SqlU256::from(self.0 $op rhs.0)
33            }
34        }
35
36        impl $trait<&SqlU256> for &SqlU256 {
37            type Output = SqlU256;
38
39            fn $method(self, rhs: &SqlU256) -> Self::Output {
40                SqlU256::from(self.0 $op rhs.0)
41            }
42        }
43    };
44}
45
46/// Macro to implement unary operations
47macro_rules! impl_unary_op {
48    ($trait:ident, $method:ident, $op:tt) => {
49        impl $trait for SqlU256 {
50            type Output = Self;
51
52            fn $method(self) -> Self::Output {
53                SqlU256::from($op self.0)
54            }
55        }
56    };
57}
58
59/// Macro to implement shift operations
60macro_rules! impl_shift_op {
61    ($trait:ident, $method:ident, $op:tt, $rhs:ty) => {
62        impl $trait<$rhs> for SqlU256 {
63            type Output = Self;
64
65            fn $method(self, rhs: $rhs) -> Self::Output {
66                SqlU256::from(self.0 $op rhs)
67            }
68        }
69    };
70}
71
72/// Macro to implement binary assignment operations (e.g., +=, -=, etc.)
73macro_rules! impl_binary_assign_op {
74    ($trait:ident, $method:ident, $op:tt) => {
75        impl $trait for SqlU256 {
76            fn $method(&mut self, rhs: Self) {
77                self.0 = self.0 $op rhs.0;
78            }
79        }
80        impl $trait<&SqlU256> for SqlU256 {
81            fn $method(&mut self, rhs: &Self) {
82                self.0 = self.0 $op rhs.0;
83            }
84        }
85    };
86}
87
88use std::ops::{AddAssign, DivAssign, MulAssign, RemAssign, SubAssign};
89impl_binary_assign_op!(AddAssign, add_assign, +);
90impl_binary_assign_op!(SubAssign, sub_assign, -);
91impl_binary_assign_op!(MulAssign, mul_assign, *);
92impl_binary_assign_op!(DivAssign, div_assign, /);
93impl_binary_assign_op!(RemAssign, rem_assign, %);
94
95// Binary arithmetic operations
96impl_binary_op!(Add, add, +);
97impl_binary_op!(Sub, sub, -);
98impl_binary_op!(Mul, mul, *);
99impl_binary_op!(Div, div, /);
100impl_binary_op!(Rem, rem, %);
101
102// Bitwise operations (only value-to-value, no reference variants needed for these)
103impl BitAnd for SqlU256 {
104    type Output = Self;
105
106    fn bitand(self, rhs: Self) -> Self::Output {
107        SqlU256::from(self.0 & rhs.0)
108    }
109}
110
111impl BitOr for SqlU256 {
112    type Output = Self;
113
114    fn bitor(self, rhs: Self) -> Self::Output {
115        SqlU256::from(self.0 | rhs.0)
116    }
117}
118
119impl BitXor for SqlU256 {
120    type Output = Self;
121
122    fn bitxor(self, rhs: Self) -> Self::Output {
123        SqlU256::from(self.0 ^ rhs.0)
124    }
125}
126
127// Unary operations
128impl_unary_op!(Not, not, !);
129
130// Shift operations
131impl_shift_op!(Shl, shl, <<, usize);
132impl_shift_op!(Shr, shr, >>, usize);
133
134// Additional mathematical operations
135impl SqlU256 {
136    /// Returns the square of this value
137    pub fn square(self) -> Self {
138        self * self
139    }
140
141    /// Returns the power of this value raised to the given exponent
142    pub fn pow(self, exp: usize) -> Self {
143        SqlU256::from(self.0.pow(U256::from(exp)))
144    }
145
146    /// Returns the greatest common divisor of two values
147    pub fn gcd(self, other: Self) -> Self {
148        let mut a = self.0;
149        let mut b = other.0;
150
151        while !b.is_zero() {
152            let temp = b;
153            b = a % b;
154            a = temp;
155        }
156        // Convert U256 back to SqlU256
157        SqlU256::from(a)
158    }
159
160    /// Returns the least common multiple of two values
161    pub fn lcm(self, other: Self) -> Self {
162        if self.0.is_zero() || other.0.is_zero() {
163            SqlU256::ZERO
164        } else {
165            let gcd = self.gcd(other);
166            (self / gcd) * other
167        }
168    }
169
170    /// Checked addition. Returns `None` if overflow occurred.
171    pub fn checked_add(self, rhs: Self) -> Option<Self> {
172        self.0.checked_add(rhs.0).map(SqlU256::from)
173    }
174
175    /// Checked subtraction. Returns `None` if overflow occurred.
176    pub fn checked_sub(self, rhs: Self) -> Option<Self> {
177        self.0.checked_sub(rhs.0).map(SqlU256::from)
178    }
179
180    /// Checked multiplication. Returns `None` if overflow occurred.
181    pub fn checked_mul(self, rhs: Self) -> Option<Self> {
182        self.0.checked_mul(rhs.0).map(SqlU256::from)
183    }
184
185    /// Checked division. Returns `None` if `rhs == 0`.
186    pub fn checked_div(self, rhs: Self) -> Option<Self> {
187        if rhs.0.is_zero() {
188            None
189        } else {
190            Some(SqlU256::from(self.0 / rhs.0))
191        }
192    }
193
194    /// Saturating addition. Clamps the result to `U256::MAX` if overflow occurred.
195    pub fn saturating_add(self, rhs: Self) -> Self {
196        SqlU256::from(self.0.saturating_add(rhs.0))
197    }
198
199    /// Saturating subtraction. Clamps the result to `0` if underflow occurred.
200    pub fn saturating_sub(self, rhs: Self) -> Self {
201        SqlU256::from(self.0.saturating_sub(rhs.0))
202    }
203
204    /// Saturating multiplication. Clamps the result to `U256::MAX` if overflow occurred.
205    pub fn saturating_mul(self, rhs: Self) -> Self {
206        SqlU256::from(self.0.saturating_mul(rhs.0))
207    }
208
209    /// Returns `true` if the value is zero
210    pub fn is_zero(self) -> bool {
211        self.0.is_zero()
212    }
213
214    /// Returns the minimum of two values
215    pub fn min(self, other: Self) -> Self {
216        if self.0 < other.0 {
217            self
218        } else {
219            other
220        }
221    }
222
223    /// Returns the maximum of two values
224    pub fn max(self, other: Self) -> Self {
225        if self.0 > other.0 {
226            self
227        } else {
228            other
229        }
230    }
231}
232
233#[cfg(test)]
234mod tests {
235    use super::*;
236
237    #[test]
238    fn test_basic_arithmetic() {
239        let a = SqlU256::from(100u64);
240        let b = SqlU256::from(50u64);
241
242        assert_eq!(a + b, SqlU256::from(150u64));
243        assert_eq!(a - b, SqlU256::from(50u64));
244        assert_eq!(a * b, SqlU256::from(5000u64));
245        assert_eq!(a / b, SqlU256::from(2u64));
246        assert_eq!(a % b, SqlU256::from(0u64));
247    }
248
249    #[test]
250    fn test_arithmetic_with_references() {
251        let a = SqlU256::from(100u64);
252        let b = SqlU256::from(50u64);
253
254        assert_eq!(&a + &b, SqlU256::from(150u64));
255        assert_eq!(a + &b, SqlU256::from(150u64));
256        assert_eq!(&a + b, SqlU256::from(150u64));
257    }
258
259    #[test]
260    fn test_bitwise_operations() {
261        let a = SqlU256::from(0b1100u64);
262        let b = SqlU256::from(0b1010u64);
263
264        assert_eq!(a & b, SqlU256::from(0b1000u64));
265        assert_eq!(a | b, SqlU256::from(0b1110u64));
266        assert_eq!(a ^ b, SqlU256::from(0b0110u64));
267        assert_eq!(!SqlU256::from(0u64), SqlU256::from(!U256::ZERO));
268    }
269
270    #[test]
271    fn test_shift_operations() {
272        let a = SqlU256::from(8u64);
273
274        assert_eq!(a << 1, SqlU256::from(16u64));
275        assert_eq!(a >> 1, SqlU256::from(4u64));
276        assert_eq!(a << 3, SqlU256::from(64u64));
277        assert_eq!(a >> 2, SqlU256::from(2u64));
278    }
279
280    #[test]
281    fn test_mathematical_operations() {
282        let a = SqlU256::from(5u64);
283        let _b = SqlU256::from(3u64);
284
285        assert_eq!(a.square(), SqlU256::from(25u64));
286        assert_eq!(a.pow(3), SqlU256::from(125u64));
287        assert_eq!(
288            SqlU256::from(12u64).gcd(SqlU256::from(8u64)),
289            SqlU256::from(4u64)
290        );
291        assert_eq!(
292            SqlU256::from(12u64).lcm(SqlU256::from(8u64)),
293            SqlU256::from(24u64)
294        );
295    }
296
297    #[test]
298    fn test_checked_operations() {
299        let a = SqlU256::from(100u64);
300        let b = SqlU256::from(50u64);
301        let zero = SqlU256::ZERO;
302
303        assert_eq!(a.checked_add(b), Some(SqlU256::from(150u64)));
304        assert_eq!(a.checked_sub(b), Some(SqlU256::from(50u64)));
305        assert_eq!(a.checked_mul(b), Some(SqlU256::from(5000u64)));
306        assert_eq!(a.checked_div(b), Some(SqlU256::from(2u64)));
307        assert_eq!(a.checked_div(zero), None);
308
309        // Test underflow
310        assert_eq!(b.checked_sub(a), None);
311    }
312
313    #[test]
314    fn test_saturating_operations() {
315        let a = SqlU256::from(100u64);
316        let b = SqlU256::from(150u64);
317
318        assert_eq!(a.saturating_add(b), SqlU256::from(250u64));
319        assert_eq!(a.saturating_sub(b), SqlU256::ZERO);
320        assert_eq!(a.saturating_mul(b), SqlU256::from(15000u64));
321    }
322
323    #[test]
324    fn test_utility_functions() {
325        let a = SqlU256::from(100u64);
326        let b = SqlU256::from(50u64);
327        let zero = SqlU256::ZERO;
328
329        assert!(!a.is_zero());
330        assert!(zero.is_zero());
331        assert_eq!(a.min(b), b);
332        assert_eq!(a.max(b), a);
333    }
334
335    #[test]
336    fn test_division_by_zero_panics() {
337        let a = SqlU256::from(100u64);
338        let zero = SqlU256::ZERO;
339
340        // This should panic
341        let result = std::panic::catch_unwind(|| {
342            let _ = a / zero;
343        });
344        assert!(result.is_err());
345    }
346
347    #[test]
348    fn test_gcd_edge_cases() {
349        let zero = SqlU256::ZERO;
350        let five = SqlU256::from(5u64);
351
352        assert_eq!(zero.gcd(five), five);
353        assert_eq!(five.gcd(zero), five);
354        assert_eq!(zero.gcd(zero), zero);
355    }
356
357    #[test]
358    fn test_lcm_edge_cases() {
359        let zero = SqlU256::ZERO;
360        let five = SqlU256::from(5u64);
361
362        assert_eq!(zero.lcm(five), zero);
363        assert_eq!(five.lcm(zero), zero);
364        assert_eq!(zero.lcm(zero), zero);
365    }
366}