cnfy-uint 0.2.3

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

impl U384 {
    /// Computes `self - other` by packing limb pairs into `u128` for
    /// native borrow chaining, returning the 384-bit result and a boolean
    /// indicating whether the subtraction underflowed (i.e., `self < other`).
    ///
    /// The six limbs are grouped into three `u128` pairs: `[0]+[1]`,
    /// `[2]+[3]`, `[4]+[5]`, with borrows propagated between groups.
    ///
    /// # Examples
    ///
    /// ```
    /// use cnfy_uint::u384::U384;
    ///
    /// let a = U384::from_be_limbs([0, 0, 0, 0, 0, 10]);
    /// let b = U384::from_be_limbs([0, 0, 0, 0, 0, 3]);
    /// let (result, underflow) = a.overflowing_sub(&b);
    /// assert_eq!(result, U384::from_be_limbs([0, 0, 0, 0, 0, 7]));
    /// assert!(!underflow);
    /// ```
    #[inline]
    pub const fn overflowing_sub(&self, other: &U384) -> (U384, bool) {
        let a_lo = ((self.0[1] as u128) << 64) | (self.0[0] as u128);
        let a_mid = ((self.0[3] as u128) << 64) | (self.0[2] as u128);
        let a_hi = ((self.0[5] as u128) << 64) | (self.0[4] as u128);

        let b_lo = ((other.0[1] as u128) << 64) | (other.0[0] as u128);
        let b_mid = ((other.0[3] as u128) << 64) | (other.0[2] as u128);
        let b_hi = ((other.0[5] as u128) << 64) | (other.0[4] as u128);

        let (diff_lo, borrow0) = a_lo.overflowing_sub(b_lo);
        let (diff_mid, u1) = a_mid.overflowing_sub(b_mid);
        let (diff_mid, u2) = diff_mid.overflowing_sub(borrow0 as u128);
        let borrow1 = u1 | u2;
        let (diff_hi, u3) = a_hi.overflowing_sub(b_hi);
        let (diff_hi, u4) = diff_hi.overflowing_sub(borrow1 as u128);

        (
            U384([
                diff_lo as u64,
                (diff_lo >> 64) as u64,
                diff_mid as u64,
                (diff_mid >> 64) as u64,
                diff_hi as u64,
                (diff_hi >> 64) as u64,
            ]),
            u3 | u4,
        )
    }
}

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

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

    /// Subtracting a larger value underflows and wraps.
    #[test]
    fn underflow_wraps() {
        let (result, underflow) = U384::ZERO.overflowing_sub(&U384::ONE);
        assert_eq!(result, U384::MAX);
        assert!(underflow);
    }

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

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

    /// Borrow crosses the lo-mid boundary.
    #[test]
    fn borrow_lo_to_mid() {
        let a = U384::from_be_limbs([0, 0, 0, 1, 0, 0]);
        let b = U384::from_be_limbs([0, 0, 0, 0, 0, 1]);
        let (result, underflow) = a.overflowing_sub(&b);
        assert_eq!(
            result,
            U384::from_be_limbs([0, 0, 0, 0, u64::MAX, u64::MAX]),
        );
        assert!(!underflow);
    }
}