use libm::{floor, log2, pow, sqrt};
pub const LDR_M_CAP: usize = 1000;
pub(crate) fn johnson_exceptional_line_count_log2(
log_degree: usize,
log_inv_rate: usize,
m: usize,
) -> f64 {
if m < 3 || log_inv_rate == 0 {
return f64::INFINITY;
}
if log_degree == 0 {
let log_n_minus_one = log_inv_rate as f64 + log2(1.0 - pow(2.0, -(log_inv_rate as f64)));
return log_inv_rate as f64 + log_n_minus_one - 1.0;
}
let log_n = log_degree as f64 + log_inv_rate as f64;
let log_rho_minus = log2(1.0 - pow(2.0, -(log_degree as f64))) - log_inv_rate as f64;
3.0 + log_n + 3.0 * log2(m as f64 + 0.5) - log2(3.0) - log_rho_minus
}
pub fn alpha_udr(log_trace_length: usize, log_blowup: usize, max_combo: usize) -> f64 {
let k = (1u64 << log_trace_length) as f64;
let n = (1u64 << (log_trace_length + log_blowup)) as f64;
let rho_plus = (k + max_combo as f64) / n;
(1.0 + rho_plus) * 0.5
}
pub fn alpha_ldr_m(log_blowup: usize, m: usize) -> f64 {
let rho = pow(2.0, -(log_blowup as f64));
(1.0 + 0.5 / m as f64) * sqrt(rho)
}
pub fn gamma_udr(log_trace_length: usize, log_blowup: usize, max_combo: usize) -> f64 {
1.0 - alpha_udr(log_trace_length, log_blowup, max_combo)
}
pub fn gamma_ldr_m(log_blowup: usize, m: usize) -> f64 {
let rho = pow(2.0, -(log_blowup as f64));
1.0 - sqrt(rho) * (1.0 + 0.5 / m as f64)
}
pub const fn list_size_udr() -> f64 {
1.0
}
pub const fn list_size_conjectured() -> f64 {
1.0
}
pub fn list_size_ldr_m(log_blowup: usize, m: usize) -> f64 {
let rho = pow(2.0, -(log_blowup as f64));
(m as f64 + 0.5) / sqrt(rho)
}
pub fn compute_upper_m(trace_length: usize, max_combo: usize) -> usize {
if trace_length == 0 {
return 0;
}
let h = trace_length as f64;
let ratio = (h + max_combo as f64) / h;
let x = 1.0 / (2.0 * (sqrt(ratio) - 1.0));
let m = floor(x);
if m == x {
(m - 1.0) as usize
} else {
m as usize
}
}
#[cfg(test)]
mod tests {
use super::johnson_exceptional_line_count_log2;
use crate::assumption::SecurityAssumption;
struct CurveCertificate {
challenge_degree: u128,
variables: u128,
equations: u128,
exceptional_count: f64,
}
fn finite_curve_certificate(d: usize, r: usize, m: usize, ell: usize) -> CurveCertificate {
let ceil_sqrt_ratio = |num: u128, den: u128| {
let root = (num / den).isqrt();
root + u128::from(root * root * den < num)
};
let k = 1u128 << d;
let n = k << r;
let degree = k - 1;
let m = m as u128;
let ell = ell as u128;
let four_t_squared = (2 * m + 1).pow(2);
let spectral_cutoff = ceil_sqrt_ratio(four_t_squared * degree * n, 4);
let jet_degree = ceil_sqrt_ratio(four_t_squared * n, 4 * degree) - 1;
let challenge_degree = (ell * four_t_squared * n - 1) / (12 * degree);
let agreements = ceil_sqrt_ratio(four_t_squared * k * n, 4 * m * m);
assert!(agreements > k && agreements <= n);
assert!(jet_degree >= m && challenge_degree >= ell * jet_degree);
let variables = (0..=jet_degree)
.map(|j| (spectral_cutoff - degree * j) * (challenge_degree + 1 - ell * j))
.sum();
let equations = n
* (0..m)
.map(|b| (m - b) * (challenge_degree + 1 - ell * b))
.sum::<u128>();
let psi = 1
+ (2 * degree - 1) * (2 * jet_degree - 1)
+ 2 * jet_degree.saturating_sub(2 * degree + 1);
let denominator = agreements - degree;
let numerator = ((2 * jet_degree - 1) * challenge_degree
+ ell * (n - degree - 1) * jet_degree)
* denominator
+ (n - degree) * (ell * jet_degree + challenge_degree * psi);
CurveCertificate {
challenge_degree,
variables,
equations,
exceptional_count: numerator as f64 / denominator as f64,
}
}
#[test]
fn curve_interpolation_keeps_the_rounding_residuals() {
for (d, r, m, ell, h, variables, equations) in [
(1, 1, 3, 3, 48, 1204, 1128),
(1, 2, 3, 1, 32, 1650, 1552),
(1, 2, 3, 3, 97, 4895, 4608),
(2, 2, 4, 1, 35, 6127, 5600),
] {
let certificate = finite_curve_certificate(d, r, m, ell);
assert_eq!(certificate.challenge_degree, h);
assert_eq!(certificate.variables, variables);
assert_eq!(certificate.equations, equations);
}
let line = finite_curve_certificate(1, 2, 3, 1);
let curve = finite_curve_certificate(1, 2, 3, 3);
assert_eq!(line.exceptional_count, 2293.75);
assert_eq!(curve.exceptional_count, 6950.75);
assert!(curve.exceptional_count > 3.0 * line.exceptional_count);
}
#[test]
fn johnson_curve_bound_covers_finite_interpolation_certificates() {
for d in [1, 2, 3, 5, 8, 12, 20] {
for r in [1, 2, 3, 4, 8] {
for m in [3, 4, 10, 31, 100, 1000] {
for ell in [1, 2, 3, 7, 31, 257, 1_572_866] {
let certificate = finite_curve_certificate(d, r, m, ell);
assert!(certificate.variables > certificate.equations);
let closed_log2 = 256.0
- SecurityAssumption::prox_gaps_error_jb_at_m(d, r, 256, ell + 1, m);
assert!(
libm::log2(certificate.exceptional_count) < closed_log2,
"d={d}, r={r}, m={m}, ell={ell}"
);
}
}
}
}
}
#[test]
fn johnson_line_count_matches_dkt26_vectors() {
for (log_degree, log_inv_rate, m, expected) in [
(1, 1, 3, 10.837102265451657_f64),
(2, 1, 3, 11.2521397647305),
(20, 2, 3, 30.83710364131352),
(20, 2, 10, 35.59199114347699),
] {
let actual = johnson_exceptional_line_count_log2(log_degree, log_inv_rate, m);
assert!(
(actual - expected).abs() < 1e-12,
"got {actual}, expected {expected}"
);
}
}
#[test]
fn johnson_line_count_handles_constant_and_invalid_inputs() {
for (r, pairs) in [(1, 1.0), (2, 6.0), (3, 28.0)] {
for m in [3, 10, 1000] {
let constant = johnson_exceptional_line_count_log2(0, r, m);
assert!((constant - libm::log2(pairs)).abs() < 1e-12);
}
}
assert!(johnson_exceptional_line_count_log2(1, 0, 3).is_infinite());
assert!(johnson_exceptional_line_count_log2(1, 1, 2).is_infinite());
assert!(johnson_exceptional_line_count_log2(usize::MAX, usize::MAX, 3).is_finite());
}
}