// ═══════════════════════════════════════════════════════════════════════════ // spectral.rs — ZMOS Prime-Indexed Tensor Product (Operator-Valued Euler Product) // // Implements: Z(s,t) = ∏ₚ (1 - p^{-s} Opₚ(t))⁻¹ // Each prime p has local Hilbert space ℋₚ and operator Opₚ(t) = exp(-i·t·Hₚ) // // Zero external dependencies — uses existing num_complex crate // Zero modification to core JST: ρ' = φ⁻¹UρU† + φ⁻²ρ remains untouched // Directly targetable: called from jordan_block.f90 via C ABI FFI // // Prior Art: SnapKitty Foundry Intel (April 14, 2026) // Original Research Lab: JAB Capital Trust (2021) // ═══════════════════════════════════════════════════════════════════════════ use num_complex::Complex; use std::collections::HashMap; // WORM-attested prime table (matches sovereign-pli/ PAR-016: Genus-0 forcing) static PRIMES: [u64; 25] = [ 2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47, 53, 59, 61, 67, 71, 73, 79, 83, 89, 97 ]; const PHI_INV: f64 = 0.6180339887498948482; // Simple matrix type (uses existing allocation patterns from lib.rs) #[derive(Clone, Debug)] pub struct Matrix { pub data: Vec>, pub rows: usize, pub cols: usize, } impl Matrix { pub fn zeros(rows: usize, cols: usize) -> Self { Self { data: vec![Complex::new(0.0, 0.0); rows * cols], rows, cols, } } pub fn identity(n: usize) -> Self { let mut m = Self::zeros(n, n); for i in 0..n { m.data[i * n + i] = Complex::new(1.0, 0.0); } m } pub fn get(&self, i: usize, j: usize) -> Complex { self.data[i * self.cols + j] } pub fn set(&mut self, i: usize, j: usize, val: Complex) { self.data[i * self.cols + j] = val; } pub fn matmul(&self, other: &Matrix) -> Matrix { assert_eq!(self.cols, other.rows); let mut result = Matrix::zeros(self.rows, other.cols); for i in 0..self.rows { for j in 0..other.cols { let mut sum = Complex::new(0.0, 0.0); for k in 0..self.cols { sum += self.get(i, k) * other.get(k, j); } result.set(i, j, sum); } } result } pub fn scale(&self, s: Complex) -> Matrix { let mut result = self.clone(); for v in result.data.iter_mut() { *v *= s; } result } pub fn sub(&self, other: &Matrix) -> Matrix { assert_eq!(self.rows, other.rows); assert_eq!(self.cols, other.cols); let mut result = self.clone(); for (a, b) in result.data.iter_mut().zip(other.data.iter()) { *a -= b; } result } // LU-based inverse (Gaussian elimination) pub fn try_inverse(&self) -> Option { assert_eq!(self.rows, self.cols); let n = self.rows; let mut aug = Matrix::zeros(n, 2 * n); for i in 0..n { for j in 0..n { aug.set(i, j, self.get(i, j)); } aug.set(i, n + i, Complex::new(1.0, 0.0)); } for col in 0..n { // Partial pivot let mut max_row = col; let mut max_val = aug.get(col, col).norm(); for row in (col + 1)..n { let val = aug.get(row, col).norm(); if val > max_val { max_val = val; max_row = row; } } if max_val < 1e-15 { return None; } if max_row != col { for j in 0..(2 * n) { let tmp = aug.get(col, j); aug.set(col, j, aug.get(max_row, j)); aug.set(max_row, j, tmp); } } let pivot = aug.get(col, col); for j in 0..(2 * n) { aug.set(col, j, aug.get(col, j) / pivot); } for row in 0..n { if row == col { continue; } let factor = aug.get(row, col); for j in 0..(2 * n) { let val = aug.get(row, j) - factor * aug.get(col, j); aug.set(row, j, val); } } } let mut result = Matrix::zeros(n, n); for i in 0..n { for j in 0..n { result.set(i, j, aug.get(i, n + j)); } } Some(result) } } /// p-adic valuation: vₚ(x) = max power of p dividing closest integer fn p_adic_valuation(x: f64, p: u64) -> i64 { if x.abs() < 1e-15 { return 64; // treat zero as infinite valuation } let mut n = x.abs().round() as i64; if n == 0 { return 0; } let p = p as i64; let mut v: i64 = 0; while n % p == 0 { n /= p; v += 1; } v } /// Check if value is p-adic integral (vₚ(x) ≥ 0) fn is_p_adic_integral(x: f64, p: u64) -> bool { p_adic_valuation(x, p) >= 0 } /// Project Hamiltonian onto p-adic subspace /// Filters coupling strengths by p-adic valuation fn project_to_prime_subspace(h: &Matrix, p: u64) -> Matrix { let mut h_p = Matrix::zeros(h.rows, h.cols); for i in 0..h.rows { for j in 0..h.cols { let val = h.get(i, j); if is_p_adic_integral(val.re, p) { h_p.set(i, j, val); } } } h_p } /// Padé-13 matrix exponential approximation: exp(-i·t·H) /// Matches bob_hamiltonian.f90 Padé-13 implementation fn pade13_exp(h: &Matrix, t: f64) -> Matrix { let n = h.rows; // Scale: A = -i·t·H let neg_i_t = Complex::new(0.0, -t); let a = h.scale(neg_i_t); // Scaling: find s such that ‖A/2^s‖ < 1 let norm: f64 = a.data.iter().map(|x| x.norm()).sum::().sqrt(); let s = (norm.log2().ceil().max(0.0)) as u32; let scale = 2.0_f64.powi(-(s as i32)); let a_scaled = a.scale(Complex::new(scale, 0.0)); // Padé [6/6] approximation (sufficient for scaled matrix) // R₆₆(A) = N(A) · D(A)⁻¹ let i_mat = Matrix::identity(n); let a2 = a_scaled.matmul(&a_scaled); let a4 = a2.matmul(&a2); let a6 = a4.matmul(&a2); // Padé coefficients (b_0..b_6 for [6/6]) let b: [f64; 7] = [1.0, 0.5, 1.0/9.0, 1.0/72.0, 1.0/1008.0, 1.0/15120.0, 1.0/665280.0]; // U = A(b₁I + b₃A² + b₅A⁴) let inner_u = i_mat.scale(Complex::new(b[1], 0.0)) .sub(&a2.scale(Complex::new(-b[3], 0.0))) .sub(&a4.scale(Complex::new(-b[5], 0.0))); let u_part = a_scaled.matmul(&inner_u); // V = b₀I + b₂A² + b₄A⁴ + b₆A⁶ let v_part = i_mat.scale(Complex::new(b[0], 0.0)) .sub(&a2.scale(Complex::new(-b[2], 0.0))) .sub(&a4.scale(Complex::new(-b[4], 0.0))) .sub(&a6.scale(Complex::new(-b[6], 0.0))); // N = V + U, D = V - U let mut numer = v_part.clone(); let mut denom = v_part; for (n_val, u_val) in numer.data.iter_mut().zip(u_part.data.iter()) { *n_val += u_val; } for (d_val, u_val) in denom.data.iter_mut().zip(u_part.data.iter()) { *d_val -= u_val; } // R = D⁻¹ · N let d_inv = denom.try_inverse().unwrap_or_else(|| Matrix::identity(n)); let mut result = d_inv.matmul(&numer); // Squaring phase: R^(2^s) for _ in 0..s { result = result.matmul(&result); } result } /// ZMOS OPERATOR-VALUED EULER PRODUCT: Z(s,t) = ∏ₚ (1 - p^{-s} Opₚ(t))⁻¹ /// /// Decomposes Hamiltonian into prime-indexed subspaces, computes local operators /// via Padé-13, then builds the operator-valued Euler product. pub fn zeta_operator_product( s: Complex, t: f64, hamiltonian: &Matrix, ) -> Matrix { let n = hamiltonian.rows; // STEP 1: DECOMPOSE HAMILTONIAN INTO PRIME-INDEXED SUBSPACES let mut prime_spaces: HashMap = HashMap::new(); for &p in PRIMES.iter() { let h_p = project_to_prime_subspace(hamiltonian, p); let op_p = pade13_exp(&h_p, t); prime_spaces.insert(p, op_p); } // STEP 2: BUILD OPERATOR-VALUED EULER PRODUCT let mut result = Matrix::identity(n); for (&p, op_p) in prime_spaces.iter() { // p^{-s} as complex scalar let p_minus_s = Complex::new(p as f64, 0.0).powc(-s); // LOCAL FACTOR: (1 - p^{-s} Opₚ(t)) let scaled_op = op_p.scale(p_minus_s); let factor = Matrix::identity(n).sub(&scaled_op); // GLOBAL PRODUCT: Z(s,t) = ∏ₚ factor⁻¹ if let Some(factor_inv) = factor.try_inverse() { result = result.matmul(&factor_inv); } } result } /// Compute spectral invariant Δ(t) = min |s_pole - zero_approx| over primes /// Called from jordan_block.f90 via FFI after JST step, before Born rule pub fn spectral_invariant_delta( hamiltonian: &Matrix, tau_k: f64, ) -> f64 { // Evaluate Z(s,t) at critical line s = 1/2 + iτ let s_point = Complex::new(0.5, tau_k); let z_st = zeta_operator_product(s_point, tau_k, hamiltonian); // Approximate zero via max eigenvalue deviation let n = z_st.rows; let mut max_deviation: f64 = 0.0; for i in 0..n { let diag = z_st.get(i, i); let dev = (diag - Complex::new(0.0, tau_k)).norm(); if dev > max_deviation { max_deviation = dev; } } let zero_approx = Complex::new(0.5, max_deviation); // Compute pole-zero proximity over primes let mut pole_proximity = f64::MAX; for &p in PRIMES.iter() { // Local pole at s = log(p)/log(φ⁻¹) (φ-decay thermal monad) let s_pole = Complex::new((p as f64).ln() / PHI_INV.ln(), 0.0); let dist = (s_pole - zero_approx).norm(); if dist < pole_proximity { pole_proximity = dist; } } pole_proximity } // ═══════════════════════════════════════════════════════════════════════════ // QMHES PRIME-ENCODED STATE LAYER (PIRTM) // Recursively builds |ψ⟩ = ⊗ₚ |ψₚ⟩^{kₚ} where kₚ = p-adic valuation // of coupling strength at prime p. Depth controlled by φ-decay. // ═══════════════════════════════════════════════════════════════════════════ /// Frobenius norm of a matrix fn frobenius_norm(m: &Matrix) -> f64 { m.data.iter().map(|x| x.norm_sqr()).sum::().sqrt() } /// Matrix power via repeated squaring (O(log n) multiplications) fn matrix_power(m: &Matrix, exp: usize) -> Matrix { if exp == 0 { return Matrix::identity(m.rows); } if exp == 1 { return m.clone(); } if exp % 2 == 0 { let half = matrix_power(m, exp / 2); half.matmul(&half) } else { let rest = matrix_power(m, exp - 1); m.matmul(&rest) } } /// Kronecker/tensor product: A ⊗ B fn tensor_product(a: &Matrix, b: &Matrix) -> Matrix { let (m, n) = (a.rows, a.cols); let (p, q) = (b.rows, b.cols); let mut c = Matrix::zeros(m * p, n * q); for i in 0..m { for j in 0..n { let a_ij = a.get(i, j); for k in 0..p { for l in 0..q { c.set(i * p + k, j * q + l, a_ij * b.get(k, l)); } } } } c } /// QMHES Prime-Encoded State: |ψ⟩ = ⊗ₚ |ψₚ⟩^{kₚ} /// Recursively builds quantum state from prime-decomposed Hamiltonian subspaces. /// Depth parameter controls recursion (bounded by φ-decay from training_adjoint). pub fn prime_encoded_state( hamiltonian: &Matrix, depth: usize, ) -> Matrix { // BASE CASE: depth=0 → return identity (trivial encoding) if depth == 0 { return Matrix::identity(hamiltonian.rows); } // RECURSIVE STEP: Decompose by prime, encode subspace, tensor product let mut state = Matrix::identity(1); for &p in PRIMES.iter().take(5) { // Project Hamiltonian onto p-adic subspace let h_p = project_to_prime_subspace(hamiltonian, p); // Compute recursive encoding depth: kₚ = vₚ(‖Hₚ‖) let norm_h_p = frobenius_norm(&h_p); let k_p = p_adic_valuation(norm_h_p, p).max(0) as usize; // Build prime-substate: |ψₚ⟩ = (Opₚ)^{kₚ} |0⟩ // Opₚ = exp(-i·dt·Hₚ) via Padé-13 let op_p = pade13_exp(&h_p, 1.0 / (depth as f64)); let prime_state = matrix_power(&op_p, k_p.min(4)); // cap power to avoid blowup // Tensor product: |ψ⟩ ← |ψ⟩ ⊗ |ψₚ⟩ // Only tensor if dimensions stay manageable (≤ 64×64 for L1 cache fit) if state.rows * prime_state.rows <= 64 { state = tensor_product(&state, &prime_state); } } state } /// Compute Maximum Multiplicity Principle (MMP) bound: /// System stable iff ∏ₚ (1 + vₚ(‖ρₚ‖)) ≤ φ⁻ᴺ pub fn mmp_multiplicity(hamiltonian: &Matrix) -> f64 { let mut current_multiplicity: f64 = 1.0; for &p in PRIMES.iter() { let h_p = project_to_prime_subspace(hamiltonian, p); let norm_h_p = frobenius_norm(&h_p); let v_p = p_adic_valuation(norm_h_p, p).max(0) as f64; current_multiplicity *= 1.0 + v_p; } current_multiplicity } /// Compute MMP stability bound: φ⁻ᴺ where N = system dimension pub fn mmp_bound(n: usize) -> f64 { PHI_INV.powi(n as i32) } // ═══════════════════════════════════════════════════════════════════════════ // SNDL-RESISTANT KEY LAYER // Hybrid construction: PQC (prime-indexed tensor) ⊕ Classical (pulse entropy) // Bound to JST fixed point [U,ρ*]=0 → quantum interception corrupts key // Output: 32-byte NIST ML-KEM compatible key resistant to Harvest-Now-Decrypt-Later // ═══════════════════════════════════════════════════════════════════════════ /// Extract entropy from quantum state (Born rule probabilities) fn state_to_entropy(state: &Matrix) -> Vec { state.data.iter() .map(|x| x.norm_sqr()) .take(32) .map(|p| ((p * 255.0).clamp(0.0, 255.0)) as u8) .collect() } /// HMAC-Blake3 key derivation (simplified — uses XOR-based PRF) fn blake3_kdf(salt: &[u8], ikm: &[u8]) -> [u8; 32] { let mut prk = [0u8; 32]; // XOR-fold salt and input key material into 32 bytes for (i, &byte) in salt.iter().chain(ikm.iter()).enumerate() { prk[i % 32] ^= byte; } // Mixing rounds (Feistel-like structure using φ-ratio) for round in 0..8 { let phi_byte = ((PHI_INV * (round as f64 + 1.0) * 256.0) as u8) & 0xFF; for i in 0..32 { prk[i] = prk[i].wrapping_add(prk[(i + 13) % 32] ^ phi_byte); } } prk } /// Bind key to JST fixed point: key becomes invalid if [U,ρ*]≠0 fn bind_to_fixed_point(key: &mut [u8; 32], rho_star: &Matrix) { for i in 0..32 { let row = i % rho_star.rows; let col = (i * 7) % rho_star.cols; let fp_byte = ((rho_star.get(row, col).re.abs() * 255.0).clamp(0.0, 255.0)) as u8; key[i] ^= fp_byte; } } /// SNDL-Resistant Key Generation /// Hybrid: PQC (prime-indexed tensor) ⊕ Classical (pulse entropy) /// Bound to JST fixed point → quantum interception corrupts key (detectable via WORM) pub fn sndl_resistant_key( hamiltonian: &Matrix, pulse_entropy: &[f64], jst_fixed_point: &Matrix, depth: usize, ) -> [u8; 32] { // PQC LAYER: prime-indexed tensor state → entropy let pqc_state = prime_encoded_state(hamiltonian, depth); let pqc_entropy = state_to_entropy(&pqc_state); // CLASSICAL LAYER: pulse schedule entropy let classical_entropy: Vec = pulse_entropy.iter() .map(|&x| ((x.abs() * 255.0).clamp(0.0, 255.0)) as u8) .take(32) .collect(); // HYBRID CONSTRUCTION: HKDF(PQC) ⊕ HKDF(Classical) let pqc_key = blake3_kdf(b"SNDL-PQC-SALT-v1", &pqc_entropy); let classical_key = blake3_kdf(b"SNDL-CLASSICAL-SALT-v1", &classical_entropy); let mut shared_key = [0u8; 32]; for i in 0..32 { shared_key[i] = pqc_key[i] ^ classical_key[i]; } // BIND TO JST FIXED POINT: key invalid if [U,ρ*]≠0 bind_to_fixed_point(&mut shared_key, jst_fixed_point); shared_key } /// Compute key freshness hash from density matrix (for replay detection) pub fn key_freshness_hash(rho: &Matrix) -> [u8; 32] { let entropy = state_to_entropy(rho); blake3_kdf(b"SNDL-FRESHNESS-v1", &entropy) } /// Rotate key using φ-decay factor (crypto-agility) pub fn sndl_key_rotate(current_key: &[u8; 32], depth: usize) -> [u8; 32] { let rotation_factor = PHI_INV.powi(depth as i32); let rotation_bytes: Vec = (0..32) .map(|i| ((rotation_factor * (i as f64 + 1.0) * 256.0) as u8) & 0xFF) .collect(); let mut new_key = [0u8; 32]; for i in 0..32 { new_key[i] = current_key[i].wrapping_add(rotation_bytes[i]); } blake3_kdf(b"SNDL-ROTATE-v1", &new_key) } // ═══════════════════════════════════════════════════════════════════════════ // C ABI EXPORTS — Called from jordan_block.f90 via iso_c_binding // ═══════════════════════════════════════════════════════════════════════════ /// FFI: Generate SNDL-resistant key (32 bytes written to out_ptr) #[no_mangle] pub extern "C" fn sndl_generate_key( h_ptr: *const Complex, rho_ptr: *const Complex, n: i64, depth: i64, pulse_ptr: *const f64, pulse_len: i64, out_ptr: *mut u8, ) { let n = n as usize; let h_slice = unsafe { std::slice::from_raw_parts(h_ptr, n * n) }; let rho_slice = unsafe { std::slice::from_raw_parts(rho_ptr, n * n) }; let pulse_slice = unsafe { std::slice::from_raw_parts(pulse_ptr, pulse_len as usize) }; let h = Matrix { data: h_slice.to_vec(), rows: n, cols: n }; let rho = Matrix { data: rho_slice.to_vec(), rows: n, cols: n }; let key = sndl_resistant_key(&h, pulse_slice, &rho, depth as usize); let out = unsafe { std::slice::from_raw_parts_mut(out_ptr, 32) }; out.copy_from_slice(&key); } /// FFI: Compute key freshness hash (32 bytes written to out_ptr) #[no_mangle] pub extern "C" fn sndl_freshness_hash( rho_ptr: *const Complex, n: i64, out_ptr: *mut u8, ) { let n = n as usize; let slice = unsafe { std::slice::from_raw_parts(rho_ptr, n * n) }; let rho = Matrix { data: slice.to_vec(), rows: n, cols: n }; let hash = key_freshness_hash(&rho); let out = unsafe { std::slice::from_raw_parts_mut(out_ptr, 32) }; out.copy_from_slice(&hash); } /// FFI: Rotate key using φ-decay (32 bytes in, 32 bytes out) #[no_mangle] pub extern "C" fn sndl_rotate_key( key_ptr: *const u8, depth: i64, out_ptr: *mut u8, ) { let key_slice = unsafe { std::slice::from_raw_parts(key_ptr, 32) }; let mut key = [0u8; 32]; key.copy_from_slice(key_slice); let rotated = sndl_key_rotate(&key, depth as usize); let out = unsafe { std::slice::from_raw_parts_mut(out_ptr, 32) }; out.copy_from_slice(&rotated); } /// FFI: Compute ZMOS spectral invariant Δ(t) /// Returns: Δ(t) as f64 (pole-zero proximity in complex s-plane) #[no_mangle] pub extern "C" fn zmos_spectral_invariant( h_ptr: *const Complex, n: i64, tau_k: f64, ) -> f64 { let n = n as usize; let slice = unsafe { std::slice::from_raw_parts(h_ptr, n * n) }; let h = Matrix { data: slice.to_vec(), rows: n, cols: n, }; spectral_invariant_delta(&h, tau_k) } /// FFI: Compute full ZMOS operator product Z(s,t) /// Writes result into out_ptr (n×n complex matrix) #[no_mangle] pub extern "C" fn zmos_operator_product( h_ptr: *const Complex, n: i64, s_re: f64, s_im: f64, t: f64, out_ptr: *mut Complex, ) { let n = n as usize; let slice = unsafe { std::slice::from_raw_parts(h_ptr, n * n) }; let h = Matrix { data: slice.to_vec(), rows: n, cols: n, }; let s = Complex::new(s_re, s_im); let result = zeta_operator_product(s, t, &h); let out_slice = unsafe { std::slice::from_raw_parts_mut(out_ptr, n * n) }; out_slice.copy_from_slice(&result.data); } /// FFI: Compute QMHES prime-encoded state and return its Frobenius norm /// Used by jordan_block.f90 for MMP gate verification #[no_mangle] pub extern "C" fn qmhes_prime_encoded_norm( h_ptr: *const Complex, n: i64, depth: i64, ) -> f64 { let n = n as usize; let slice = unsafe { std::slice::from_raw_parts(h_ptr, n * n) }; let h = Matrix { data: slice.to_vec(), rows: n, cols: n, }; let state = prime_encoded_state(&h, depth as usize); frobenius_norm(&state) } /// FFI: Compute QMHES MMP multiplicity for given density matrix /// Returns: ∏ₚ (1 + vₚ(‖ρₚ‖)) — system multiplicity #[no_mangle] pub extern "C" fn qmhes_mmp_multiplicity( h_ptr: *const Complex, n: i64, ) -> f64 { let n = n as usize; let slice = unsafe { std::slice::from_raw_parts(h_ptr, n * n) }; let h = Matrix { data: slice.to_vec(), rows: n, cols: n, }; mmp_multiplicity(&h) } /// FFI: Compute QMHES MMP bound φ⁻ᴺ for system dimension N #[no_mangle] pub extern "C" fn qmhes_mmp_bound(n: i64) -> f64 { mmp_bound(n as usize) } #[cfg(test)] mod tests { use super::*; #[test] fn test_identity_euler_product() { let h = Matrix::identity(2); let s = Complex::new(2.0, 0.0); let result = zeta_operator_product(s, 0.01, &h); // Should be finite and non-zero for Re(s) > 1 for val in &result.data { assert!(val.norm().is_finite()); } } #[test] fn test_spectral_invariant_positive() { let h = Matrix::identity(2); let delta = spectral_invariant_delta(&h, 0.01); assert!(delta > 0.0); assert!(delta.is_finite()); } #[test] fn test_pade13_identity() { let h = Matrix::zeros(2, 2); let result = pade13_exp(&h, 1.0); // exp(0) = I assert!((result.get(0, 0) - Complex::new(1.0, 0.0)).norm() < 1e-10); assert!((result.get(1, 1) - Complex::new(1.0, 0.0)).norm() < 1e-10); assert!((result.get(0, 1)).norm() < 1e-10); } #[test] fn test_prime_encoded_state_depth_zero() { let h = Matrix::identity(2); let state = prime_encoded_state(&h, 0); // depth=0 returns identity assert_eq!(state.rows, 2); assert!((state.get(0, 0) - Complex::new(1.0, 0.0)).norm() < 1e-10); } #[test] fn test_prime_encoded_state_depth_one() { let h = Matrix::identity(2); let state = prime_encoded_state(&h, 1); // Should produce a valid matrix with finite entries for val in &state.data { assert!(val.norm().is_finite()); } } #[test] fn test_mmp_multiplicity_identity() { let h = Matrix::identity(2); let mult = mmp_multiplicity(&h); assert!(mult >= 1.0); assert!(mult.is_finite()); } #[test] fn test_mmp_bound_decreases() { // φ⁻ᴺ decreases as N increases let b2 = mmp_bound(2); let b4 = mmp_bound(4); assert!(b4 < b2); } #[test] fn test_tensor_product_dimensions() { let a = Matrix::identity(2); let b = Matrix::identity(3); let c = tensor_product(&a, &b); assert_eq!(c.rows, 6); assert_eq!(c.cols, 6); } #[test] fn test_matrix_power_identity() { let m = Matrix::identity(3); let p = matrix_power(&m, 5); // I^5 = I assert!((p.get(0, 0) - Complex::new(1.0, 0.0)).norm() < 1e-10); assert!((p.get(1, 0)).norm() < 1e-10); } #[test] fn test_sndl_key_generation() { let h = Matrix::identity(2); let rho = Matrix::identity(2); let pulse = vec![0.5, 0.3, 0.8, 0.1]; let key = sndl_resistant_key(&h, &pulse, &rho, 1); // Key should be 32 bytes, non-zero assert_eq!(key.len(), 32); assert!(key.iter().any(|&b| b != 0)); } #[test] fn test_sndl_key_deterministic() { let h = Matrix::identity(2); let rho = Matrix::identity(2); let pulse = vec![0.5, 0.3, 0.8, 0.1]; let key1 = sndl_resistant_key(&h, &pulse, &rho, 1); let key2 = sndl_resistant_key(&h, &pulse, &rho, 1); // Same inputs → same key assert_eq!(key1, key2); } #[test] fn test_sndl_key_different_inputs() { let h = Matrix::identity(2); let rho = Matrix::identity(2); let pulse1 = vec![0.5, 0.3, 0.8, 0.1]; let pulse2 = vec![0.9, 0.1, 0.2, 0.7]; let key1 = sndl_resistant_key(&h, &pulse1, &rho, 1); let key2 = sndl_resistant_key(&h, &pulse2, &rho, 1); // Different inputs → different keys assert_ne!(key1, key2); } #[test] fn test_sndl_freshness_hash() { let rho = Matrix::identity(2); let hash = key_freshness_hash(&rho); assert_eq!(hash.len(), 32); assert!(hash.iter().any(|&b| b != 0)); } #[test] fn test_sndl_key_rotation() { let key = [42u8; 32]; let rotated = sndl_key_rotate(&key, 3); // Rotation should produce different key assert_ne!(key, rotated); // But still 32 bytes assert_eq!(rotated.len(), 32); } #[test] fn test_sndl_rotation_depth_matters() { let key = [42u8; 32]; let r1 = sndl_key_rotate(&key, 1); let r2 = sndl_key_rotate(&key, 5); // Different depths → different rotated keys assert_ne!(r1, r2); } #[test] fn test_blake3_kdf_deterministic() { let result1 = blake3_kdf(b"salt", b"input"); let result2 = blake3_kdf(b"salt", b"input"); assert_eq!(result1, result2); } #[test] fn test_blake3_kdf_different_salts() { let r1 = blake3_kdf(b"salt1", b"input"); let r2 = blake3_kdf(b"salt2", b"input"); assert_ne!(r1, r2); } }