//! Constant-time conditional selection between two [`U256`] values.
use super::U256;

impl U256 {
    /// Returns `self` if `condition` is true, `other` if false, in constant time.
    ///
    /// Uses the XOR-merge pattern: `other ^ ((self ^ other) & mask)`. When
    /// mask is all-ones (true), the XOR cancels and returns `self`. When
    /// mask is zero (false), the AND zeroes the difference and returns `other`.
    /// Both paths are always fully evaluated — no short-circuiting.
    ///
    /// # Examples
    ///
    /// ```
    /// use cnfy_uint::u256::U256;
    ///
    /// let a = U256::from_be_limbs([0, 0, 0, 42]);
    /// let b = U256::from_be_limbs([0, 0, 0, 99]);
    /// assert_eq!(a.ct_select(&b, true), a);
    /// assert_eq!(a.ct_select(&b, false), b);
    /// ```
    #[inline]
    pub const fn ct_select(&self, other: &U256, condition: bool) -> U256 {
        let mask = 0u64.wrapping_sub(condition as u64);
        U256([
            other.0[0] ^ ((self.0[0] ^ other.0[0]) & mask),
            other.0[1] ^ ((self.0[1] ^ other.0[1]) & mask),
            other.0[2] ^ ((self.0[2] ^ other.0[2]) & mask),
            other.0[3] ^ ((self.0[3] ^ other.0[3]) & mask),
        ])
    }
}

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

    /// Selecting with true returns self.
    #[test]
    fn true_returns_self() {
        let a = U256::from_be_limbs([1, 2, 3, 4]);
        let b = U256::from_be_limbs([5, 6, 7, 8]);
        assert_eq!(a.ct_select(&b, true), a);
    }

    /// Selecting with false returns other.
    #[test]
    fn false_returns_other() {
        let a = U256::from_be_limbs([1, 2, 3, 4]);
        let b = U256::from_be_limbs([5, 6, 7, 8]);
        assert_eq!(a.ct_select(&b, false), b);
    }

    /// Both arguments are the same — result is identical regardless of condition.
    #[test]
    fn same_value() {
        let a = U256::from_be_limbs([0xDEAD, 0xBEEF, 0xCAFE, 0xBABE]);
        assert_eq!(a.ct_select(&a, true), a);
        assert_eq!(a.ct_select(&a, false), a);
    }

    /// Works with zero values.
    #[test]
    fn zero_select() {
        let a = U256::ZERO;
        let b = U256::from_be_limbs([u64::MAX, u64::MAX, u64::MAX, u64::MAX]);
        assert_eq!(a.ct_select(&b, true), U256::ZERO);
        assert_eq!(a.ct_select(&b, false), b);
    }

    /// Works with max values in all limbs.
    #[test]
    fn max_values() {
        let a = U256::from_be_limbs([u64::MAX, u64::MAX, u64::MAX, u64::MAX]);
        let b = U256::ZERO;
        assert_eq!(a.ct_select(&b, true), a);
        assert_eq!(a.ct_select(&b, false), U256::ZERO);
    }
}
