//! Constant-time modular subtraction of two [`U256`] values.
use super::U256;

impl U256 {
    /// Computes `(self - other) mod modulus` in constant time.
    ///
    /// Always computes both the subtraction and the corrective addition,
    /// then selects the result via branchless bitmask (`ct_select`).
    /// Both operands must be in `[0, modulus)` (reduced).
    ///
    /// # Examples
    ///
    /// ```
    /// use cnfy_uint::u256::U256;
    /// let P = U256::from_be_limbs([
    ///     0xFFFFFFFFFFFFFFFF, 0xFFFFFFFFFFFFFFFF,
    ///     0xFFFFFFFFFFFFFFFF, 0xFFFFFFFEFFFFFC2F,
    /// ]);
    ///
    /// let a = U256::from_be_limbs([0, 0, 0, 30]);
    /// let b = U256::from_be_limbs([0, 0, 0, 20]);
    /// assert_eq!(a.sub_mod_ct(&b, &P), U256::from_be_limbs([0, 0, 0, 10]));
    /// ```
    #[inline]
    pub fn sub_mod_ct(&self, other: &U256, modulus: &U256) -> U256 {
        let (result, underflow) = self.overflowing_sub(other);
        let (corrected, _) = result.overflowing_add(modulus);

        // If underflow, return corrected (result + modulus); else return result.
        corrected.ct_select(&result, underflow)
    }
}

#[cfg(test)]
mod ai_tests {
    use super::*;
    const P: U256 = U256::from_be_limbs([0xFFFFFFFFFFFFFFFF, 0xFFFFFFFFFFFFFFFF, 0xFFFFFFFFFFFFFFFF, 0xFFFFFFFEFFFFFC2F]);

    /// Subtracting zero from zero yields zero.
    #[test]
    fn zero_minus_zero() {
        assert_eq!(U256::ZERO.sub_mod_ct(&U256::ZERO, &P), U256::ZERO);
    }

    /// Subtracting zero is the identity operation.
    #[test]
    fn subtractive_identity() {
        let a = U256::from_be_limbs([0, 0, 0, 42]);
        assert_eq!(a.sub_mod_ct(&U256::ZERO, &P), a);
    }

    /// Subtracting a value from itself yields zero.
    #[test]
    fn self_cancellation() {
        let a = U256::from_be_limbs([
            0xFFFFFFFFFFFFFFFF,
            0xFFFFFFFFFFFFFFFF,
            0xFFFFFFFFFFFFFFFF,
            0xFFFFFFFEFFFFFC2E,
        ]);
        assert_eq!(a.sub_mod_ct(&a, &P), U256::ZERO);
    }

    /// Zero minus a non-zero value wraps: 0 - a = P - a.
    #[test]
    fn zero_minus_value() {
        let p_minus_1 = U256::from_be_limbs([
            0xFFFFFFFFFFFFFFFF,
            0xFFFFFFFFFFFFFFFF,
            0xFFFFFFFFFFFFFFFF,
            0xFFFFFFFEFFFFFC2E,
        ]);
        assert_eq!(U256::ZERO.sub_mod_ct(&U256::ONE, &P), p_minus_1);
    }

    /// 1 - 2 mod P = P - 1 (underflow path).
    #[test]
    fn one_minus_two() {
        let two = U256::from_be_limbs([0, 0, 0, 2]);
        let p_minus_1 = U256::from_be_limbs([
            0xFFFFFFFFFFFFFFFF,
            0xFFFFFFFFFFFFFFFF,
            0xFFFFFFFFFFFFFFFF,
            0xFFFFFFFEFFFFFC2E,
        ]);
        assert_eq!(U256::ONE.sub_mod_ct(&two, &P), p_minus_1);
    }

    /// (P-1) - 1 = P - 2 (no underflow).
    #[test]
    fn p_minus_one_minus_one() {
        let p_minus_1 = U256::from_be_limbs([
            0xFFFFFFFFFFFFFFFF,
            0xFFFFFFFFFFFFFFFF,
            0xFFFFFFFFFFFFFFFF,
            0xFFFFFFFEFFFFFC2E,
        ]);
        let p_minus_2 = U256::from_be_limbs([
            0xFFFFFFFFFFFFFFFF,
            0xFFFFFFFFFFFFFFFF,
            0xFFFFFFFFFFFFFFFF,
            0xFFFFFFFEFFFFFC2D,
        ]);
        assert_eq!(p_minus_1.sub_mod_ct(&U256::ONE, &P), p_minus_2);
    }

    /// CT version matches VT version for diverse reduced inputs.
    #[test]
    fn matches_vt() {
        let cases = [
            (U256::ZERO, U256::ZERO),
            (U256::ONE, U256::from_be_limbs([0, 0, 0, 2])),
            (
                U256::from_be_limbs([0, 0x1234, 0, 0x5678]),
                U256::from_be_limbs([0, 0xABCD, 0, 0xEF01]),
            ),
            (
                U256::from_be_limbs([
                    0xFFFFFFFFFFFFFFFF, 0xFFFFFFFFFFFFFFFF,
                    0xFFFFFFFFFFFFFFFF, 0xFFFFFFFEFFFFFC2E,
                ]),
                U256::ONE,
            ),
            (
                U256::from_be_limbs([
                    0x79BE667EF9DCBBAC, 0x55A06295CE870B07,
                    0x029BFCDB2DCE28D9, 0x59F2815B16F81798,
                ]),
                U256::from_be_limbs([
                    0x483ADA7726A3C465, 0x5DA4FBFC0E1108A8,
                    0xFD17B448A6855419, 0x9C47D08FFB10D4B8,
                ]),
            ),
        ];
        for (a, b) in &cases {
            assert_eq!(
                a.sub_mod_ct(b, &P),
                a.sub_mod_vt(b, &P),
                "CT/VT mismatch for {a:?} - {b:?}"
            );
        }
    }
}
