ext_ops/
saturating_ops.rs

1/*
2 * Copyright (c) 2023 Martin Mills <daggerbot@gmail.com>
3 *
4 * This Source Code Form is subject to the terms of the Mozilla Public
5 * License, v. 2.0. If a copy of the MPL was not distributed with this
6 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
7 */
8
9/// Addition operator which returns the closest possible value in the event of an overflow or
10/// underflow.
11pub trait SaturatingAdd<Rhs = Self> {
12    type Output;
13    fn saturating_add(self, rhs: Rhs) -> Self::Output;
14}
15
16/// Multiplication operator which returns the closest possible value in the event of an overflow or
17/// underflow.
18pub trait SaturatingMul<Rhs = Self> {
19    type Output;
20    fn saturating_mul(self, rhs: Rhs) -> Self::Output;
21}
22
23/// Negation operator which returns the closest possible value in the event of an overflow or
24/// underflow.
25pub trait SaturatingNeg {
26    type Output;
27    fn saturating_neg(self) -> Self::Output;
28}
29
30/// Subtraction operator which returns the closest possible value in the event of an overflow or
31/// underflow.
32pub trait SaturatingSub<Rhs = Self> {
33    type Output;
34    fn saturating_sub(self, rhs: Rhs) -> Self::Output;
35}
36
37//--------------------------------------------------------------------------------------------------
38
39/// Implements unary operators for reference types.
40macro_rules! impl_unary_ref_ops {
41    { $(impl $trait:ident::$fn:ident for $ty:ident;)* } => { $(
42        impl<'a> $trait for &'a $ty {
43            type Output = $ty;
44
45            fn $fn(self) -> $ty {
46                $trait::$fn(*self)
47            }
48        }
49    )* };
50}
51
52/// Implements binary saturating operators.
53macro_rules! impl_binary_ops {
54    { $(impl $trait:ident::$fn:ident for $ty:ident;)* } => { $(
55        impl $trait for $ty {
56            type Output = $ty;
57
58            fn $fn(self, rhs: $ty) -> $ty {
59                self.$fn(rhs)
60            }
61        }
62
63        impl<'a> $trait<$ty> for &'a $ty {
64            type Output = $ty;
65
66            fn $fn(self, rhs: $ty) -> $ty {
67                $trait::$fn(*self, rhs)
68            }
69        }
70
71        impl<'r> $trait<&'r $ty> for $ty {
72            type Output = $ty;
73
74            fn $fn(self, rhs: &'r $ty) -> $ty {
75                $trait::$fn(self, *rhs)
76            }
77        }
78
79        impl<'a, 'r> $trait<&'r $ty> for &'a $ty {
80            type Output = $ty;
81
82            fn $fn(self, rhs: &'r $ty) -> $ty {
83                $trait::$fn(*self, *rhs)
84            }
85        }
86    )* };
87}
88
89/// Implements saturating operators for signed integer types.
90macro_rules! impl_int_ops {
91    ($($ty:ident),*) => { $(
92        impl SaturatingNeg for $ty {
93            type Output = $ty;
94
95            fn saturating_neg(self) -> $ty {
96                self.saturating_neg()
97            }
98        }
99
100        impl_unary_ref_ops! {
101            impl SaturatingNeg::saturating_neg for $ty;
102        }
103
104        impl_binary_ops! {
105            impl SaturatingAdd::saturating_add for $ty;
106            impl SaturatingMul::saturating_mul for $ty;
107            impl SaturatingSub::saturating_sub for $ty;
108        }
109    )* };
110}
111
112impl_int_ops!(i8, i16, i32, i64, i128, isize);
113
114/// Implements saturating operators for unsigned integer types.
115macro_rules! impl_uint_ops {
116    ($($ty:ident),*) => { $(
117        impl_binary_ops! {
118            impl SaturatingAdd::saturating_add for $ty;
119            impl SaturatingMul::saturating_mul for $ty;
120            impl SaturatingSub::saturating_sub for $ty;
121        }
122    )* };
123}
124
125impl_uint_ops!(u8, u16, u32, u64, u128, usize);
126
127//--------------------------------------------------------------------------------------------------
128
129#[test]
130fn test_saturating_add() {
131    assert_eq!(SaturatingAdd::saturating_add(100i8, 26), 126);
132    assert_eq!(SaturatingAdd::saturating_add(100i8, 27), 127);
133    assert_eq!(SaturatingAdd::saturating_add(100i8, 28), 127);
134    assert_eq!(SaturatingAdd::saturating_add(-100i8, -27), -127);
135    assert_eq!(SaturatingAdd::saturating_add(-100i8, -28), -128);
136    assert_eq!(SaturatingAdd::saturating_add(-100i8, -29), -128);
137    assert_eq!(SaturatingAdd::saturating_add(200u8, 54), 254);
138    assert_eq!(SaturatingAdd::saturating_add(200u8, 55), 255);
139    assert_eq!(SaturatingAdd::saturating_add(200u8, 56), 255);
140}
141
142#[test]
143fn test_saturating_mul() {
144    assert_eq!(SaturatingMul::saturating_mul(50i8, 2), 100);
145    assert_eq!(SaturatingMul::saturating_mul(50i8, 3), 127);
146    assert_eq!(SaturatingMul::saturating_mul(50i8, -2), -100);
147    assert_eq!(SaturatingMul::saturating_mul(50i8, -3), -128);
148    assert_eq!(SaturatingMul::saturating_mul(-50i8, -2), 100);
149    assert_eq!(SaturatingMul::saturating_mul(-50i8, -3), 127);
150    assert_eq!(SaturatingMul::saturating_mul(50u8, 5), 250);
151    assert_eq!(SaturatingMul::saturating_mul(50u8, 6), 255);
152}
153
154#[test]
155fn test_saturating_neg() {
156    assert_eq!(SaturatingNeg::saturating_neg(127i8), -127);
157    assert_eq!(SaturatingNeg::saturating_neg(-127i8), 127);
158    assert_eq!(SaturatingNeg::saturating_neg(-128i8), 127);
159}
160
161#[test]
162fn test_saturating_sub() {
163    assert_eq!(SaturatingSub::saturating_sub(100i8, -26), 126);
164    assert_eq!(SaturatingSub::saturating_sub(100i8, -27), 127);
165    assert_eq!(SaturatingSub::saturating_sub(100i8, -28), 127);
166    assert_eq!(SaturatingSub::saturating_sub(-100i8, 27), -127);
167    assert_eq!(SaturatingSub::saturating_sub(-100i8, 28), -128);
168    assert_eq!(SaturatingSub::saturating_sub(-100i8, 29), -128);
169    assert_eq!(SaturatingSub::saturating_sub(100u8, 99), 1);
170    assert_eq!(SaturatingSub::saturating_sub(100u8, 100), 0);
171    assert_eq!(SaturatingSub::saturating_sub(100u8, 101), 0);
172}