aurum_numeric/
try_ops.rs

1// Copyright (c) 2016-2017 <daggerbot@gmail.com>
2// This software is available under the terms of the zlib license.
3// See COPYING.md for more information.
4
5use std::{i8, i16, i32, i64, isize};
6use std::error::Error;
7use std::fmt::{self, Display, Formatter};
8
9/// Error which occurs when an operation causes integer overflow.
10#[derive(Clone, Copy, Debug, Eq, PartialEq)]
11pub struct IntOverflow;
12
13const INT_OVERFLOW_STR: &'static str = "integer overflow";
14
15impl Display for IntOverflow {
16    fn fmt (&self, f: &mut Formatter) -> fmt::Result {
17        f.write_str(INT_OVERFLOW_STR)
18    }
19}
20
21impl Error for IntOverflow {
22    fn description (&self) -> &str { INT_OVERFLOW_STR }
23}
24
25/// Error which occurs when an operation causes integer underflow.
26#[derive(Clone, Copy, Debug, Eq, PartialEq)]
27pub struct IntUnderflow;
28
29const INT_UNDERFLOW_STR: &'static str = "integer underflow";
30
31impl Display for IntUnderflow {
32    fn fmt (&self, f: &mut Formatter) -> fmt::Result {
33        f.write_str(INT_UNDERFLOW_STR)
34    }
35}
36
37impl Error for IntUnderflow {
38    fn description (&self) -> &str { INT_UNDERFLOW_STR }
39}
40
41/// Error which occurs when an operation causes integer overflow or underflow.
42#[derive(Clone, Copy, Debug, Eq, PartialEq)]
43pub enum IntRangeError {
44    Overflow,
45    Underflow,
46}
47
48impl IntRangeError {
49    fn as_str (self) -> &'static str {
50        match self {
51            IntRangeError::Overflow => INT_OVERFLOW_STR,
52            IntRangeError::Underflow => INT_UNDERFLOW_STR,
53        }
54    }
55}
56
57impl Display for IntRangeError {
58    fn fmt (&self, f: &mut Formatter) -> fmt::Result {
59        f.write_str(self.as_str())
60    }
61}
62
63impl Error for IntRangeError {
64    fn description (&self) -> &str { self.as_str() }
65}
66
67/// Error which may occur on division by zero.
68#[derive(Clone, Copy, Debug, Eq, PartialEq)]
69pub struct DivideByZero;
70
71const DIVIDE_BY_ZERO_STR: &'static str = "divide by zero";
72
73impl Display for DivideByZero {
74    fn fmt (&self, f: &mut Formatter) -> fmt::Result {
75        f.write_str(DIVIDE_BY_ZERO_STR)
76    }
77}
78
79impl Error for DivideByZero {
80    fn description (&self) -> &str { DIVIDE_BY_ZERO_STR }
81}
82
83/// Fallible addition trait.
84pub trait TryAdd<RHS = Self> {
85    type Output;
86    type Err;
87
88    fn try_add (self, rhs: RHS) -> Result<Self::Output, Self::Err>;
89}
90
91macro_rules! impl_try_add_unsigned {
92    ($($ty:ty),*) => { $(
93        impl TryAdd for $ty {
94            type Output = $ty;
95            type Err = IntOverflow;
96
97            fn try_add (self, rhs: $ty) -> Result<$ty, IntOverflow> {
98                match self.checked_add(rhs) {
99                    None => Err(IntOverflow),
100                    Some(n) => Ok(n),
101                }
102            }
103        }
104    )* };
105}
106
107macro_rules! impl_try_add_signed {
108    ($($ty:ty),*) => { $(
109        impl TryAdd for $ty {
110            type Output = $ty;
111            type Err = IntRangeError;
112
113            fn try_add (self, rhs: $ty) -> Result<$ty, IntRangeError> {
114                match self.checked_add(rhs) {
115                    None => {
116                        if rhs > 0 {
117                            Err(IntRangeError::Overflow)
118                        } else {
119                            Err(IntRangeError::Underflow)
120                        }
121                    },
122                    Some(n) => Ok(n),
123                }
124            }
125        }
126    )* };
127}
128
129impl_try_add_unsigned!(u8, u16, u32, u64, usize);
130impl_try_add_signed!(i8, i16, i32, i64, isize);
131
132#[test]
133fn test_try_add () {
134    assert_eq!(254u8.try_add(1), Ok(255u8));
135    assert_eq!(255u8.try_add(1), Err(IntOverflow));
136    assert_eq!(126i8.try_add(1), Ok(127i8));
137    assert_eq!(127i8.try_add(1), Err(IntRangeError::Overflow));
138    assert_eq!((-127i8).try_add(-1), Ok(-128i8));
139    assert_eq!((-128i8).try_add(-1), Err(IntRangeError::Underflow));
140}
141
142/// Fallible subtraction trait.
143pub trait TrySub<RHS = Self> {
144    type Output;
145    type Err;
146
147    fn try_sub (self, rhs: RHS) -> Result<Self::Output, Self::Err>;
148}
149
150macro_rules! impl_try_sub_unsigned {
151    ($($ty:ty),*) => { $(
152        impl TrySub for $ty {
153            type Output = $ty;
154            type Err = IntUnderflow;
155
156            fn try_sub (self, rhs: $ty) -> Result<$ty, IntUnderflow> {
157                match self.checked_sub(rhs) {
158                    None => Err(IntUnderflow),
159                    Some(n) => Ok(n),
160                }
161            }
162        }
163    )* };
164}
165
166macro_rules! impl_try_sub_signed {
167    ($($ty:ty),*) => { $(
168        impl TrySub for $ty {
169            type Output = $ty;
170            type Err = IntRangeError;
171
172            fn try_sub (self, rhs: $ty) -> Result<$ty, IntRangeError> {
173                match self.checked_sub(rhs) {
174                    None => {
175                        if rhs > 0 {
176                            Err(IntRangeError::Underflow)
177                        } else {
178                            Err(IntRangeError::Overflow)
179                        }
180                    },
181                    Some(n) => Ok(n),
182                }
183            }
184        }
185    )* };
186}
187
188impl_try_sub_unsigned!(u8, u16, u32, u64, usize);
189impl_try_sub_signed!(i8, i16, i32, i64, isize);
190
191#[test]
192fn test_try_sub () {
193    assert_eq!(1u8.try_sub(1), Ok(0u8));
194    assert_eq!(0u8.try_sub(1), Err(IntUnderflow));
195    assert_eq!((-127i8).try_sub(1), Ok(-128i8));
196    assert_eq!((-128i8).try_sub(1), Err(IntRangeError::Underflow));
197    assert_eq!(126i8.try_sub(-1), Ok(127i8));
198    assert_eq!(127i8.try_sub(-1), Err(IntRangeError::Overflow));
199}
200
201//TODO
202
203/// Fallible multiplication trait.
204pub trait TryMul<RHS = Self> {
205    type Output;
206    type Err;
207
208    fn try_mul (self, rhs: RHS) -> Result<Self::Output, Self::Err>;
209}
210
211macro_rules! impl_try_mul_unsigned {
212    ($($ty:ty),*) => { $(
213        impl TryMul for $ty {
214            type Output = $ty;
215            type Err = IntOverflow;
216
217            fn try_mul (self, rhs: $ty) -> Result<$ty, IntOverflow> {
218                match self.checked_mul(rhs) {
219                    None => Err(IntOverflow),
220                    Some(n) => Ok(n),
221                }
222            }
223        }
224    )* };
225}
226
227macro_rules! impl_try_mul_signed {
228    ($($ty:ty),*) => { $(
229        impl TryMul for $ty {
230            type Output = $ty;
231            type Err = IntRangeError;
232
233            fn try_mul (self, rhs: $ty) -> Result<$ty, IntRangeError> {
234                match self.checked_mul(rhs) {
235                    None => {
236                        if (self > 0) == (rhs > 0) {
237                            Err(IntRangeError::Overflow)
238                        } else {
239                            Err(IntRangeError::Underflow)
240                        }
241                    },
242                    Some(n) => Ok(n),
243                }
244            }
245        }
246    )* };
247}
248
249impl_try_mul_unsigned!(u8, u16, u32, u64, usize);
250impl_try_mul_signed!(i8, i16, i32, i64, isize);
251
252#[test]
253fn test_try_mul () {
254    assert_eq!(85u8.try_mul(3), Ok(255u8));
255    assert_eq!(64u8.try_mul(4), Err(IntOverflow));
256    assert_eq!(63i8.try_mul(2), Ok(126i8));
257    assert_eq!(64i8.try_mul(2), Err(IntRangeError::Overflow));
258    assert_eq!(64i8.try_mul(-2), Ok(-128i8));
259    assert_eq!(65i8.try_mul(-2), Err(IntRangeError::Underflow));
260    assert_eq!((-64i8).try_mul(2), Ok(-128i8));
261    assert_eq!((-65i8).try_mul(2), Err(IntRangeError::Underflow));
262    assert_eq!((-63i8).try_mul(-2), Ok(126i8));
263    assert_eq!((-64i8).try_mul(-2), Err(IntRangeError::Overflow));
264}
265
266/// Fallible division trait.
267pub trait TryDiv<RHS = Self> {
268    type Output;
269    type Err;
270
271    fn try_div (self, rhs: RHS) -> Result<Self::Output, Self::Err>;
272}
273
274macro_rules! impl_try_div {
275    ($($ty:ty),*) => { $(
276        impl TryDiv for $ty {
277            type Output = $ty;
278            type Err = DivideByZero;
279
280            fn try_div (self, rhs: $ty) -> Result<$ty, DivideByZero> {
281                if rhs == 0 {
282                    Err(DivideByZero)
283                } else {
284                    Ok(self / rhs)
285                }
286            }
287        }
288    )* };
289}
290
291impl_try_div!(u8, u16, u32, u64, usize, i8, i16, i32, i64, isize);
292
293#[test]
294fn test_try_div () {
295    assert_eq!(255u8.try_div(3), Ok(85u8));
296    assert_eq!(255u8.try_div(0), Err(DivideByZero));
297}
298
299/// Fallible division remainder trait.
300pub trait TryRem<RHS = Self> {
301    type Output;
302    type Err;
303
304    fn try_rem (self, rhs: RHS) -> Result<Self::Output, Self::Err>;
305}
306
307macro_rules! impl_try_rem {
308    ($($ty:ty),*) => { $(
309        impl TryRem for $ty {
310            type Output = $ty;
311            type Err = DivideByZero;
312
313            fn try_rem (self, rhs: $ty) -> Result<$ty, DivideByZero> {
314                if rhs == 0 {
315                    Err(DivideByZero)
316                } else {
317                    Ok(self % rhs)
318                }
319            }
320        }
321    )* };
322}
323
324impl_try_rem!(u8, u16, u32, u64, usize, i8, i16, i32, i64, isize);
325
326#[test]
327fn test_try_rem () {
328    assert_eq!(11u8.try_rem(5), Ok(1u8));
329    assert_eq!(11u8.try_rem(0), Err(DivideByZero));
330}
331
332/// Fallible negation trait.
333pub trait TryNeg {
334    type Output;
335    type Err;
336
337    fn try_neg (self) -> Result<Self::Output, Self::Err>;
338}
339
340macro_rules! impl_try_neg {
341    ($($ty:ident),*) => { $(
342        impl TryNeg for $ty {
343            type Output = $ty;
344            type Err = IntOverflow;
345
346            fn try_neg (self) -> Result<$ty, IntOverflow> {
347                if self == $ty::MIN {
348                    Err(IntOverflow)
349                } else {
350                    Ok(-self)
351                }
352            }
353        }
354    )* };
355}
356
357impl_try_neg!(i8, i16, i32, i64, isize);
358
359#[test]
360fn test_try_neg () {
361    assert_eq!((-127i8).try_neg(), Ok(127i8));
362    assert_eq!((-128i8).try_neg(), Err(IntOverflow));
363}