//! Modular addition of two [`U256`] values.
//!
//! Dispatches to `add_mod_ct` (constant-time, branchless) when the `ct-field`
//! feature is enabled, or `add_mod_vt` (variable-time, branching) by default.
use super::U256;

impl U256 {
    /// Computes `(self + other) mod modulus`.
    ///
    /// With the `ct-field` feature enabled, this is constant-time (branchless) and
    /// requires reduced inputs (both in `[0, modulus)`). Without the feature,
    /// this is variable-time and handles unreduced inputs.
    ///
    /// # 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, 10]);
    /// let b = U256::from_be_limbs([0, 0, 0, 20]);
    /// assert_eq!(a.add_mod(&b, &P), U256::from_be_limbs([0, 0, 0, 30]));
    /// ```
    #[inline]
    pub fn add_mod(&self, other: &U256, modulus: &U256) -> U256 {
        #[cfg(feature = "ct-field")]
        { self.add_mod_ct(other, modulus) }
        #[cfg(not(feature = "ct-field"))]
        { self.add_mod_vt(other, modulus) }
    }
}

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

    /// Dispatcher produces the same result as the VT variant for reduced inputs.
    #[test]
    fn matches_vt_for_reduced() {
        let cases = [
            (U256::ZERO, U256::ONE),
            (U256::from_be_limbs([0, 0, 0, 10]), U256::from_be_limbs([0, 0, 0, 20])),
            (
                U256::from_be_limbs([
                    0xFFFFFFFFFFFFFFFF, 0xFFFFFFFFFFFFFFFF,
                    0xFFFFFFFFFFFFFFFF, 0xFFFFFFFEFFFFFC2E,
                ]),
                U256::from_be_limbs([
                    0xFFFFFFFFFFFFFFFF, 0xFFFFFFFFFFFFFFFF,
                    0xFFFFFFFFFFFFFFFF, 0xFFFFFFFEFFFFFC2E,
                ]),
            ),
        ];
        for (a, b) in &cases {
            assert_eq!(a.add_mod(b, &P), a.add_mod_vt(b, &P));
        }
    }
}
