//! Constant-time primitives for cryptographic operations.
//!
//! All functions in this module execute in constant time regardless of input values,
//! preventing timing side-channel attacks.

/// Constant-time conditional selection.
///
/// Returns `a` if `choice == 1`, otherwise returns `b`.
/// Executes in constant time regardless of the value of `choice`.
///
/// Note: `choice` should be 0 or 1. Other values may produce unexpected results.
#[inline(always)]
pub fn ct_select(choice: u64, a: u64, b: u64) -> u64 {
    ct_select_u64(choice, a, b)
}

/// Constant-time selection for u64.
#[inline(always)]
pub fn ct_select_u64(choice: u64, a: u64, b: u64) -> u64 {
    let mask = choice.wrapping_neg();
    (a & mask) | (b & !mask)
}

/// Constant-time selection for u32.
#[inline(always)]
pub fn ct_select_u32(choice: u32, a: u32, b: u32) -> u32 {
    let mask = choice.wrapping_neg();
    (a & mask) | (b & !mask)
}

/// Constant-time less-than comparison.
///
/// Returns `1` if `a < b`, otherwise returns `0`.
/// Executes in constant time.
#[inline(always)]
pub fn ct_lt(a: u64, b: u64) -> u64 {
    let a128 = a as i128;
    let b128 = b as i128;
    let diff = a128 - b128;
    ((diff >> 127) & 1) as u64
}

/// Constant-time equality comparison.
///
/// Returns `1` if `a == b`, otherwise returns `0`.
/// Executes in constant time.
#[inline(always)]
pub fn ct_eq(a: u64, b: u64) -> u64 {
    let diff = a ^ b;
    ((diff.wrapping_sub(1)) & !diff) >> 63
}

/// Constant-time greater-than comparison.
///
/// Returns `1` if `a > b`, otherwise returns `0`.
/// Executes in constant time.
#[inline(always)]
pub fn ct_gt(a: u64, b: u64) -> u64 {
    ct_lt(b, a)
}

/// Constant-time less-than-or-equal comparison.
///
/// Returns `1` if `a <= b`, otherwise returns `0`.
/// Executes in constant time.
#[inline(always)]
pub fn ct_le(a: u64, b: u64) -> u64 {
    !ct_gt(a, b)
}

/// Constant-time greater-than-or-equal comparison.
///
/// Returns `1` if `a >= b`, otherwise returns `0`.
/// Executes in constant time.
#[inline(always)]
pub fn ct_ge(a: u64, b: u64) -> u64 {
    !ct_lt(a, b)
}

/// Constant-time masking utility.
///
/// Returns `value` if `mask == 0xFFFFFFFF...`, otherwise returns `0`.
#[inline(always)]
pub fn ct_mask(mask: u64, value: u64) -> u64 {
    mask & value
}

/// Constant-time conditional swap.
///
/// Swaps `a` and `b` if `choice == 1`, otherwise leaves them unchanged.
/// Executes in constant time.
#[inline(always)]
pub fn ct_swap(choice: u64, a: &mut u64, b: &mut u64) {
    let mask = choice.wrapping_neg();
    let diff = *a ^ *b;
    *a ^= diff & mask;
    *b ^= diff & mask;
}

/// Constant-time conditional swap for arrays.
///
/// Swaps corresponding elements of `a` and `b` if `choice == 1`.
#[inline(always)]
pub fn ct_swap_slice(choice: u64, a: &mut [u64], b: &mut [u64]) {
    let mask = choice.wrapping_neg();
    let len = a.len().min(b.len());
    for i in 0..len {
        let diff = a[i] ^ b[i];
        a[i] ^= diff & mask;
        b[i] ^= diff & mask;
    }
}

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

    #[test]
    fn test_ct_select() {
        assert_eq!(ct_select_u64(0, 10, 20), 20);
        assert_eq!(ct_select_u64(1, 10, 20), 10);
        assert_eq!(ct_select_u64(0, 0xFFFF, 0x0000), 0x0000);
        assert_eq!(ct_select_u64(1, 0xFFFF, 0x0000), 0xFFFF);
    }

    #[test]
    fn test_ct_lt() {
        assert_eq!(ct_lt(5, 10), 1);
        assert_eq!(ct_lt(10, 5), 0);
        assert_eq!(ct_lt(10, 10), 0);
        assert_eq!(ct_lt(0, u64::MAX), 1);
    }

    #[test]
    fn test_ct_eq() {
        assert_eq!(ct_eq(5, 5), 1);
        assert_eq!(ct_eq(5, 10), 0);
        assert_eq!(ct_eq(0, 0), 1);
        assert_eq!(ct_eq(u64::MAX, u64::MAX), 1);
    }

    #[test]
    fn test_ct_swap() {
        let mut a = 10u64;
        let mut b = 20u64;
        ct_swap(0, &mut a, &mut b);
        assert_eq!(a, 10);
        assert_eq!(b, 20);

        let mut a = 10u64;
        let mut b = 20u64;
        ct_swap(1, &mut a, &mut b);
        assert_eq!(a, 20);
        assert_eq!(b, 10);
    }
}
