Skip to main content

ps_uuid/implementations/ops/
arithmetic.rs

1//! Arithmetic operator implementations for UUID.
2//!
3//! This module provides implementations for:
4//!
5//! | Operator | Trait | Semantics |
6//! |----------|-------|-----------|
7//! | `+` | [`Add`] | Wrapping addition |
8//! | `-` | [`Sub`] | Wrapping subtraction |
9//! | `*` | [`Mul`] | Wrapping multiplication |
10//! | `/` | [`Div`] | Standard division (panics on zero) |
11//! | `%` | [`Rem`] | Standard remainder (panics on zero) |
12//!
13//! # Wrapping Semantics
14//!
15//! Addition, subtraction, and multiplication use **wrapping semantics** to match
16//! typical integer behavior and avoid panics on overflow. This means:
17//!
18//! - `UUID::max() + 1` wraps to `UUID::nil()`
19//! - `UUID::nil() - 1` wraps to `UUID::max()`
20//! - Multiplication can overflow and wrap
21//!
22//! # Division and Remainder
23//!
24//! Division and remainder use standard semantics and will **panic** if the
25//! divisor is zero, matching the behavior of `u128`.
26//!
27//! # Supported Operand Combinations
28//!
29//! Each operator supports all of:
30//!
31//! - `UUID op UUID` and ref variants
32//! - `UUID op T` where `T: IntOperand` (all integer types)
33//! - `T op UUID` where `T: IntOperand`
34//! - Compound assignment: `UUID op= UUID`, `UUID op= T`
35//!
36//! # Examples
37//!
38//! ```
39//! use ps_uuid::UUID;
40//!
41//! // Basic arithmetic
42//! let a = UUID::from(100u128);
43//! let b = UUID::from(30u128);
44//!
45//! assert_eq!(u128::from(a + b), 130);
46//! assert_eq!(u128::from(a - b), 70);
47//! assert_eq!(u128::from(a * 2u32), 200);
48//! assert_eq!(u128::from(a / 10u32), 10);
49//! assert_eq!(u128::from(a % 30u32), 10);
50//!
51//! // Wrapping behavior
52//! let max = UUID::max();
53//! assert_eq!(max + 1u32, UUID::nil());
54//!
55//! let zero = UUID::nil();
56//! assert_eq!(zero - 1u32, UUID::max());
57//!
58//! // Compound assignment
59//! let mut uuid = UUID::from(10u128);
60//! uuid += 5u32;
61//! assert_eq!(u128::from(uuid), 15);
62//! ```
63
64use core::ops::{Add, AddAssign, Div, DivAssign, Mul, MulAssign, Rem, RemAssign, Sub, SubAssign};
65
66use super::int_operand::IntOperand;
67use super::{
68    impl_binop_uuid_uuid, impl_int_op_uuid_commutative, impl_int_op_uuid_noncommutative,
69    impl_ref_lhs_uuid, impl_uuid_op_int,
70};
71use crate::UUID;
72
73// ============================================================================
74// UUID op UUID Implementations
75// ============================================================================
76
77impl_binop_uuid_uuid!(Add, add, AddAssign, add_assign, u128::wrapping_add);
78impl_binop_uuid_uuid!(Sub, sub, SubAssign, sub_assign, u128::wrapping_sub);
79impl_binop_uuid_uuid!(Mul, mul, MulAssign, mul_assign, u128::wrapping_mul);
80impl_binop_uuid_uuid!(Div, div, DivAssign, div_assign, u128::div);
81impl_binop_uuid_uuid!(Rem, rem, RemAssign, rem_assign, u128::rem);
82
83impl_ref_lhs_uuid!(Add, add);
84impl_ref_lhs_uuid!(Sub, sub);
85impl_ref_lhs_uuid!(Mul, mul);
86impl_ref_lhs_uuid!(Div, div);
87impl_ref_lhs_uuid!(Rem, rem);
88
89// ============================================================================
90// UUID op T Implementations (where T: IntOperand)
91// ============================================================================
92
93impl_uuid_op_int!(Add, add, AddAssign, add_assign, u128::wrapping_add);
94impl_uuid_op_int!(Sub, sub, SubAssign, sub_assign, u128::wrapping_sub);
95impl_uuid_op_int!(Mul, mul, MulAssign, mul_assign, u128::wrapping_mul);
96impl_uuid_op_int!(Div, div, DivAssign, div_assign, u128::div);
97impl_uuid_op_int!(Rem, rem, RemAssign, rem_assign, u128::rem);
98
99// ============================================================================
100// T op UUID Implementations (where T: IntOperand)
101// ============================================================================
102
103// Add and Mul are commutative: T + UUID == UUID + T
104impl_int_op_uuid_commutative!(
105    Add, add, u8, u16, u32, u64, u128, usize, i8, i16, i32, i64, i128, isize
106);
107impl_int_op_uuid_commutative!(
108    Mul, mul, u8, u16, u32, u64, u128, usize, i8, i16, i32, i64, i128, isize
109);
110
111// Sub, Div, Rem are non-commutative: T - UUID != UUID - T
112impl_int_op_uuid_noncommutative!(
113    Sub,
114    sub,
115    u128::wrapping_sub,
116    u8,
117    u16,
118    u32,
119    u64,
120    u128,
121    usize,
122    i8,
123    i16,
124    i32,
125    i64,
126    i128,
127    isize
128);
129impl_int_op_uuid_noncommutative!(
130    Div,
131    div,
132    u128::div,
133    u8,
134    u16,
135    u32,
136    u64,
137    u128,
138    usize,
139    i8,
140    i16,
141    i32,
142    i64,
143    i128,
144    isize
145);
146impl_int_op_uuid_noncommutative!(
147    Rem,
148    rem,
149    u128::rem,
150    u8,
151    u16,
152    u32,
153    u64,
154    u128,
155    usize,
156    i8,
157    i16,
158    i32,
159    i64,
160    i128,
161    isize
162);
163
164// ============================================================================
165// Tests
166// ============================================================================
167
168#[cfg(test)]
169mod tests {
170    #![allow(clippy::op_ref)]
171
172    use super::*;
173
174    // -------------------------------------------------------------------------
175    // Addition tests
176    // -------------------------------------------------------------------------
177
178    #[test]
179    fn add_uuid_uuid() {
180        let a = UUID::from(100u128);
181        let b = UUID::from(50u128);
182        assert_eq!(u128::from(a + b), 150);
183    }
184
185    #[test]
186    fn add_uuid_ref_uuid() {
187        let a = UUID::from(100u128);
188        let b = UUID::from(50u128);
189        assert_eq!(u128::from(a + &b), 150);
190    }
191
192    #[test]
193    fn add_ref_uuid_uuid() {
194        let a = UUID::from(100u128);
195        let b = UUID::from(50u128);
196        assert_eq!(u128::from(&a + b), 150);
197    }
198
199    #[test]
200    fn add_ref_uuid_ref_uuid() {
201        let a = UUID::from(100u128);
202        let b = UUID::from(50u128);
203        assert_eq!(u128::from(&a + &b), 150);
204    }
205
206    #[test]
207    fn add_uuid_int() {
208        let uuid = UUID::from(100u128);
209        assert_eq!(u128::from(uuid + 50u8), 150);
210        assert_eq!(u128::from(uuid + 50u16), 150);
211        assert_eq!(u128::from(uuid + 50u32), 150);
212        assert_eq!(u128::from(uuid + 50u64), 150);
213        assert_eq!(u128::from(uuid + 50u128), 150);
214        assert_eq!(u128::from(uuid + 50usize), 150);
215        assert_eq!(u128::from(uuid + 50i8), 150);
216        assert_eq!(u128::from(uuid + 50i16), 150);
217        assert_eq!(u128::from(uuid + 50i32), 150);
218        assert_eq!(u128::from(uuid + 50i64), 150);
219        assert_eq!(u128::from(uuid + 50i128), 150);
220        assert_eq!(u128::from(uuid + 50isize), 150);
221    }
222
223    #[test]
224    fn add_int_uuid() {
225        let uuid = UUID::from(100u128);
226        assert_eq!(u128::from(50u8 + uuid), 150);
227        assert_eq!(u128::from(50u16 + uuid), 150);
228        assert_eq!(u128::from(50u32 + uuid), 150);
229        assert_eq!(u128::from(50u64 + uuid), 150);
230        assert_eq!(u128::from(50u128 + uuid), 150);
231        assert_eq!(u128::from(50usize + uuid), 150);
232    }
233
234    #[test]
235    fn add_int_ref_uuid() {
236        let uuid = UUID::from(100u128);
237        assert_eq!(u128::from(50u32 + &uuid), 150);
238    }
239
240    #[test]
241    fn add_ref_int_uuid() {
242        let uuid = UUID::from(100u128);
243        let n = 50u32;
244        assert_eq!(u128::from(&n + uuid), 150);
245    }
246
247    #[test]
248    fn add_ref_int_ref_uuid() {
249        let uuid = UUID::from(100u128);
250        let n = 50u32;
251        assert_eq!(u128::from(&n + &uuid), 150);
252    }
253
254    #[test]
255    fn add_wraps_on_overflow() {
256        let max = UUID::max();
257        assert_eq!(max + 1u32, UUID::nil());
258        assert_eq!(max + 2u32, UUID::from(1u128));
259    }
260
261    #[test]
262    fn add_assign_uuid() {
263        let mut uuid = UUID::from(100u128);
264        uuid += UUID::from(50u128);
265        assert_eq!(u128::from(uuid), 150);
266    }
267
268    #[test]
269    fn add_assign_ref_uuid() {
270        let mut uuid = UUID::from(100u128);
271        let other = UUID::from(50u128);
272        uuid += &other;
273        assert_eq!(u128::from(uuid), 150);
274    }
275
276    #[test]
277    fn add_assign_int() {
278        let mut uuid = UUID::from(100u128);
279        uuid += 50u32;
280        assert_eq!(u128::from(uuid), 150);
281    }
282
283    // -------------------------------------------------------------------------
284    // Subtraction tests
285    // -------------------------------------------------------------------------
286
287    #[test]
288    fn sub_uuid_uuid() {
289        let a = UUID::from(100u128);
290        let b = UUID::from(30u128);
291        assert_eq!(u128::from(a - b), 70);
292    }
293
294    #[test]
295    fn sub_uuid_int() {
296        let uuid = UUID::from(100u128);
297        assert_eq!(u128::from(uuid - 30u32), 70);
298    }
299
300    #[test]
301    fn sub_int_uuid() {
302        let uuid = UUID::from(30u128);
303        assert_eq!(u128::from(100u32 - uuid), 70);
304    }
305
306    #[test]
307    fn sub_wraps_on_underflow() {
308        let zero = UUID::nil();
309        assert_eq!(zero - 1u32, UUID::max());
310        assert_eq!(zero - 2u32, UUID::from(u128::MAX - 1));
311    }
312
313    #[test]
314    fn sub_noncommutative() {
315        let a = UUID::from(100u128);
316        // UUID - int != int - UUID (unless they're equal)
317        assert_eq!(u128::from(a - 30u32), 70);
318        assert_eq!(u128::from(30u32 - a), u128::MAX - 69); // Wraps around
319    }
320
321    #[test]
322    fn sub_assign() {
323        let mut uuid = UUID::from(100u128);
324        uuid -= 30u32;
325        assert_eq!(u128::from(uuid), 70);
326    }
327
328    // -------------------------------------------------------------------------
329    // Multiplication tests
330    // -------------------------------------------------------------------------
331
332    #[test]
333    fn mul_uuid_uuid() {
334        let a = UUID::from(10u128);
335        let b = UUID::from(20u128);
336        assert_eq!(u128::from(a * b), 200);
337    }
338
339    #[test]
340    fn mul_uuid_int() {
341        let uuid = UUID::from(10u128);
342        assert_eq!(u128::from(uuid * 20u32), 200);
343    }
344
345    #[test]
346    fn mul_int_uuid() {
347        let uuid = UUID::from(10u128);
348        assert_eq!(u128::from(20u32 * uuid), 200);
349    }
350
351    #[test]
352    fn mul_wraps_on_overflow() {
353        let large = UUID::from(u128::MAX / 2 + 1);
354        let result = large * 2u32;
355        // (u128::MAX / 2 + 1) * 2 wraps
356        assert_eq!(u128::from(result), 0);
357    }
358
359    #[test]
360    fn mul_assign() {
361        let mut uuid = UUID::from(10u128);
362        uuid *= 20u32;
363        assert_eq!(u128::from(uuid), 200);
364    }
365
366    // -------------------------------------------------------------------------
367    // Division tests
368    // -------------------------------------------------------------------------
369
370    #[test]
371    fn div_uuid_uuid() {
372        let a = UUID::from(100u128);
373        let b = UUID::from(10u128);
374        assert_eq!(u128::from(a / b), 10);
375    }
376
377    #[test]
378    fn div_uuid_int() {
379        let uuid = UUID::from(100u128);
380        assert_eq!(u128::from(uuid / 10u32), 10);
381    }
382
383    #[test]
384    fn div_int_uuid() {
385        let uuid = UUID::from(10u128);
386        assert_eq!(u128::from(100u32 / uuid), 10);
387    }
388
389    #[test]
390    fn div_truncates() {
391        let uuid = UUID::from(10u128);
392        assert_eq!(u128::from(uuid / 3u32), 3); // 10 / 3 = 3 (truncated)
393    }
394
395    #[test]
396    fn div_assign() {
397        let mut uuid = UUID::from(100u128);
398        uuid /= 10u32;
399        assert_eq!(u128::from(uuid), 10);
400    }
401
402    #[test]
403    #[should_panic(expected = "attempt to divide by zero")]
404    fn div_by_zero_panics() {
405        let uuid = UUID::from(100u128);
406        let _ = uuid / 0u32;
407    }
408
409    // -------------------------------------------------------------------------
410    // Remainder tests
411    // -------------------------------------------------------------------------
412
413    #[test]
414    fn rem_uuid_uuid() {
415        let a = UUID::from(100u128);
416        let b = UUID::from(30u128);
417        assert_eq!(u128::from(a % b), 10);
418    }
419
420    #[test]
421    fn rem_uuid_int() {
422        let uuid = UUID::from(100u128);
423        assert_eq!(u128::from(uuid % 30u32), 10);
424    }
425
426    #[test]
427    fn rem_int_uuid() {
428        let uuid = UUID::from(30u128);
429        assert_eq!(u128::from(100u32 % uuid), 10);
430    }
431
432    #[test]
433    fn rem_assign() {
434        let mut uuid = UUID::from(100u128);
435        uuid %= 30u32;
436        assert_eq!(u128::from(uuid), 10);
437    }
438
439    #[test]
440    #[should_panic(expected = "attempt to calculate the remainder with a divisor of zero")]
441    fn rem_by_zero_panics() {
442        let uuid = UUID::from(100u128);
443        let _ = uuid % 0u32;
444    }
445
446    // -------------------------------------------------------------------------
447    // Reference variant comprehensive tests
448    // -------------------------------------------------------------------------
449
450    #[test]
451    fn all_reference_variants_add() {
452        let a = UUID::from(100u128);
453        let b = UUID::from(50u128);
454        let n = 50u32;
455
456        // UUID op UUID variants
457        assert_eq!(a + b, UUID::from(150u128));
458        assert_eq!(a + &b, UUID::from(150u128));
459        assert_eq!(&a + b, UUID::from(150u128));
460        assert_eq!(&a + &b, UUID::from(150u128));
461
462        // UUID op T variants
463        assert_eq!(a + n, UUID::from(150u128));
464        assert_eq!(&a + n, UUID::from(150u128));
465        assert_eq!(a + &n, UUID::from(150u128));
466        assert_eq!(&a + &n, UUID::from(150u128));
467
468        // T op UUID variants
469        assert_eq!(n + a, UUID::from(150u128));
470        assert_eq!(n + &a, UUID::from(150u128));
471        assert_eq!(&n + a, UUID::from(150u128));
472        assert_eq!(&n + &a, UUID::from(150u128));
473    }
474
475    // -------------------------------------------------------------------------
476    // Negative integer operand tests
477    // -------------------------------------------------------------------------
478
479    #[test]
480    fn add_negative_int_wraps() {
481        let uuid = UUID::from(100u128);
482        // Adding -1i32 is like adding u128::MAX (due to sign extension)
483        // which wraps to uuid - 1
484        let result = uuid + (-1i32);
485        assert_eq!(result, UUID::from(99u128));
486    }
487
488    #[test]
489    fn sub_negative_int() {
490        let uuid = UUID::from(100u128);
491        // Subtracting -1i32 (which is u128::MAX after sign extension)
492        // is like adding 1
493        let result = uuid - (-1i32);
494        assert_eq!(result, UUID::from(101u128));
495    }
496
497    // -------------------------------------------------------------------------
498    // Identity and boundary tests
499    // -------------------------------------------------------------------------
500
501    #[test]
502    fn add_zero_is_identity() {
503        let uuid = UUID::from(42u128);
504        assert_eq!(uuid + 0u32, uuid);
505        assert_eq!(uuid + UUID::nil(), uuid);
506    }
507
508    #[test]
509    fn sub_zero_is_identity() {
510        let uuid = UUID::from(42u128);
511        assert_eq!(uuid - 0u32, uuid);
512        assert_eq!(uuid - UUID::nil(), uuid);
513    }
514
515    #[test]
516    fn mul_one_is_identity() {
517        let uuid = UUID::from(42u128);
518        assert_eq!(uuid * 1u32, uuid);
519        assert_eq!(uuid * UUID::from(1u128), uuid);
520    }
521
522    #[test]
523    #[allow(clippy::erasing_op)]
524    fn mul_zero_is_zero() {
525        let uuid = UUID::from(42u128);
526        assert_eq!(uuid * 0u32, UUID::nil());
527        assert_eq!(uuid * UUID::nil(), UUID::nil());
528    }
529
530    #[test]
531    fn div_one_is_identity() {
532        let uuid = UUID::from(42u128);
533        assert_eq!(uuid / 1u32, uuid);
534        assert_eq!(uuid / UUID::from(1u128), uuid);
535    }
536
537    #[test]
538    fn div_self_is_one() {
539        let uuid = UUID::from(42u128);
540        assert_eq!(uuid / uuid, UUID::from(1u128));
541    }
542
543    #[test]
544    fn rem_self_is_zero() {
545        let uuid = UUID::from(42u128);
546        assert_eq!(uuid % uuid, UUID::nil());
547    }
548}