Skip to main content

nanvm_lib/vm/bigint/
sub.rs

1use core::ops::Sub;
2
3use crate::vm::{Any, BigInt, IVm};
4
5/// `-`, or the `RangeError` of a difference longer than `MAX_WORDS` words, as
6/// `+`, `*` and `<<` throw for a result that long.
7impl<A: IVm> Sub for BigInt<A> {
8    type Output = Result<Self, Any<A>>;
9    fn sub(self, rhs: Self) -> Self::Output {
10        let rhs_sign = rhs.sign().flip();
11        self.add_signed(rhs, rhs_sign).within_limit()
12    }
13}
14
15#[cfg(test)]
16mod tests {
17    use crate::vm::{
18        bigint::test_util::{T, int, pos, pow2},
19        error,
20    };
21
22    #[test]
23    fn same_signs_rhs_magnitude_greater() {
24        assert_eq!(int(3) - int(5), Ok(int(-2)));
25        assert_eq!(int(-3) - int(-5), Ok(int(2)));
26    }
27
28    #[test]
29    fn same_signs_lhs_magnitude_greater() {
30        assert_eq!(int(5) - int(3), Ok(int(2)));
31        assert_eq!(int(-5) - int(-3), Ok(int(-2)));
32    }
33
34    #[test]
35    fn cancellation_to_zero() {
36        assert_eq!(int(7) - int(7), Ok(T::default()));
37        assert_eq!(int(-7) - int(-7), Ok(T::default()));
38    }
39
40    #[test]
41    fn different_signs() {
42        assert_eq!(int(3) - int(-5), Ok(int(8)));
43        assert_eq!(int(-3) - int(5), Ok(int(-8)));
44    }
45
46    #[test]
47    fn multi_word_borrow() {
48        // 2^64 - 1 = u64::MAX
49        let a = pos([0, 1]);
50        let b: T = 1u64.into();
51        let expected: T = u64::MAX.into();
52        assert_eq!(a - b, Ok(expected));
53    }
54
55    /// `2^1048575` is `MAX_WORDS` words; minus its negation is one word more.
56    #[test]
57    fn difference_at_and_past_the_limit() {
58        assert_eq!(pow2(1_048_575) - int(0), Ok(pow2(1_048_575)));
59        assert_eq!(
60            pow2(1_048_575) - -pow2(1_048_575),
61            Err(error::bigint_too_large())
62        );
63        assert_eq!(
64            -pow2(1_048_575) - pow2(1_048_575),
65            Err(error::bigint_too_large())
66        );
67    }
68}