cnfy-uint 0.2.3

Zero-dependency 256-bit unsigned integer arithmetic for cryptographic applications
Documentation
//! 320-bit subtraction with underflow detection.
use super::U320;

impl U320 {
    /// Computes `self - other`, returning the 320-bit result and a
    /// boolean indicating whether the subtraction underflowed (i.e.,
    /// `other > self` in unsigned terms).
    ///
    /// Packs limb pairs `[0]+[1]` and `[2]+[3]` into `u128` for native
    /// borrow chaining, then handles the top limb `[4]` separately with
    /// the final borrow.
    ///
    /// # Examples
    ///
    /// ```
    /// use cnfy_uint::u320::U320;
    ///
    /// let a = U320::from_be_limbs([0, 0, 0, 0, 10]);
    /// let b = U320::from_be_limbs([0, 0, 0, 0, 3]);
    /// let (result, underflow) = a.overflowing_sub(&b);
    /// assert_eq!(result, U320::from_be_limbs([0, 0, 0, 0, 7]));
    /// assert!(!underflow);
    /// ```
    #[inline]
    pub const fn overflowing_sub(&self, other: &U320) -> (U320, bool) {
        let a_lo = ((self.0[1] as u128) << 64) | (self.0[0] as u128);
        let a_hi = ((self.0[3] as u128) << 64) | (self.0[2] as u128);
        let b_lo = ((other.0[1] as u128) << 64) | (other.0[0] as u128);
        let b_hi = ((other.0[3] as u128) << 64) | (other.0[2] as u128);

        let (diff_lo, borrow1) = a_lo.overflowing_sub(b_lo);
        let (diff_hi, u1) = a_hi.overflowing_sub(b_hi);
        let (diff_hi, u2) = diff_hi.overflowing_sub(borrow1 as u128);

        let (top, t1) = self.0[4].overflowing_sub(other.0[4]);
        let (top, t2) = top.overflowing_sub((u1 | u2) as u64);

        (
            U320([
                diff_lo as u64,
                (diff_lo >> 64) as u64,
                diff_hi as u64,
                (diff_hi >> 64) as u64,
                top,
            ]),
            t1 | t2,
        )
    }
}

#[cfg(test)]
mod ai_tests {
    use super::*;

    /// Subtracting a smaller value produces no underflow.
    #[test]
    fn small_no_underflow() {
        let a = U320::from_be_limbs([0, 0, 0, 0, 10]);
        let b = U320::from_be_limbs([0, 0, 0, 0, 3]);
        let (result, underflow) = a.overflowing_sub(&b);
        assert_eq!(result, U320::from_be_limbs([0, 0, 0, 0, 7]));
        assert!(!underflow);
    }

    /// Subtracting self yields zero with no underflow.
    #[test]
    fn self_cancellation() {
        let a = U320::from_be_limbs([0x1234, 0x5678, 0x9ABC, 0xDEF0, 0x1111]);
        let (result, underflow) = a.overflowing_sub(&a);
        assert_eq!(result, U320::ZERO);
        assert!(!underflow);
    }

    /// Borrow propagates across all five limbs.
    #[test]
    fn borrow_propagation() {
        let a = U320::from_be_limbs([1, 0, 0, 0, 0]);
        let b = U320::ONE;
        let (result, underflow) = a.overflowing_sub(&b);
        assert_eq!(
            result,
            U320::from_be_limbs([0, u64::MAX, u64::MAX, u64::MAX, u64::MAX]),
        );
        assert!(!underflow);
    }

    /// Zero minus one underflows.
    #[test]
    fn underflow_wraps() {
        let (result, underflow) = U320::ZERO.overflowing_sub(&U320::ONE);
        assert_eq!(result, U320::MAX);
        assert!(underflow);
    }

    /// Borrow across the u128-pair boundary.
    #[test]
    fn cross_pair_borrow() {
        let a = U320::from_be_limbs([0, 1, 0, 0, 0]);
        let b = U320::ONE;
        let (result, underflow) = a.overflowing_sub(&b);
        assert_eq!(
            result,
            U320::from_be_limbs([0, 0, u64::MAX, u64::MAX, u64::MAX]),
        );
        assert!(!underflow);
    }

    /// Consistent with existing Sub trait.
    #[test]
    fn consistent_with_sub() {
        let a = U320::from_be_limbs([5, 10, 15, 20, 25]);
        let b = U320::from_be_limbs([1, 2, 3, 4, 5]);
        let (result, _) = a.overflowing_sub(&b);
        assert_eq!(result, a - b);
    }
}