cnfy-uint 0.2.3

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

impl U256 {
    /// Computes `self - other` by packing limb pairs into `u128` for
    /// native `sub` + `sbb` borrow chaining, returning the 256-bit
    /// result and a boolean indicating whether the subtraction
    /// underflowed (i.e., `self < other`).
    ///
    /// Using `u128` lets LLVM emit a single `sbb` for the intra-pair
    /// borrow instead of two `overflowing_sub` calls with a bool
    /// intermediate, reducing the operation count from 7 subs + 7 setc
    /// + 3 or to 3 subs + 3 sbb + 3 setc.
    ///
    /// # Examples
    ///
    /// ```
    /// use cnfy_uint::u256::U256;
    ///
    /// let a = U256::from_be_limbs([0, 0, 0, 10]);
    /// let b = U256::from_be_limbs([0, 0, 0, 3]);
    /// let (result, underflow) = a.overflowing_sub(&b);
    /// assert_eq!(result, U256::from_be_limbs([0, 0, 0, 7]));
    /// assert!(!underflow);
    /// ```
    #[inline]
    pub const fn overflowing_sub(&self, other: &U256) -> (U256, 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, borrow) = a_lo.overflowing_sub(b_lo);
        let (diff_hi, u1) = a_hi.overflowing_sub(b_hi);
        let (diff_hi, u2) = diff_hi.overflowing_sub(borrow as u128);

        (
            U256([
                diff_lo as u64,
                (diff_lo >> 64) as u64,
                diff_hi as u64,
                (diff_hi >> 64) as u64,
            ]),
            u1 | u2,
        )
    }
}

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

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

    /// Subtracting a larger value underflows and wraps.
    #[test]
    fn underflow_wraps() {
        let a = U256::from_be_limbs([0, 0, 0, 0]);
        let b = U256::from_be_limbs([0, 0, 0, 1]);
        let (result, underflow) = a.overflowing_sub(&b);
        assert_eq!(
            result,
            U256::from_be_limbs([u64::MAX, u64::MAX, u64::MAX, u64::MAX]),
        );
        assert!(underflow);
    }

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

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