// stdlib/crypto/ct_ops.sy — constant-time comparison and selection
//
// WARNING: these fallback implementations are NOT constant-time in the
// strong sense. They use plain Scyla `if` branches which the compiler or
// CPU may fold. They are *logically* constant-time in the input data
// (they don't early-exit based on content), but a real side-channel
// attacker watching timing, cache, or branch-predictor behavior can
// probably still extract information.
//
// USE THIS FILE ONLY FOR CORRECTNESS TESTING during the Phase 3 port.
// Before Phase 3 ships any living-crypto variant to real users, every
// function here must be replaced with a VM builtin backed by the
// `subtle` crate (§1.3 of CARDEA_PHASE3_PREREQS.md). The public API
// here matches the target builtin API, so call sites don't change.
//
// Aislinn McCubbins, Apr 11 2026

use crypto::bitops

// ── Integer equality → 0/1 mask ──────────────────────────
// Returns 1 if a == b, else 0.
// Target: VM builtin `ct_eq_int` using `subtle::ConstantTimeEq`.
fn ct_eq_int(a: Int, b: Int) -> Int {
    if a == b { return 1 }
    return 0
}

// Returns 1 if a != b, else 0.
fn ct_neq_int(a: Int, b: Int) -> Int {
    if a == b { return 0 }
    return 1
}

// ── Integer conditional select ───────────────────────────
// Returns a if mask == 1, else b. Assumes mask is 0 or 1 (i.e., the
// output of a ct_eq_int call — do not pass arbitrary truthy values).
// Target: VM builtin `ct_select_int` using `subtle::ConditionallySelectable`.
fn ct_select_int(mask: Int, a: Int, b: Int) -> Int {
    // mask * a + (1 - mask) * b is branchless at the algebra level.
    // The Scyla `if`-free form exercises the exact code the real builtin
    // will eventually replace.
    return mask * a + (1 - mask) * b
}

// ── Byte-tensor equality ─────────────────────────────────
// Returns 1 if the two tensors are equal, length-equal, byte-for-byte;
// else 0. Walks both tensors to completion (no early exit) so that
// timing doesn't leak which byte differed.
// Target: VM builtin `ct_eq_bytes`.
fn ct_eq_bytes(a: Tensor, b: Tensor) -> Int {
    let len_a = tensor_len(a)
    let len_b = tensor_len(b)
    if len_a != len_b {
        return 0
    }

    // Accumulate difference in `diff`. If any byte differs, diff becomes
    // nonzero for the rest of the loop.
    let mut diff = 0
    let mut i = 0
    while i < len_a {
        let av = t_index_get(a, i)
        let bv = t_index_get(b, i)
        // |av - bv| — not using bit_xor here because Tensor stores f32.
        // Correctness only; real builtin will do u8^u8.
        let d = av - bv
        if d < 0.0 {
            diff = diff + (0 - (d + 0))
        }
        if d > 0.0 {
            diff = diff + (d + 0)
        }
        i = i + 1
    }
    if diff == 0 {
        return 1
    }
    return 0
}

// ── Byte-tensor conditional select ───────────────────────
// Returns a copy of `a` if mask==1, else a copy of `b`. Both tensors
// MUST have the same length; enforcing that is the caller's contract
// (the real builtin will assert).
// Target: VM builtin `ct_select_bytes`.
fn ct_select_bytes(mask: Int, a: Tensor, b: Tensor) -> Tensor {
    let n = tensor_len(a)
    let mut out = t_zeros(n)
    let mut i = 0
    while i < n {
        let av = t_index_get(a, i)
        let bv = t_index_get(b, i)
        let chosen = (mask + 0.0) * av + (1.0 - (mask + 0.0)) * bv
        out = t_index_set(out, i, chosen)
        i = i + 1
    }
    return out
}

// ── Greater-than-or-equal as a mask ──────────────────────
// Returns 1 if a >= b, else 0. Used inside Barrett-style conditional
// subtraction paths where we don't want to branch on secret data.
fn ct_gte_int(a: Int, b: Int) -> Int {
    if a >= b { return 1 }
    return 0
}

fn ct_lt_int(a: Int, b: Int) -> Int {
    if a < b { return 1 }
    return 0
}

// ── Self-test ────────────────────────────────────────────
fn ct_self_test() -> Int {
    if ct_eq_int(5, 5) != 1 { return 1 }
    if ct_eq_int(5, 6) != 0 { return 2 }
    if ct_select_int(1, 42, 99) != 42 { return 3 }
    if ct_select_int(0, 42, 99) != 99 { return 4 }
    if ct_gte_int(10, 5) != 1 { return 5 }
    if ct_gte_int(5, 10) != 0 { return 6 }
    return 0
}
