use crate::math_utils::{is_prime, smallest_prime_ge};
use crate::parameters::r10::smallest_x_for_ldpc;
use fountain_engine::types::CodeParams;
pub fn generate_rq_like_parameters(k: usize) -> CodeParams {
assert!(k >= 1, "k must be >= 1");
let x = smallest_x_for_ldpc(k);
let l_target = (0.01 * k as f64 + x as f64).ceil() as usize;
let l = smallest_prime_ge(l_target.max(2));
let h = choose_h_10_to_16(k);
let target_a = if k < 60 {
k
} else {
let reduction = (1.57 * (k as f64).sqrt()).round() as usize;
k.saturating_sub(reduction).max(1)
};
let a = nearest_a_with_prime_sum(target_a, k, l);
CodeParams::new(k, a, l, h)
}
fn choose_h_10_to_16(k: usize) -> usize {
let min_k: f64 = 60.0_f64.ln();
let max_k: f64 = 56_403.0_f64.ln();
let h = 10.0 + 6.0 * ((k as f64).ln() - min_k) / (max_k - min_k);
h.round().clamp(10.0, 16.0) as usize
}
fn nearest_a_with_prime_sum(target_a: usize, k: usize, l: usize) -> usize {
let t = target_a.clamp(1, k);
if is_prime(t + l) {
return t;
}
for d in 1..=k {
if t + d <= k {
let a_up = t + d;
if is_prime(a_up + l) {
return a_up;
}
}
if t > d {
let a_down = t - d;
if is_prime(a_down + l) {
return a_down;
}
}
}
1
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_rq_like_basic_properties() {
for k in [2usize, 10, 100, 1000, 10_000, 56_403] {
let p = generate_rq_like_parameters(k);
assert_eq!(p.k, k);
assert!((10..=16).contains(&p.h));
assert!(is_prime(p.l));
assert!(is_prime(p.a + p.l));
assert!(p.a >= 1 && p.a <= k);
}
}
#[test]
fn test_rq_like_l_close_to_target() {
for k in [100usize, 1000, 10_000, 56_403] {
let p = generate_rq_like_parameters(k);
let target = 0.01 * k as f64 + (2.0 * k as f64).sqrt();
let diff = (p.l as f64 - target).abs();
assert!(diff < 20.0);
}
}
#[test]
fn test_rq_like_a_piecewise_rule() {
for k in [10usize, 30, 59] {
let p = generate_rq_like_parameters(k);
assert!(k.saturating_sub(p.a) <= 5, "k<60 should keep a near k");
}
for k in [60usize, 100, 1000, 10_000] {
let p = generate_rq_like_parameters(k);
let target = k as f64 - 1.57 * (k as f64).sqrt();
assert!((p.a as f64 - target).abs() <= 10.0);
}
}
#[test]
fn test_print_parameters_for_range_of_k() {
for k in [10, 30, 49, 60, 101, 180, 168, 573, 811, 1136, 1579, 2125, 2831, 3751, 4901, 6296, 8111, 10351, 13143, 16674, 21199, 26838, 33961, 42916, 56403] {
let p = generate_rq_like_parameters(k);
println!("k: {}, a: {}, l: {}, h: {}", k, p.a, p.l, p.h);
}
}
}