upcast_arithmetic/
lib.rs

1//! [![Crates.io](https://img.shields.io/crates/v/upcast-arithmetic)](https://crates.io/crates/upcast-arithmetic)
2//! [![docs.rs](https://img.shields.io/docsrs/upcast-arithmetic)](https://docs.rs/upcast-arithmetic/latest/upcast_arithmetic/)
3//! [![GitHub license](https://img.shields.io/github/license/umgefahren/upcast-arithmetic)](https://github.com/umgefahren/upcast-arithmetic/blob/main/LICENSE)
4//! [![Rust](https://github.com/umgefahren/upcast-arithmetic/actions/workflows/rust.yml/badge.svg)](https://github.com/umgefahren/upcast-arithmetic/actions/workflows/rust.yml)
5//!
6//! Utility library for dealing with arithmetic on type limits by upcasting into types with higher
7//! limits.
8//!
9//! # Examples
10//!
11//! ##  Without `upcast_arithmetic` (panics)
12//! ```should_panic
13//! let a = u8::MAX;
14//! let b = 2u8;
15//!
16//! let modulo = u8::MAX;
17//!
18//! # #[allow(arithmetic_overflow)]
19//! let res = (a + b) % modulo;
20//! assert_eq!(res, 2);
21//! ```
22//! ## With `upcast_arithmetic`
23//! ```
24//! use upcast_arithmetic::*;
25//!
26//! let a = u8::MAX;
27//! let b = 2u8;
28//!
29//! let modulo = u8::MAX;
30//!
31//! let res = a.upcast_add_mod(b, modulo);
32//! assert_eq!(res, 2);
33//! ```
34//!
35//! # Performance
36//! The performance overhead is very small. In benchmarks it seems like there is only a neglegible
37//! performance penelty, compared to assuming no overflow occurs.
38//!
39//! # `no_std`
40//! The crate is fully `#![no_std]` compatible.
41//!
42//! # Unsafe
43//! There is no unsafe code and the flag `#![deny(unsafe_code)]` is set.
44#![no_std]
45#![cfg_attr(docsrs, feature(doc_cfg))]
46#![warn(clippy::unwrap_in_result)]
47#![warn(rustdoc::all)]
48#![deny(unsafe_code)]
49#![warn(missing_docs)]
50#![warn(clippy::dbg_macro)]
51#![warn(clippy::cargo)]
52#![warn(clippy::perf)]
53#![warn(clippy::pedantic)]
54#![warn(clippy::all)]
55#![allow(rustdoc::missing_doc_code_examples)]
56
57use core::ops::{Add, Mul, MulAssign, Rem, Sub};
58
59#[allow(clippy::doc_markdown)]
60/// Performs checked addition. Maps directly to the integers checked_add method. (i.e.
61/// [`u8::checked_add`]) Consult the docs of the primitive methods to learn more.
62pub trait CheckedAdd: Add + Sized {
63    /// performs checked add
64    /// # Example
65    /// ```
66    /// # use upcast_arithmetic::CheckedAdd;
67    /// let a = 1u8;
68    /// let b = 2u8;
69    /// 
70    /// assert_eq!(CheckedAdd::checked_add(a, b), a.checked_add(b));
71    /// let b = u8::MAX;
72    /// assert_eq!(CheckedAdd::checked_add(a, b), a.checked_add(b));
73    /// ```
74    fn checked_add(self, rhs: Self) -> Option<Self>;
75}
76
77#[allow(clippy::doc_markdown)]
78/// Performs checked substraction. Maps directly to the integers checked_sub method. (i.e.
79/// [`u8::checked_sub`]) Consult the docs of the primitive methods to learn more.
80pub trait CheckedSub: Sub + Sized {
81    /// performs checked sub
82    /// # Example 
83    /// ```
84    /// # use upcast_arithmetic::CheckedSub;
85    /// let a = 2u8;
86    /// let b = 1u8;
87    ///
88    /// assert_eq!(CheckedSub::checked_sub(a, b), a.checked_sub(b));
89    /// assert_eq!(CheckedSub::checked_sub(b, a), b.checked_sub(a));
90    fn checked_sub(self, rhs: Self) -> Option<Self>;
91}
92
93#[allow(clippy::doc_markdown)]
94/// Performs checked multiplication. Maps directly to the integers checked_mul method. (i.e.
95/// [`u8::checked_mul`]) Consult the docs of the primitive methods to learn more.
96pub trait CheckedMul: Mul + Sized {
97    /// performs checked mul
98    /// # Example 
99    /// ```
100    /// # use upcast_arithmetic::CheckedMul;
101    /// let a = 2u8;
102    /// let b = 1u8;
103    ///
104    /// assert_eq!(CheckedMul::checked_mul(a, b), a.checked_mul(b));
105    /// assert_eq!(CheckedMul::checked_mul(a, u8::MAX), a.checked_mul(u8::MAX));
106    fn checked_mul(self, rhs: Self) -> Option<Self>;
107}
108
109#[allow(clippy::doc_markdown)]
110/// Performs checked power. Maps directly to the integers checked_pow method. (i.e.
111/// [`u8::checked_pow`]) Consult the docs of the primitive methods to learn more.
112pub trait CheckedPow: Sized {
113    /// performs checked pow
114    fn checked_pow(self, exp: u32) -> Option<Self>;
115}
116
117macro_rules! checked_impl {
118    ($t:ty) => {
119        impl CheckedAdd for $t {
120            #[inline]
121            fn checked_add(self, rhs: Self) -> Option<Self> {
122                Self::checked_add(self, rhs)
123            }
124        }
125
126        impl CheckedSub for $t {
127            #[inline]
128            fn checked_sub(self, rhs: Self) -> Option<Self> {
129                Self::checked_sub(self, rhs)
130            }
131        }
132
133        impl CheckedMul for $t {
134            #[inline]
135            fn checked_mul(self, rhs: Self) -> Option<Self> {
136                Self::checked_mul(self, rhs)
137            }
138        }
139
140        impl CheckedPow for $t {
141            #[inline]
142            fn checked_pow(self, exp: u32) -> Option<Self> {
143                Self::checked_pow(self, exp)
144            }
145        }
146    };
147}
148
149checked_impl!(u8);
150checked_impl!(u16);
151checked_impl!(u32);
152checked_impl!(u64);
153checked_impl!(i8);
154checked_impl!(i16);
155checked_impl!(i32);
156checked_impl!(i64);
157
158/// Performs power (self ^ exp). Maps directly to the underlaying integer method.
159///
160/// # Panics
161///
162/// Panics if the calculation overflows.
163pub trait Pow: Sized {
164    /// performs power operation
165    #[must_use]
166    fn pow(self, exp: u32) -> Self;
167}
168
169macro_rules! pow_impl {
170    ($t:ty) => {
171        impl Pow for $t {
172            #[inline]
173            fn pow(self, exp: u32) -> Self {
174                Self::pow(self, exp)
175            }
176        }
177    };
178}
179
180pow_impl!(u8);
181pow_impl!(u16);
182pow_impl!(u32);
183pow_impl!(u64);
184pow_impl!(u128);
185pow_impl!(i8);
186pow_impl!(i16);
187pow_impl!(i32);
188pow_impl!(i64);
189pow_impl!(i128);
190
191/// Performs upcasting to type with higher (and/or lower) limits.
192///
193/// i.e. casts u8 to u16
194pub trait Upcast: Sized {
195    /// The higher type casted to (if Self is u8, this is u16).
196    type Higher;
197
198    /// Casts self to higher.
199    fn upcast(self) -> Self::Higher;
200    /// Attempts to cast down to lower from higher. Returns [`Option::None`] if inp exceeds the
201    /// limits of Self.
202    fn downcast(inp: Self::Higher) -> Option<Self>;
203}
204
205macro_rules! upcast_impl {
206    ($t:ty, $h:ty) => {
207        impl Upcast for $t {
208            type Higher = $h;
209
210            #[inline]
211            fn upcast(self) -> Self::Higher {
212                self.into()
213            }
214
215            #[inline]
216            fn downcast(inp: Self::Higher) -> Option<Self> {
217                inp.try_into().ok()
218            }
219        }
220    };
221}
222
223upcast_impl!(u8, u16);
224upcast_impl!(u16, u32);
225upcast_impl!(u32, u64);
226upcast_impl!(u64, u128);
227upcast_impl!(i8, i16);
228upcast_impl!(i16, i32);
229upcast_impl!(i32, i64);
230upcast_impl!(i64, i128);
231
232/// Trait for the one value of a type.
233pub trait One {
234    /// Associated constant.
235    const ONE: Self;
236}
237
238macro_rules! one_impl {
239    ($t:ty) => {
240        impl One for $t {
241            const ONE: Self = 1;
242        }
243    };
244}
245
246one_impl!(u8);
247one_impl!(u16);
248one_impl!(u32);
249one_impl!(u64);
250one_impl!(u128);
251one_impl!(i8);
252one_impl!(i16);
253one_impl!(i32);
254one_impl!(i64);
255one_impl!(i128);
256
257/// Trait to implement upcast add. (Casting up when type bounds are hit during arithmetic)
258pub trait UpcastAdd<H: Add<Output = H> + Rem<Output = H>>:
259    Upcast<Higher = H> + CheckedAdd + Copy
260{
261    /// performs the operation `self + rhs` and returns value as type H.
262    ///
263    /// ```
264    /// # use upcast_arithmetic::UpcastAdd;
265    /// let a = u8::MAX;
266    /// let b = u8::MAX;
267    ///
268    /// // a + b would panic
269    /// let res = a.upcast_add(b);
270    ///
271    /// let expected = (a as u16) * 2;
272    /// assert_eq!(res, expected);
273    /// ```
274    #[must_use]
275    #[inline]
276    fn upcast_add(self, rhs: Self) -> H {
277        self.checked_add(rhs)
278            .map_or_else(|| self.upcast() + rhs.upcast(), Upcast::upcast)
279    }
280
281    /// performs the operation `(self + rhs) % modulo` and returns the value as type Self.
282    ///
283    /// ```
284    /// # use upcast_arithmetic::UpcastAdd;
285    /// let a = u8::MAX;
286    /// let b = 10;
287    /// let modulo = u8::MAX;
288    ///
289    /// let expected = b;
290    ///
291    /// let res = a.upcast_add_mod(b, modulo);
292    /// assert_eq!(res, expected);
293    #[must_use]
294    #[inline]
295    fn upcast_add_mod(self, rhs: Self, modulo: Self) -> Self {
296        Self::downcast(Self::upcast_add(self, rhs).rem(modulo.upcast())).unwrap()
297    }
298}
299
300/// Trait to implement upcast sub. (Casting up when type bounds are hit during arithmetic)
301pub trait UpcastSub<H: Sub<Output = H> + Rem<Output = H>>:
302    Upcast<Higher = H> + CheckedSub + Copy
303{
304    /// performs the operatoin `self - rhs` and returns value as type H.
305    ///
306    /// ```
307    /// # use upcast_arithmetic::UpcastSub;
308    /// let a = i8::MIN;
309    /// let b = 10;
310    ///
311    /// let expected = i8::MIN as i16 - 10;
312    ///
313    /// let res = a.upcast_sub(b);
314    /// assert_eq!(res, expected);
315    /// ```
316    ///
317    /// # Panics
318    /// Panics if the operation still exceeds the higher types limit.
319    ///
320    /// ```should_panic
321    /// # use upcast_arithmetic::UpcastSub;
322    /// let a = 1u8;
323    /// let b = 2u8;
324    ///
325    /// let _ = a.upcast_sub(b);
326    /// ```
327    #[must_use]
328    #[inline]
329    fn upcast_sub(self, rhs: Self) -> H {
330        self.checked_sub(rhs)
331            .map_or_else(|| self.upcast() - rhs.upcast(), Upcast::upcast)
332    }
333
334    /// performs the operation `(self - rhs) % modulo` and returns value as type Self.
335    ///
336    /// ```
337    /// # use upcast_arithmetic::UpcastSub;
338    /// let a = i8::MIN;
339    /// let b = 10;
340    /// let modulo = i8::MIN;
341    ///
342    /// let expected = -10;
343    ///
344    /// let res = a.upcast_sub_mod(b, modulo);
345    /// assert_eq!(res, expected);
346    /// ```
347    #[must_use]
348    #[inline]
349    fn upcast_sub_mod(self, rhs: Self, modulo: Self) -> Self {
350        Self::downcast(Self::upcast_sub(self, rhs).rem(modulo.upcast())).unwrap()
351    }
352}
353
354/// Trait to implement upcast mul. (Casting up when type bounds are hit during arithmetic)
355pub trait UpcastMul<H: Mul<Output = H> + Rem<Output = H>>:
356    Upcast<Higher = H> + CheckedMul + Rem<Output = Self> + Copy
357{
358    /// performs the operation `self * rhs` and returns a value of type H.
359    ///
360    /// ```
361    /// # use upcast_arithmetic::UpcastMul;
362    /// let a = u8::MAX;
363    /// let b = 2;
364    ///
365    /// let expected = u8::MAX as u16 * 2;
366    ///
367    /// let res = a.upcast_mul(b);
368    /// assert_eq!(res, expected);
369    /// ```
370    #[must_use]
371    #[inline]
372    fn upcast_mul(self, rhs: Self) -> H {
373        self.checked_mul(rhs)
374            .map_or_else(|| self.upcast() * rhs.upcast(), Upcast::upcast)
375    }
376
377    /// performs the operation `(self * rhs) % modulo` and returns the a value of type Self.
378    #[must_use]
379    #[inline]
380    fn upcast_mul_mod(self, rhs: Self, modulo: Self) -> Self {
381        Self::downcast(Self::upcast_mul(self, rhs).rem(modulo.upcast())).unwrap()
382    }
383}
384
385/// Trait to perform upcast pow. (Casting up when type bounds are hit during arithmetic)
386///
387/// # Why the additional bounds?
388/// The algorithm to perform `(self % rhs) % modulo` is not as straightforward as the other
389/// algorithms. The reason behind this is a more complex algorithm used to calculate `(self * rhs)
390/// % modulo`.
391pub trait UpcastPow<H>: Upcast<Higher = H> + CheckedPow + Copy + Rem<Output = Self>
392where
393    H: Pow + One + PartialOrd + Rem<Output = H> + MulAssign + Copy,
394{
395    /// performs the operation `self * rhs` and returns a value of type H.
396    #[must_use]
397    #[inline]
398    fn upcast_pow(self, exp: u32) -> H {
399        self.checked_pow(exp)
400            .map_or_else(|| self.upcast().pow(32), Upcast::upcast)
401    }
402
403    /// performs the operation `(self * rhs) % modulo` and returns a value of type Self.
404    ///
405    /// The implementation uses the [Right-to-left binary
406    /// method](https://en.wikipedia.org/wiki/Modular_exponentiation#Right-to-left_binary_method).
407    #[must_use]
408    #[inline]
409    fn upcast_pow_mod(self, mut exp: u32, modulo: Self) -> Self {
410        self.checked_pow(exp).map_or_else(
411            || {
412                let mut res: H = H::ONE;
413                let mut x_upcast = self.upcast();
414                while exp > 0 {
415                    if exp % 2 == 1 {
416                        res *= x_upcast;
417                    }
418                    exp >>= 1;
419                    x_upcast *= x_upcast;
420                }
421                Self::downcast(res % modulo.upcast()).unwrap()
422            },
423            |e| e % modulo,
424        )
425    }
426}
427
428macro_rules! upcast_arith_impl {
429    ($t:ty, $h:ty) => {
430        impl UpcastAdd<$h> for $t {}
431        impl UpcastMul<$h> for $t {}
432        impl UpcastPow<$h> for $t {}
433        impl UpcastSub<$h> for $t {}
434    };
435}
436
437upcast_arith_impl!(u8, u16);
438upcast_arith_impl!(u16, u32);
439upcast_arith_impl!(u32, u64);
440upcast_arith_impl!(u64, u128);
441upcast_arith_impl!(i8, i16);
442upcast_arith_impl!(i16, i32);
443upcast_arith_impl!(i32, i64);
444upcast_arith_impl!(i64, i128);
445
446/// utilities for performing upcast arithmetic in `const`.
447#[cfg(any(feature = "const", doc))]
448#[cfg_attr(docsrs, doc(cfg(feature = "const")))]
449#[allow(rustdoc::missing_doc_code_examples)]
450pub mod constant {
451    macro_rules! gen_upcast_arith {
452        ($t:ty, $h:ty, $i:ident, $o:ident, $m:ident, $e:tt, $d:meta, $mo:meta) => {
453            #[$d]
454            #[must_use]
455            pub const fn $i(lhs: $t, rhs: $t) -> $h {
456                match lhs.$m(rhs) {
457                    Some(e) => e as $h,
458                    None => (lhs as $h) $e (rhs as $h),
459                }
460            }
461
462            #[$mo]
463            #[must_use]
464            pub const fn $o(lhs: $t, rhs: $t, modulo: $t) -> $t {
465                match lhs.$m(rhs) {
466                    Some(e) => e % modulo,
467                    None => (((lhs as $h) $e (rhs as $h)) % (modulo as $h)) as $t,
468                }
469            }
470        };
471    }
472    macro_rules! gen_upcast_add_impl {
473        ($t:ty, $h:ty, $i:ident, $o:ident) => {
474            gen_upcast_arith!($t, $h, $i, $o, checked_add, +, doc = "const equivalent of [`UpcastAdd::upcast_add`](crate::UpcastAdd::upcast_add).", doc = "const equivalent of [`UpcastAdd::upcast_add_mod`](crate::UpcastAdd::upcast_add_mod).");
475        };
476    }
477    macro_rules! gen_upcast_sub_impl {
478        ($t:ty, $h:ty, $i:ident, $o:ident) => {
479            gen_upcast_arith!($t, $h, $i, $o, checked_sub, -, doc = "const equivalent of [`UpcastSub::upcast_sub`](crate::UpcastSub::upcast_sub).", doc = "const equivalent of [`UpcastSub::upcast_sub_mod`](crate::UpcastSub::upcast_sub_mod).");
480        };
481    }
482
483    macro_rules! gen_upcast_mul_impl {
484        ($t:ty, $h:ty, $i:ident, $o:ident) => {
485            gen_upcast_arith!($t, $h, $i, $o, checked_mul, *, doc = "const equivalent of [`UpcastMul::upcast_mul`](crate::UpcastMul::upcast_mul).", doc = "const equivalent of [`UpcastMul::upcast_mul_mod`](crate::UpcastMul::upcast_mul_mod).");
486        };
487    }
488
489    gen_upcast_add_impl!(u8, u16, upcast_add_u8, upcast_add_mod_u8);
490    gen_upcast_add_impl!(u16, u32, upcast_add_u16, upcast_add_mod_u16);
491    gen_upcast_add_impl!(u32, u64, upcast_add_u32, upcast_add_mod_u32);
492    gen_upcast_add_impl!(u64, u128, upcast_add_u64, upcast_add_mod_u64);
493    gen_upcast_add_impl!(i8, i16, upcast_add_i8, upcast_add_mod_i8);
494    gen_upcast_add_impl!(i16, i32, upcast_add_i16, upcast_add_mod_i16);
495    gen_upcast_add_impl!(i32, i64, upcast_add_i32, upcast_add_mod_i32);
496    gen_upcast_add_impl!(i64, i128, upcast_add_i64, upcast_add_mod_i64);
497
498    gen_upcast_sub_impl!(u8, u16, upcast_sub_u8, upcast_sub_mod_u8);
499    gen_upcast_sub_impl!(u16, u32, upcast_sub_u16, upcast_sub_mod_u16);
500    gen_upcast_sub_impl!(u32, u64, upcast_sub_u32, upcast_sub_mod_u32);
501    gen_upcast_sub_impl!(u64, u128, upcast_sub_u64, upcast_sub_mod_u64);
502    gen_upcast_sub_impl!(i8, i16, upcast_sub_i8, upcast_sub_mod_i8);
503    gen_upcast_sub_impl!(i16, i32, upcast_sub_i16, upcast_sub_mod_i16);
504    gen_upcast_sub_impl!(i32, i64, upcast_sub_i32, upcast_sub_mod_i32);
505    gen_upcast_sub_impl!(i64, i128, upcast_sub_i64, upcast_sub_mod_i64);
506
507    gen_upcast_mul_impl!(u8, u16, upcast_mul_u8, upcast_mul_mod_u8);
508    gen_upcast_mul_impl!(u16, u32, upcast_mul_u16, upcast_mul_mod_u16);
509    gen_upcast_mul_impl!(u32, u64, upcast_mul_u32, upcast_mul_mod_u32);
510    gen_upcast_mul_impl!(u64, u128, upcast_mul_u64, upcast_mul_mod_u64);
511    gen_upcast_mul_impl!(i8, i16, upcast_mul_i8, upcast_mul_mod_i8);
512    gen_upcast_mul_impl!(i16, i32, upcast_mul_i16, upcast_mul_mod_i16);
513    gen_upcast_mul_impl!(i32, i64, upcast_mul_i32, upcast_mul_mod_i32);
514    gen_upcast_mul_impl!(i64, i128, upcast_mul_i64, upcast_mul_mod_i64);
515}