//! Hemera β€” Poseidon2 parameter set over the Goldilocks field.
//!
//! Single source of truth for every constant in the protocol.
//! The WGSL shader (`gpu/poseidon2.wgsl`) duplicates a subset of
//! these values because WGSL cannot import Rust; keep them in sync.
//!
//! ```text
//! β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
//! β”‚  HEMERA β€” Complete Specification                         β”‚
//! β”‚                                                          β”‚
//! β”‚  Field:           p = 2⁢⁴ βˆ’ 2Β³Β² + 1 (Goldilocks)       β”‚
//! β”‚  S-box:           d = 7  (x β†’ x⁷, minimum for field)    β”‚
//! β”‚  State width:     t = 16                      = 2⁴       β”‚
//! β”‚  Full rounds:     R_F = 8  (4 + 4)            = 2Β³       β”‚
//! β”‚  Partial rounds:  R_P = 64                    = 2⁢       β”‚
//! β”‚  Rate:            r = 8  elements (56 bytes)  = 2Β³       β”‚
//! β”‚  Capacity:        c = 8  elements (64 bytes)  = 2Β³       β”‚
//! β”‚  Output:          8  elements (64 bytes)      = 2Β³       β”‚
//! β”‚                                                          β”‚
//! β”‚  Full round constants:    8 Γ— 16 = 128        = 2⁷       β”‚
//! β”‚  Partial round constants: 64                  = 2⁢       β”‚
//! β”‚  Total constants:         192                 = 3 Γ— 2⁢   β”‚
//! β”‚  Total rounds:            72                  = 9 Γ— 2Β³   β”‚
//! β”‚                                                          β”‚
//! β”‚  Classical collision resistance:  256 bits     = 2⁸       β”‚
//! β”‚  Quantum collision resistance:   170 bits                β”‚
//! β”‚  Algebraic degree:               2¹⁸⁰                    β”‚
//! β”‚                                                          β”‚
//! β”‚  Every parameter that appears in code is a power of 2.   β”‚
//! β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
//! ```

use crate::field::Goldilocks;

// ── Permutation parameters ──────────────────────────────────────────

/// Width of the Poseidon2 state (number of Goldilocks field elements).
pub const WIDTH: usize = 16;

/// Number of full (external) rounds β€” 4 initial + 4 final.
pub const ROUNDS_F: usize = 8;

/// Number of partial (internal) rounds.
pub const ROUNDS_P: usize = 64;

/// S-box degree (x β†’ x^d).
pub const SBOX_DEGREE: usize = 7;

// ── Sponge parameters ───────────────────────────────────────────────

/// Number of rate elements in the sponge.
pub const RATE: usize = 8;

/// Number of capacity elements in the sponge.
pub const CAPACITY: usize = WIDTH - RATE; // 8

// ── Encoding parameters ─────────────────────────────────────────────

/// Bytes per field element when encoding arbitrary input data.
///
/// We use 7 bytes per element because 2^56 βˆ’ 1 < p (Goldilocks prime),
/// so any 7-byte value fits without reduction.
pub const INPUT_BYTES_PER_ELEMENT: usize = 7;

/// Bytes per field element when encoding hash output.
///
/// For output we use the full canonical u64 representation (8 bytes),
/// since output elements are already valid field elements.
pub const OUTPUT_BYTES_PER_ELEMENT: usize = 8;

// ── Derived constants ───────────────────────────────────────────────

/// Number of input bytes that fill one rate block (8 elements Γ— 7 bytes).
pub const RATE_BYTES: usize = RATE * INPUT_BYTES_PER_ELEMENT; // 56

/// Number of output elements extracted per squeeze (= rate).
pub const OUTPUT_ELEMENTS: usize = RATE; // 8

/// Number of output bytes per squeeze (8 elements Γ— 8 bytes).
pub const OUTPUT_BYTES: usize = OUTPUT_ELEMENTS * OUTPUT_BYTES_PER_ELEMENT; // 64

// ── Tree parameters ─────────────────────────────────────────────────

/// Canonical chunk size for content tree construction (4 KB).
///
/// Content is split into fixed 4 KB chunks. Each chunk is hashed via
/// `hash_leaf`. The last chunk may be shorter. See spec Β§4.6.1.
pub const CHUNK_SIZE: usize = 4096;

/// Maximum tree depth (sufficient for 2^64 chunks).
pub const MAX_TREE_DEPTH: usize = 64;

// ── Security properties (informational) ─────────────────────────────

/// Classical collision resistance in bits.
pub const COLLISION_BITS: usize = 256;

// ── Permutation entry point ─────────────────────────────────────────

/// Apply the Poseidon2 permutation in-place.
#[inline]
pub(crate) fn permute(state: &mut [Goldilocks; WIDTH]) {
    crate::permutation::permute(state);
}

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

    #[test]
    fn permutation_is_deterministic() {
        let mut s1 = [Goldilocks::ZERO; WIDTH];
        let mut s2 = [Goldilocks::ZERO; WIDTH];
        permute(&mut s1);
        permute(&mut s2);
        assert_eq!(s1, s2);
    }

    #[test]
    fn permutation_changes_state() {
        let mut state = [Goldilocks::ZERO; WIDTH];
        let original = state;
        permute(&mut state);
        assert_ne!(state, original);
    }

    #[test]
    fn different_inputs_different_outputs() {
        let mut s1 = [Goldilocks::ZERO; WIDTH];
        let mut s2 = [Goldilocks::ZERO; WIDTH];
        s2[0] = Goldilocks::new(1);
        permute(&mut s1);
        permute(&mut s2);
        assert_ne!(s1, s2);
    }

    #[test]
    fn sponge_geometry() {
        assert_eq!(WIDTH, RATE + CAPACITY);
        assert_eq!(RATE_BYTES, 56);
        assert_eq!(OUTPUT_BYTES, 64);
        assert_eq!(OUTPUT_ELEMENTS, 8);
    }
}

Local Graph