///|
/// Cryptographic parameters for the Dilithium post-quantum signature scheme.
///
/// This module defines the fundamental constants and parameter sets used in
/// the Dilithium digital signature algorithm. These parameters are derived from
/// the NIST FIPS 204 standard and are critical for the security and correctness
/// of the implementation.
///
/// The Dilithium scheme operates in the polynomial ring Z_q[X]/(X^N + 1) where:
/// - N is the degree of the polynomial ring
/// - Q is the modulus for coefficient arithmetic
/// - D is used for rounding operations
///
/// # Examples
///
/// ```moonbit
/// // Access fundamental parameters
/// let dimension = @ruifeng/moondsa.N  // 256
/// let modulus = @ruifeng/moondsa.Q    // 8380417
/// ```

///|
/// The dimension N of the polynomial ring Z_q[X]/(X^N + 1).
/// This determines the degree of polynomials used in the scheme.
pub const N = 256

///|
/// The modulus q for the polynomial ring Z_q[X]/(X^N + 1).
/// All polynomial coefficients are reduced modulo this prime value.
pub const Q = 8380417

///|
/// The rounding parameter D used in power-of-2 rounding operations.
/// This determines the precision of rounding in various cryptographic operations.
pub const D = 13

///|
/// Size in bytes for random seeds used in key generation and signature operations.
/// This corresponds to 256-bit security strength for the seed material.
pub const SEEDBYTES = 32

///|
/// Size in bytes for collision-resistant hash function outputs.
/// This constant defines the output size for cryptographic hash functions,
/// particularly for operations requiring 512-bit (64-byte) digests in the
/// Dilithium signature scheme.
///
/// This constant specifies the output size in bytes for cryptographic hash
/// functions, particularly for operations that require 64-byte hash outputs. In
/// the context of Dilithium, this is commonly used for collision-resistant hash
/// functions and other cryptographic primitives that produce 512-bit (64-byte)
/// digests.
///
pub const CRHBYTES = 64

///|
/// Rate (block size) in bytes for the SHAKE128 extendable-output function.
/// This determines the input block size for the Keccak sponge construction.
pub const SHAKE128_RATE : UInt = 168

///|
/// Rate (block size) in bytes for the SHAKE256 extendable-output function.
/// This determines the input block size for the Keccak sponge construction.
pub const SHAKE256_RATE : UInt = 136

///|
/// Block size in bytes for SHAKE128-based streaming operations.
/// Alias for SHAKE128_RATE to maintain consistency with the reference implementation.
pub const STREAM128_BLOCKBYTES : UInt = SHAKE128_RATE

///|
/// Block size in bytes for SHAKE256-based streaming operations.
/// Alias for SHAKE256_RATE to maintain consistency with the reference implementation.
pub const STREAM256_BLOCKBYTES : UInt = SHAKE256_RATE

///|
/// Size in bytes for packed polynomial coefficients in the t0 representation.
/// Used for efficient storage and transmission of polynomial data.
pub const POLYT0_PACKEDBYTES : Int = 416

///|
/// Size in bytes for packed polynomial coefficients in the t1 representation.
/// Used for efficient storage and transmission of polynomial data.
pub const POLYT1_PACKEDBYTES : Int = 320

///|
/// Security levels supported by the Dilithium signature scheme.
///
/// Each variant provides different security strengths and performance characteristics:
/// - `Dilithium2`: 128-bit classical security, fastest performance
/// - `Dilithium3`: 192-bit classical security, balanced performance
/// - `Dilithium5`: 256-bit classical security, highest security
pub(all) enum SecurityLevel {
  Dilithium2
  Dilithium3
  Dilithium5
}

///|
/// Configuration parameters for Dilithium digital signature scheme variants.
///
/// Fields:
///
/// * `k` : Dimension of the s\_1 vector.
/// * `l` : Dimension of the s\_2 (error) vector.
/// * `eta` : Coefficient range parameter for secret polynomials.
/// * `tau` : Number of non-zero entries in the challenge polynomial.
/// * `beta` : Maximum coefficient of the commitment randomness.
/// * `gamma1` : Parameter for decomposition, computed as (q - 1) / 16.
/// * `gamma2` : Half of gamma1, used for range checks.
/// * `omega` : Maximum number of 1s in the hint polynomial.
/// * `publickey_bytes` : Size of the public key in bytes (seedbytes + k \* t1).
/// * `polyeta_packedbytes` : Size of a packed eta polynomial in bytes.
/// * `polyw1_packedbytes` : Size of a packed w1 polynomial in bytes.
/// * `polyz_packed_bytes` : Size of a packed z polynomial in bytes.
/// * `poly_uniform_gamma1_nblocks` : Number of SHAKE256 blocks needed for
///   sampling gamma1.
/// * `polyvech_packed_bytes` : Size of a packed hint vector in bytes.
/// * `sign_bytes` : Size of the signature in bytes (seedbytes + l \*
///   polyz\_packed\_bytes + polyvech\_packed\_bytes).
/// * `level` : Security level (Dilithium2, Dilithium3, or Dilithium5).
/// * `features` : Additional features map (e.g., "aes" support, currently not
///   supported).
///
///
pub struct ParameterSet {
  k : Int // s_1 dimension
  l : Int // s_2 (error) dimension
  eta : Int
  tau : Int
  beta : Int
  gamma1 : Int // (q - 1) / 16
  gamma2 : Int // gamma1 / 2
  omega : Int
  publickey_bytes : Int // seedbytes + k * t1
  polyeta_packedbytes : Int
  polyw1_packedbytes : Int
  polyz_packed_bytes : Int
  poly_uniform_gamma1_nblocks : UInt //(polyz_packed_bytes + STREAM256_BLOCKBYTES - 1) / STREAM256_BLOCKBYTES
  polyvech_packed_bytes : Int
  sign_bytes : Int // seedbytes + l * polyz_packed_bytes + polyvech_packed_bytes
  level : SecurityLevel
  features : Map[String, String] // features(i.e. "aes" not supported now)
}

///|
/// Generates a complete parameter set configuration for the specified Dilithium
/// security level.
///
/// Parameters:
///
/// * `self` : The Dilithium security level (Dilithium2, Dilithium3, or
///   Dilithium5).
/// * `features` : Optional configuration features map for additional settings
///   (currently unused, defaults to empty map).
///
/// Returns a `ParameterSet` containing all cryptographic parameters,
/// dimensions, and byte sizes needed for the specified Dilithium variant.
fn SecurityLevel::gen_paramset(
  self : SecurityLevel,
  features~ : Map[String, String] = Map::new(),
) -> ParameterSet {
  match self {
    SecurityLevel::Dilithium2 =>
      {
        k: 4,
        l: 4,
        eta: 2,
        tau: 39,
        beta: 78,
        gamma1: 1 << 17,
        gamma2: (Q - 1) / 88,
        omega: 80,
        publickey_bytes: SEEDBYTES + 4 * POLYT1_PACKEDBYTES,
        polyeta_packedbytes: 96,
        polyw1_packedbytes: 192,
        polyz_packed_bytes: 576,
        poly_uniform_gamma1_nblocks: (576 + STREAM256_BLOCKBYTES - 1) /
        STREAM256_BLOCKBYTES,
        polyvech_packed_bytes: 80 + 4, // omega + k
        sign_bytes: SEEDBYTES + 4 * 576 + 84,
        level: self,
        features,
      }
    SecurityLevel::Dilithium3 =>
      {
        k: 6,
        l: 5,
        eta: 4,
        tau: 49,
        beta: 196,
        gamma1: 1 << 19,
        gamma2: (Q - 1) / 32,
        omega: 55,
        publickey_bytes: SEEDBYTES + 6 * POLYT1_PACKEDBYTES, // seedbytes + k * t1
        polyeta_packedbytes: 128,
        polyw1_packedbytes: 128,
        polyz_packed_bytes: 640,
        poly_uniform_gamma1_nblocks: (640 + STREAM256_BLOCKBYTES - 1) /
        STREAM256_BLOCKBYTES, //(polyz_packed_bytes + STREAM256_BLOCKBYTES - 1) / STREAM256_BLOCKBYTES
        polyvech_packed_bytes: 55 + 6, // omega + k
        sign_bytes: SEEDBYTES + 5 * 640 + 61, // seedbytes + l * polyz_packed_bytes + polyvech_packed_bytes
        level: self,
        features,
      }
    SecurityLevel::Dilithium5 =>
      {
        k: 8,
        l: 7,
        eta: 2,
        tau: 60,
        beta: 120,
        gamma1: 1 << 19,
        gamma2: (Q - 1) / 32,
        omega: 75,
        publickey_bytes: SEEDBYTES + 8 * POLYT1_PACKEDBYTES, // seedbytes + k * t1
        polyeta_packedbytes: 96,
        polyw1_packedbytes: 128,
        polyz_packed_bytes: 640,
        poly_uniform_gamma1_nblocks: (640 + STREAM256_BLOCKBYTES - 1) /
        STREAM256_BLOCKBYTES, //(polyz_packed_bytes + STREAM256_BLOCKBYTES - 1) / STREAM256_BLOCKBYTES
        polyvech_packed_bytes: 75 + 8, // omega + k
        sign_bytes: SEEDBYTES + 7 * 640 + 83, // seedbytes + l * polyz_packed_bytes + polyvech_packed_bytes
        level: self,
        features,
      }
  }
}