#pragma once
#include <cstdint>
#include <cstddef>

namespace netplus {

struct u256 { uint32_t w[8]; };
struct u512 { uint32_t w[16]; };

extern const u256 P256_P;
extern const u256 P256_R2;

// -------- low-level helpers --------
void u256_zero(u256& a);
bool u256_is_zero(const u256& a);

static inline void u256_one(u256& a) {
    u256_zero(a);
    a.w[0] = 1;
}

static inline void cswap_u256(u256& A, u256& B, uint32_t swap)
{
    // swap is 0 or 1
    uint32_t mask = 0u - swap;  // 0x00000000 or 0xFFFFFFFF

    for (int i = 0; i < 8; i++) {
        uint32_t t = (A.w[i] ^ B.w[i]) & mask;
        A.w[i] ^= t;
        B.w[i] ^= t;
    }
}


int  u256_cmp(const u256& a, const u256& b);

// raw ops (no mod reduction)
u256 u256_add_raw(const u256& a, const u256& b, uint32_t& carry);
u256 u256_sub_raw(const u256& a, const u256& b, uint32_t& borrow);

// conversion (network BE <-> internal LE)
u256 u256_from_be(const uint8_t be32[32]);
void u256_to_be(uint8_t out32[32], const u256& a);
u512 u512_from_be(const uint8_t in[64]);
u256 bytes_be_to_u256(const uint8_t in[32]);
// multiply
u512 u256_mul_raw(const u256& a, const u256& b);

// Montgomery core
u256 mont_mul(const u256& a, const u256& b);
u256 to_mont(const u256& a);
u256 from_mont(const u256& a);

// field ops (Montgomery domain)
u256 fp_add(const u256& a, const u256& b);
u256 fp_sub(const u256& a, const u256& b);
u256 fp_mul(const u256& a, const u256& b);
u256 fp_sqr(const u256& a);

// inversion (works with Montgomery domain inputs)
u256 fp_inv(const u256& a);

// scalar bit access
uint32_t get_bit_be(const uint8_t priv[32], int bit_index);

// reduction (512 -> 256 mod P)
u256 mod_p256(const u512& in);

// scalar field arithmetic (mod n, group order) for ECDSA
u256 mod_n256(const u512& in);
u256 scalar_mul_mod_n(const u256& a, const u256& b);
u256 scalar_add_mod_n(const u256& a, const u256& b);
u256 scalar_inv_mod_n(const u256& a);

} // namespace netplus
