#[allow(clippy::doc_lazy_continuation)]
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
pub struct Params {
pub n_star: u32,
}
impl Params {
pub const K: u32 = 16;
pub const fn new(n_star: u32) -> Self {
assert!(
n_star >= 1,
"Params::new: n_star must be ≥ 1 (per spec §4.1)"
);
assert!(
n_star < u32::MAX,
"Params::new: n_star + 1 must be a power of two (n_star = u32::MAX overflows)"
);
assert!(
((n_star + 1) & n_star) == 0,
"Params::new: n_star + 1 must be a power of two (recommended regime: \
n_star ∈ {{1, 3, 7, 15, 31, 63, 127, 255, 511, 1023, …}})"
);
assert!(
n_star <= 16_383,
"Params::new: n_star must be ≤ 16383 = 2^14 − 1 (the field's 2-adicity \
caps the position space at T ≤ 2^34; larger budgets are not deployable)"
);
Self { n_star }
}
pub fn nu(&self) -> u32 {
let prod = (self.n_star as u64 + 1) * Self::K as u64;
ceil_log2_u64(prod)
}
pub fn m(&self) -> usize {
1usize << self.nu()
}
pub fn d(&self) -> usize {
self.m() - 1
}
pub fn t(&self) -> usize {
let base = (self.n_star as u64) * (Self::K as u64) * 65_536;
(base.max(1) as usize).next_power_of_two()
}
pub fn n_cliff(&self) -> usize {
self.m().div_ceil(Self::K as usize)
}
pub(crate) fn canonical_bytes(&self) -> [u8; 4] {
self.n_star.to_le_bytes()
}
pub const THETA: u64 = 64;
pub fn nu_prime(&self) -> u32 {
let total = self.m() as u64 + (self.n_star as u64).saturating_mul(Self::THETA);
let total = total.max(self.m() as u64 + Self::THETA);
ceil_log2_u64(total)
}
pub fn n(&self) -> usize {
1usize << self.nu_prime()
}
pub const M_ZK: usize = 64;
pub const T_ZK: usize = 32;
pub const RATE_INV_ZK: usize = 16;
pub const L_ZK: usize = 3;
}
fn ceil_log2_u64(x: u64) -> u32 {
assert!(x > 0, "ceil_log2_u64: input must be positive");
if x == 1 {
0
} else {
64 - (x - 1).leading_zeros()
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn k_is_sixteen() {
assert_eq!(Params::K, 16);
}
#[test]
fn reference_n_star_1023() {
let p = Params::new(1023);
assert_eq!(p.nu(), 14);
assert_eq!(p.m(), 1 << 14);
assert_eq!(p.d(), (1 << 14) - 1);
assert_eq!(p.n_cliff(), 1024);
assert_eq!(p.nu_prime(), 17);
assert_eq!(p.n(), 1 << 17);
}
#[test]
fn nu_prime_strictly_greater_than_nu() {
for n_star in [1u32, 3, 7, 15, 31, 63, 127, 255, 511, 1023] {
let p = Params::new(n_star);
assert!(p.nu_prime() > p.nu(), "n_star={n_star}");
assert!(
(p.n() - p.m()) as u64 >= (n_star as u64) * Params::THETA,
"n_star={n_star}: B = {} < n* · θ = {}",
p.n() - p.m(),
(n_star as u64) * Params::THETA
);
}
}
#[test]
fn n_cliff_equals_n_star_plus_one_in_recommended_regime() {
for n_star in [1u32, 3, 7, 15, 31, 63, 127, 255, 511, 1023] {
let p = Params::new(n_star);
assert_eq!(p.n_cliff(), (n_star + 1) as usize, "n_star={n_star}");
}
}
#[test]
fn t_is_power_of_two() {
for n_star in [1u32, 3, 15, 63, 1023] {
let p = Params::new(n_star);
assert!(p.t().is_power_of_two(), "n*={n_star} t={}", p.t());
}
}
#[test]
fn t_matches_paper_for_reference_configs() {
assert_eq!(Params::new(127).t(), 1 << 27);
assert_eq!(Params::new(1023).t(), 1 << 30);
assert_eq!(Params::new(16_383).t(), 1 << 34);
}
#[test]
fn canonical_bytes_layout() {
assert_eq!(Params::new(1023).canonical_bytes(), 0x3ff_u32.to_le_bytes());
}
#[test]
#[should_panic(expected = "n_star must be ≥ 1")]
fn new_rejects_zero() {
let _ = Params::new(0);
}
#[test]
fn new_rejects_non_recommended_regime() {
for bad in [2u32, 4, 5, 6, 8, 9, 16, 64, 100, 128, 1000, 16_384] {
let result = std::panic::catch_unwind(|| Params::new(bad));
assert!(
result.is_err(),
"Params::new({bad}) should have panicked (n_star + 1 not a power of two)"
);
}
}
#[test]
fn new_rejects_above_2adicity_ceiling() {
for bad in [32_767u32, 65_535, (1 << 20) - 1] {
let result = std::panic::catch_unwind(|| Params::new(bad));
assert!(
result.is_err(),
"Params::new({bad}) should have panicked (T > 2^34)"
);
}
}
#[test]
fn new_accepts_full_recommended_set() {
for k in 1u32..=14 {
let n_star = (1u32 << k) - 1;
let p = Params::new(n_star);
assert_eq!(
p.n_cliff(),
(n_star + 1) as usize,
"n_cliff must equal n_star + 1 for every recommended n_star"
);
}
}
#[test]
fn ceil_log2_smoke() {
assert_eq!(ceil_log2_u64(1), 0);
assert_eq!(ceil_log2_u64(2), 1);
assert_eq!(ceil_log2_u64(3), 2);
assert_eq!(ceil_log2_u64(4), 2);
assert_eq!(ceil_log2_u64(1 << 14), 14);
assert_eq!(ceil_log2_u64((1 << 14) + 1), 15);
}
}