//! Cross-validation test: assert the Rust Level 1 field arithmetic
//! produces byte-identical output to an independent implementation
//! for a fixed sequence of operations.
//!
//! The expected hex bytes below were generated by the external harness
//! under `tools/c-validate/`; see its README for the procedure to
//! regenerate them. The point of this test is to catch any subtle
//! drift in Montgomery encoding, endianness, or reduction behaviour
//! that the algebraic-identity tests would not notice.

use hex_literal::hex;
use sqisign_verify::fp::Fp;
use sqisign_verify::params::Level1;

fn fp_from_bytes(bytes: &[u8; 32]) -> Fp<Level1> {
    Fp::<Level1>::decode(bytes).expect("input fits in [0, p)")
}

fn assert_bytes(label: &str, a: &Fp<Level1>, expected: &[u8; 32]) {
    let encoded = a.encode();
    let got: &[u8] = &encoded[..];
    assert_eq!(
        got,
        &expected[..],
        "{label}: got {} expected {}",
        hex_str(got),
        hex_str(expected),
    );
}

fn hex_str(bytes: &[u8]) -> String {
    let mut s = String::with_capacity(bytes.len() * 2);
    for b in bytes {
        s.push_str(&format!("{:02x}", b));
    }
    s
}

#[test]
fn c_crossvalidate_lvl1() {
    // Two canonical inputs chosen to exercise the low and high regions
    // of the prime. The external harness in `tools/c-validate/`
    // operates on the same byte values.
    let a_bytes = hex!("0123456789abcdef fedcba9876543210 0011223344556677 8899aabbccddee03");
    let b_bytes = hex!("ffffffffffffffff ffffffffffffffff ffffffffffffffff ffffffffffffff03");

    let a = fp_from_bytes(&a_bytes);
    let b = fp_from_bytes(&b_bytes);

    // Expected outputs are the bytes produced by the external harness;
    // see tools/c-validate/README.md for the regeneration procedure.
    assert_bytes(
        "a+b",
        &a.add(&b),
        &hex!("0123456789abcdef fedcba9876543210 0011223344556677 8899aabbccddee02"),
    );
    assert_bytes(
        "a-b",
        &a.sub(&b),
        &hex!("0123456789abcdef fedcba9876543210 0011223344556677 8899aabbccddee04"),
    );
    assert_bytes(
        "-a",
        &a.neg(),
        &hex!("fedcba9876543210 0123456789abcdef ffeeddccbbaa9988 7766554433221101"),
    );
    assert_bytes(
        "a*b",
        &a.mul(&b),
        &hex!("999258eb4a777036 000741ae4e222963 66c992c25855b881 b14744a770a03601"),
    );
    assert_bytes(
        "a^2",
        &a.sqr(),
        &hex!("660df65ff2ba2d25 04882c146135b3fc 1004455583ea72d1 db676a98734a3804"),
    );
    assert_bytes(
        "1/a",
        &a.inv(),
        &hex!("ddaefff3e2b3fb5a 482f18fc4aa5a04c 798cfd230bc59ab1 db727a1da2942103"),
    );
    // sqrt(a^2) returns +a or -a; in this fixed sequence the chosen
    // sign happens to be +a.
    let a_sq = a.sqr();
    let root = a_sq.sqrt();
    assert_bytes(
        "sqrt(a^2)",
        &root,
        &hex!("0123456789abcdef fedcba9876543210 0011223344556677 8899aabbccddee03"),
    );
}
