//! 512-bit subtraction with underflow detection.
use super::U512;

impl U512 {
    /// Computes `self - other` by packing limb pairs into `u128` for
    /// native `sub` + `sbb` borrow chaining, returning the 512-bit
    /// result and a boolean indicating whether the subtraction
    /// underflowed (i.e., `self < other`).
    ///
    /// Packs the eight limbs as four `u128` pairs: `[0]+[1]`, `[2]+[3]`,
    /// `[4]+[5]`, `[6]+[7]`, then chains borrows from LSB to MSB.
    ///
    /// # Examples
    ///
    /// ```
    /// use cnfy_uint::u512::U512;
    ///
    /// let a = U512::from_be_limbs([0, 0, 0, 0, 0, 0, 0, 10]);
    /// let b = U512::from_be_limbs([0, 0, 0, 0, 0, 0, 0, 3]);
    /// let (result, underflow) = a.overflowing_sub(&b);
    /// assert_eq!(result, U512::from_be_limbs([0, 0, 0, 0, 0, 0, 0, 7]));
    /// assert!(!underflow);
    /// ```
    #[inline]
    pub const fn overflowing_sub(&self, other: &U512) -> (U512, bool) {
        // Pack as 4 u128 pairs (LE pair ordering: p0=LSB, p3=MSB)
        let a0 = ((self.0[1] as u128) << 64) | (self.0[0] as u128);
        let a1 = ((self.0[3] as u128) << 64) | (self.0[2] as u128);
        let a2 = ((self.0[5] as u128) << 64) | (self.0[4] as u128);
        let a3 = ((self.0[7] as u128) << 64) | (self.0[6] as u128);

        let b0 = ((other.0[1] as u128) << 64) | (other.0[0] as u128);
        let b1 = ((other.0[3] as u128) << 64) | (other.0[2] as u128);
        let b2 = ((other.0[5] as u128) << 64) | (other.0[4] as u128);
        let b3 = ((other.0[7] as u128) << 64) | (other.0[6] as u128);

        let (d0, w0) = a0.overflowing_sub(b0);
        let (d1, w1a) = a1.overflowing_sub(b1);
        let (d1, w1b) = d1.overflowing_sub(w0 as u128);
        let (d2, w2a) = a2.overflowing_sub(b2);
        let (d2, w2b) = d2.overflowing_sub((w1a | w1b) as u128);
        let (d3, w3a) = a3.overflowing_sub(b3);
        let (d3, w3b) = d3.overflowing_sub((w2a | w2b) as u128);

        (
            U512([
                d0 as u64,
                (d0 >> 64) as u64,
                d1 as u64,
                (d1 >> 64) as u64,
                d2 as u64,
                (d2 >> 64) as u64,
                d3 as u64,
                (d3 >> 64) as u64,
            ]),
            w3a | w3b,
        )
    }
}

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

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

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

    /// Borrow propagates across all eight limbs.
    #[test]
    fn borrow_propagation() {
        let a = U512::from_be_limbs([1, 0, 0, 0, 0, 0, 0, 0]);
        let b = U512::from_be_limbs([0, 0, 0, 0, 0, 0, 0, 1]);
        let (result, underflow) = a.overflowing_sub(&b);
        assert_eq!(
            result,
            U512::from_be_limbs([0, u64::MAX, u64::MAX, 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 = U512::from_be_limbs([0x1234, 0x5678, 0x9ABC, 0xDEF0, 1, 2, 3, 4]);
        let (result, underflow) = a.overflowing_sub(&a);
        assert_eq!(result, U512::ZERO);
        assert!(!underflow);
    }

    /// Subtraction across u128 pair boundaries.
    #[test]
    fn cross_pair_borrow() {
        let a = U512::from_be_limbs([0, 0, 0, 0, 1, 0, 0, 0]);
        let b = U512::from_be_limbs([0, 0, 0, 0, 0, 0, 0, 1]);
        let (result, underflow) = a.overflowing_sub(&b);
        assert_eq!(
            result,
            U512::from_be_limbs([0, 0, 0, 0, 0, u64::MAX, u64::MAX, u64::MAX]),
        );
        assert!(!underflow);
    }
}
