af_utilities/types/
i256.rs

1use std::cmp::Ordering;
2use std::ops::{
3    Add,
4    AddAssign,
5    Div,
6    DivAssign,
7    Mul,
8    MulAssign,
9    Neg,
10    Rem,
11    RemAssign,
12    Sub,
13    SubAssign,
14};
15
16use af_sui_types::u256::U256;
17use num_traits::{One, Zero};
18use serde::{Deserialize, Serialize};
19
20use super::errors::Error;
21use super::onchain;
22
23#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, Deserialize, Serialize)]
24pub struct I256(U256);
25
26impl std::fmt::Display for I256 {
27    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
28        if self.is_neg() {
29            write!(f, "-{}", self.uabs())
30        } else {
31            write!(f, "{}", self.0)
32        }
33    }
34}
35
36macro_rules! impl_from_uint {
37    ($($int:ty)*) => {
38        $(
39            impl From<$int> for I256 {
40                fn from(value: $int) -> Self {
41                    Self(U256::from(value))
42                }
43            }
44        )*
45    };
46}
47
48impl_from_uint!(u8 u16 u32 u64 u128);
49
50macro_rules! impl_from_int {
51    ($($int:ty)*) => {
52        $(
53            impl From<$int> for I256 {
54                fn from(value: $int) -> Self {
55                    let is_neg = value.is_negative();
56                    let abs = Self::from(value.unsigned_abs());
57                    match is_neg {
58                        true => abs.neg(),
59                        false => abs,
60                    }
61                }
62            }
63        )*
64    };
65}
66
67impl_from_int!(i8 i16 i32 i64 i128);
68
69macro_rules! impl_try_into_int {
70    ($($bridge:ty => $int:ty),*) => {
71        $(
72            impl TryFrom<I256> for $int {
73                type Error = Error;
74
75                fn try_from(value: I256) -> Result<Self, Self::Error> {
76                    let is_neg = value.is_neg();
77                    let bridge: $bridge = value.uabs().try_into().map_err(|_| Error::Overflow)?;
78                    let self_: Self = bridge.try_into().map_err(|_| Error::Overflow)?;
79                    Ok(match is_neg {
80                        true => -self_,
81                        false => self_,
82                    })
83                }
84            }
85        )*
86    };
87}
88
89impl_try_into_int!(u8 => i8, u16 => i16, u32 => i32, u64 => i64, u128 => i128);
90
91macro_rules! impl_try_into_uint {
92    ($($int:ty)*) => {
93        $(
94            impl TryFrom<I256> for $int {
95                type Error = Error;
96
97                fn try_from(value: I256) -> Result<Self, Self::Error> {
98                    if value.is_neg() {
99                        return Err(Error::Underflow);
100                    }
101                    value.uabs().try_into().map_err(|_| Error::Overflow)
102                }
103            }
104        )*
105    };
106}
107
108impl_try_into_uint!(u8 u16 u32 u64 u128);
109
110impl TryFrom<U256> for I256 {
111    type Error = Error;
112
113    fn try_from(value: U256) -> Result<Self, Self::Error> {
114        if value <= onchain::max_i256() {
115            Ok(Self(value))
116        } else {
117            Err(Error::Overflow)
118        }
119    }
120}
121
122impl TryFrom<I256> for U256 {
123    type Error = Error;
124
125    fn try_from(value: I256) -> Result<Self, Self::Error> {
126        if value.is_neg() {
127            return Err(Error::Underflow);
128        }
129        Ok(value.0)
130    }
131}
132
133impl Add for I256 {
134    type Output = Self;
135
136    fn add(self, rhs: Self) -> Self::Output {
137        let Self(x) = self;
138        let Self(y) = rhs;
139        let greatest_bit = Self::greatest_bit();
140        let not_greatest_bit = Self::not_greatest_bit();
141
142        // First, compute sum of x and y except the greatest bit.
143        let w = (x & not_greatest_bit) + (y & not_greatest_bit);
144        Self(if x ^ y < greatest_bit {
145            // The signs of x and y are the same, so the result sign must also be the same
146            // for no overflow.
147            // assert!(x ^ w < greatest_bit, overflow_error);
148            w
149        } else {
150            // Overflow cannot happen if the signs are different because sum will be closer
151            // to 0 than an input.
152            w ^ greatest_bit
153        })
154    }
155}
156
157impl Sub for I256 {
158    type Output = Self;
159
160    fn sub(self, rhs: Self) -> Self::Output {
161        let Self(x) = self;
162        let Self(y) = rhs;
163        // First, compute wrapping difference of x and y.
164        let w = if x >= y {
165            x - y
166        } else {
167            ((y - x) ^ Self::neg_one().0) + U256::one()
168        };
169        // assert!(x ^ y < GREATEST_BIT || x ^ w < GREATEST_BIT, OVERFLOW_ERROR);
170        Self(w)
171    }
172}
173
174impl Mul for I256 {
175    type Output = Self;
176
177    fn mul(self, rhs: Self) -> Self::Output {
178        let z = self.uabs() * rhs.uabs();
179        let (Self(x), Self(y)) = (self, rhs);
180        let greatest_bit = Self::greatest_bit();
181
182        Self(if x ^ y < greatest_bit {
183            // assert!(z < greatest_bit, overflow_error);
184            z
185        } else {
186            // assert!(z <= greatest_bit, overflow_error);
187            (greatest_bit - z) ^ greatest_bit
188        })
189    }
190}
191
192impl Div for I256 {
193    type Output = Self;
194
195    fn div(self, rhs: Self) -> Self::Output {
196        let z = self.uabs() / rhs.uabs();
197        let (Self(x), Self(y)) = (self, rhs);
198        let greatest_bit = Self::greatest_bit();
199
200        Self(if x ^ y < greatest_bit {
201            // assert!(z < greatest_bit, overflow_error);
202            z
203        } else {
204            (greatest_bit - z) ^ greatest_bit
205        })
206    }
207}
208
209impl Rem for I256 {
210    type Output = Self;
211
212    fn rem(self, rhs: Self) -> Self::Output {
213        let is_neg = self.is_neg();
214        let abs_rem = Self(self.uabs() % rhs.uabs());
215        match is_neg {
216            true => abs_rem.neg(),
217            false => abs_rem,
218        }
219    }
220}
221
222super::reuse_op_for_assign!(I256 {
223    AddAssign add_assign +,
224    SubAssign sub_assign -,
225    MulAssign mul_assign *,
226    DivAssign div_assign /,
227    RemAssign rem_assign %,
228});
229
230impl PartialOrd for I256 {
231    fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
232        Some(self.cmp(other))
233    }
234}
235
236impl Ord for I256 {
237    fn cmp(&self, other: &Self) -> Ordering {
238        let Self(x) = self;
239        let Self(y) = other;
240
241        if x == y {
242            Ordering::Equal
243        } else if *x ^ Self::greatest_bit() < *y ^ Self::greatest_bit() {
244            Ordering::Less
245        } else {
246            Ordering::Greater
247        }
248    }
249}
250
251impl Neg for I256 {
252    type Output = Self;
253
254    fn neg(self) -> Self::Output {
255        Self(((self.0 ^ Self::not_greatest_bit()) + U256::one()) ^ Self::greatest_bit())
256    }
257}
258
259impl One for I256 {
260    fn one() -> Self {
261        Self::one()
262    }
263}
264
265impl Zero for I256 {
266    fn zero() -> Self {
267        Self(U256::zero())
268    }
269
270    fn is_zero(&self) -> bool {
271        self.0 == U256::zero()
272    }
273}
274
275impl I256 {
276    fn greatest_bit() -> U256 {
277        U256::one() << 255_u8
278    }
279
280    fn not_greatest_bit() -> U256 {
281        (U256::one() << 255_u8) - U256::one()
282    }
283
284    pub const fn neg_one() -> Self {
285        Self(U256::max_value())
286    }
287
288    pub const fn into_inner(self) -> U256 {
289        self.0
290    }
291
292    pub const fn from_inner(inner: U256) -> Self {
293        Self(inner)
294    }
295
296    pub const fn one() -> Self {
297        Self(U256::one())
298    }
299
300    pub const fn zero() -> Self {
301        Self(U256::zero())
302    }
303
304    pub fn is_neg(&self) -> bool {
305        self.0 >= Self::greatest_bit()
306    }
307
308    /// Absolute value of a number.
309    /// Can be thought as function from i256 to u256, so doesn't abort.
310    pub fn uabs(&self) -> U256 {
311        let Self(x) = self;
312        if *x >= Self::greatest_bit() {
313            (*x ^ Self::neg_one().0) + U256::one()
314        } else {
315            *x
316        }
317    }
318
319    pub fn abs(self) -> Self {
320        let Self(x) = self;
321        let greatest_bit = Self::greatest_bit();
322        let not_greatest_bit = Self::not_greatest_bit();
323        Self(if x >= greatest_bit {
324            ((x ^ not_greatest_bit) + U256::one()) ^ greatest_bit
325        } else {
326            x
327        })
328    }
329}
330
331#[cfg(test)]
332#[allow(clippy::unwrap_used)]
333mod tests {
334    use super::*;
335
336    #[test]
337    fn from_u128_max_doesnt_overflow() {
338        assert!(!I256::from(u128::MAX).is_neg())
339    }
340
341    #[test]
342    fn from_i128_min_doesnt_underflow() {
343        assert!(I256::from(i128::MIN).is_neg())
344    }
345}