use crate::data_structures::VerifierConfig;
#[cfg(feature = "prover")]
use crate::data_structures::ProverConfig;
#[cfg(feature = "prover")]
use binary_fields::BinaryFieldElement;
#[cfg(feature = "prover")]
use reed_solomon::reed_solomon;
pub const MIN_LOG_SIZE: u32 = 20;
pub const MAX_LOG_SIZE: u32 = 30;
const OPTIMAL_CONFIGS: [(u32, usize, u32, usize, &[(u32, usize)]); 11] = [
(20, 1, 14, 6, &[(10, 4)]),
(21, 1, 15, 6, &[(11, 4)]),
(22, 2, 16, 6, &[(12, 4), (8, 4)]),
(23, 2, 17, 6, &[(13, 4), (9, 4)]),
(24, 2, 18, 6, &[(14, 4), (10, 4)]),
(25, 2, 19, 6, &[(15, 4), (11, 4)]),
(26, 3, 20, 6, &[(16, 4), (12, 4), (8, 4)]),
(27, 3, 21, 6, &[(17, 4), (13, 4), (9, 4)]),
(28, 3, 22, 6, &[(18, 4), (14, 4), (10, 4)]),
(29, 3, 23, 6, &[(19, 4), (15, 4), (11, 4)]),
(30, 3, 23, 7, &[(19, 4), (15, 4), (11, 4)]),
];
#[inline]
pub fn log_size_for_len(len: usize) -> u32 {
if len == 0 {
return MIN_LOG_SIZE;
}
let log = (len as f64).log2().ceil() as u32;
log.clamp(MIN_LOG_SIZE, MAX_LOG_SIZE)
}
#[cfg(feature = "prover")]
pub fn prover_config_for_size<T, U>(len: usize) -> (ProverConfig<T, U>, usize)
where
T: BinaryFieldElement,
U: BinaryFieldElement,
{
let log_size = log_size_for_len(len);
let config = prover_config_for_log_size::<T, U>(log_size);
(config, 1 << log_size)
}
pub fn verifier_config_for_size(len: usize) -> VerifierConfig {
let log_size = log_size_for_len(len);
verifier_config_for_log_size(log_size)
}
#[cfg(feature = "prover")]
pub fn prover_config_for_log_size<T, U>(log_size: u32) -> ProverConfig<T, U>
where
T: BinaryFieldElement,
U: BinaryFieldElement,
{
let log_size = log_size.clamp(MIN_LOG_SIZE, MAX_LOG_SIZE);
let idx = (log_size - MIN_LOG_SIZE) as usize;
let (_, recursive_steps, initial_dim_log, initial_k, dims_ks) = OPTIMAL_CONFIGS[idx];
let inv_rate = 4;
let initial_n_log = initial_dim_log;
let initial_m_log = log_size - initial_dim_log;
let initial_dims = (1 << initial_n_log, 1 << initial_m_log);
let dims: Vec<(usize, usize)> = dims_ks
.iter()
.map(|&(dim_log, k)| {
let n_log = dim_log - k as u32;
(1 << n_log, 1 << k)
})
.collect();
let ks: Vec<usize> = dims_ks.iter().map(|&(_, k)| k).collect();
let initial_reed_solomon = reed_solomon::<T>(initial_dims.0, initial_dims.0 * inv_rate);
let reed_solomon_codes = dims
.iter()
.map(|&(m, _)| reed_solomon::<U>(m, m * inv_rate))
.collect();
ProverConfig {
recursive_steps,
initial_dims,
dims,
initial_k,
ks,
initial_reed_solomon,
reed_solomon_codes,
num_queries: 148, }
}
pub fn verifier_config_for_log_size(log_size: u32) -> VerifierConfig {
let log_size = log_size.clamp(MIN_LOG_SIZE, MAX_LOG_SIZE);
let idx = (log_size - MIN_LOG_SIZE) as usize;
let (_, recursive_steps, initial_dim_log, initial_k, dims_ks) = OPTIMAL_CONFIGS[idx];
let initial_dim = initial_dim_log as usize;
let log_dims: Vec<usize> = dims_ks
.iter()
.map(|&(dim_log, k)| (dim_log - k as u32) as usize)
.collect();
let ks: Vec<usize> = dims_ks.iter().map(|&(_, k)| k).collect();
VerifierConfig {
recursive_steps,
initial_dim,
log_dims,
initial_k,
ks,
num_queries: 148,
}
}
#[derive(Debug, Clone)]
pub struct ConfigInfo {
pub log_size: u32,
pub poly_size: usize,
pub recursive_steps: usize,
pub initial_k: usize,
pub ks: Vec<usize>,
pub estimated_proof_bytes: usize,
}
pub fn config_info(len: usize) -> ConfigInfo {
let log_size = log_size_for_len(len);
config_info_for_log_size(log_size)
}
pub fn config_info_for_log_size(log_size: u32) -> ConfigInfo {
let log_size = log_size.clamp(MIN_LOG_SIZE, MAX_LOG_SIZE);
let idx = (log_size - MIN_LOG_SIZE) as usize;
let (_, recursive_steps, _, initial_k, dims_ks) = OPTIMAL_CONFIGS[idx];
let ks: Vec<usize> = dims_ks.iter().map(|&(_, k)| k).collect();
let num_queries = 148;
let base_field_bytes = 4; let ext_field_bytes = 16;
let initial_row_size = 1 << initial_k;
let initial_data = num_queries * initial_row_size * base_field_bytes;
let recursive_data: usize = dims_ks
.iter()
.map(|&(_, k)| num_queries * (1 << k) * ext_field_bytes)
.sum();
let merkle_overhead = num_queries * 32 * (log_size as usize + 2) * (recursive_steps + 1);
let sumcheck_overhead = 48 * (initial_k + ks.iter().sum::<usize>());
let estimated_proof_bytes = initial_data + recursive_data + merkle_overhead + sumcheck_overhead;
ConfigInfo {
log_size,
poly_size: 1 << log_size,
recursive_steps,
initial_k,
ks,
estimated_proof_bytes,
}
}
#[cfg(feature = "std")]
pub fn print_config_summary() {
println!("Ligerito Configuration Summary");
println!("==============================");
println!(
"{:>6} {:>12} {:>8} {:>10} {:>12}",
"Log", "Poly Size", "Steps", "k values", "Est. Proof"
);
println!("{:-<6} {:-<12} {:-<8} {:-<10} {:-<12}", "", "", "", "", "");
for log_size in MIN_LOG_SIZE..=MAX_LOG_SIZE {
let info = config_info_for_log_size(log_size);
let ks_str = core::iter::once(info.initial_k)
.chain(info.ks.iter().copied())
.map(|k| k.to_string())
.collect::<Vec<_>>()
.join(",");
println!(
"{:>6} {:>12} {:>8} {:>10} {:>10} KB",
log_size,
format_size(info.poly_size),
info.recursive_steps,
ks_str,
info.estimated_proof_bytes / 1024
);
}
}
#[cfg(feature = "std")]
fn format_size(n: usize) -> String {
if n >= 1_000_000_000 {
format!("{:.1}G", n as f64 / 1_000_000_000.0)
} else if n >= 1_000_000 {
format!("{:.1}M", n as f64 / 1_000_000.0)
} else if n >= 1_000 {
format!("{:.1}K", n as f64 / 1_000.0)
} else {
format!("{}", n)
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_log_size_for_len() {
assert_eq!(log_size_for_len(0), 20);
assert_eq!(log_size_for_len(1), 20);
assert_eq!(log_size_for_len(1 << 20), 20);
assert_eq!(log_size_for_len((1 << 20) + 1), 21);
assert_eq!(log_size_for_len(1 << 24), 24);
assert_eq!(log_size_for_len(1 << 30), 30);
assert_eq!(log_size_for_len(usize::MAX), 30);
}
#[test]
fn test_verifier_config_consistency() {
for log_size in MIN_LOG_SIZE..=MAX_LOG_SIZE {
let config = verifier_config_for_log_size(log_size);
assert_eq!(config.recursive_steps, config.log_dims.len());
assert_eq!(config.recursive_steps, config.ks.len());
assert!(config.initial_k > 0);
assert!(config.initial_dim > 0);
let initial_total = config.initial_dim + config.initial_k;
assert_eq!(
initial_total as u32, log_size,
"initial_dim + initial_k should equal log_size for config {}",
log_size
);
}
}
#[test]
#[cfg(feature = "prover")]
fn test_prover_config_consistency() {
use ligerito_binary_fields::{BinaryElem128, BinaryElem32};
#[cfg(debug_assertions)]
let test_sizes = [20, 21, 22, 24];
#[cfg(not(debug_assertions))]
let test_sizes: Vec<u32> = (MIN_LOG_SIZE..=MAX_LOG_SIZE).collect();
for &log_size in &test_sizes {
let config = prover_config_for_log_size::<BinaryElem32, BinaryElem128>(log_size);
let initial_size = config.initial_dims.0 * config.initial_dims.1;
assert_eq!(
initial_size,
1 << log_size,
"initial dims should multiply to 2^{}",
log_size
);
assert_eq!(config.recursive_steps, config.dims.len());
assert_eq!(config.recursive_steps, config.ks.len());
assert_eq!(config.recursive_steps, config.reed_solomon_codes.len());
}
}
#[test]
fn test_config_info() {
let info = config_info_for_log_size(24);
assert_eq!(info.log_size, 24);
assert_eq!(info.poly_size, 1 << 24);
assert!(info.estimated_proof_bytes > 0);
assert!(info.estimated_proof_bytes < 1_000_000); }
#[test]
#[cfg(feature = "std")]
fn test_print_summary() {
print_config_summary();
}
}