1use num::{CheckedAdd, CheckedDiv, CheckedMul, CheckedSub};
2
3use primitive_types::U256;
4use thiserror::Error;
5
6#[derive(Error, Debug, PartialEq)]
7pub enum MathError {
8 #[error("Addition overflowed")]
9 AdditionOverflow,
10 #[error("Subtraction overflowed")]
11 SubtractionOverflow,
12 #[error("Multiplication overflowed")]
13 MultiplicationOverflow,
14 #[error("Division overflowed")]
15 DivisionOverflow,
16 #[error("Failed to convert a math result from U256 back to its original type")]
17 ConversionError,
18}
19
20pub fn safe_add<T>(a: T, b: T) -> Result<T, MathError>
21where
22 T: CheckedAdd,
23{
24 a.checked_add(&b).ok_or(MathError::AdditionOverflow)
25}
26
27pub fn safe_sub<T>(a: T, b: T) -> Result<T, MathError>
28where
29 T: CheckedSub,
30{
31 a.checked_sub(&b).ok_or(MathError::SubtractionOverflow)
32}
33
34pub fn safe_mul<T>(a: T, b: T) -> Result<T, MathError>
35where
36 T: CheckedMul,
37{
38 a.checked_mul(&b).ok_or(MathError::MultiplicationOverflow)
39}
40
41pub fn safe_div<T>(a: T, b: T) -> Result<T, MathError>
42where
43 T: CheckedDiv,
44{
45 a.checked_div(&b).ok_or(MathError::DivisionOverflow)
46}
47
48pub fn mul_div<T>(mul_a: T, mul_b: T, div: T) -> Result<T, MathError>
49where
50 T: TryFrom<U256>,
51 U256: From<T>,
52{
53 let a = U256::from(mul_a);
54 let b = U256::from(mul_b);
55
56 let mul = a.checked_mul(b).ok_or(MathError::MultiplicationOverflow)?;
57 let div = U256::from(div);
58
59 let res = mul.checked_div(div).ok_or(MathError::DivisionOverflow)?;
60
61 res.try_into().map_err(|_| MathError::ConversionError)
62}