use crate::types::ProbValue;
pub fn ideal_soliton_pdf(m: usize, dmax: usize) -> Vec<ProbValue> {
let cdf = ideal_soliton_cdf(m, dmax);
cdf_to_pdf(&cdf)
}
pub fn robust_soliton_pdf(m: usize, dmax: usize, c: f64, delta: f64) -> Vec<ProbValue> {
let cdf = robust_soliton_cdf(m, dmax, c, delta);
cdf_to_pdf(&cdf)
}
pub fn ideal_soliton_cdf(m: usize, dmax: usize) -> Vec<ProbValue> {
let mut cdf = vec![0usize; dmax+1];
cdf[1] = m / dmax;
for i in 2..dmax {
cdf[i] = m - m / i + cdf[1];
}
cdf[dmax] = m;
cdf.iter().map(|x| *x as u32).collect()
}
pub fn robust_soliton_cdf(m: usize, dmax: usize, c: f64, delta: f64) -> Vec<ProbValue> {
let ideal_pdf = ideal_soliton_pdf(m, dmax)
.iter()
.map(|x| *x as f64 / m as f64)
.collect::<Vec<f64>>();
let kf = dmax as f64;
let r = (kf.sqrt() * c * (kf / delta).ln()).max(1.0);
let upper = (kf / r).ceil() as usize;
let mut tau = vec![0.0; dmax + 1];
for i in 1..upper.min(dmax) {
tau[i] = r / (i as f64 * kf);
}
if upper <= dmax {
tau[upper] = r * (r / delta).ln() / kf;
}
let mut cdf = vec![0.0; dmax + 1];
for i in 1..=dmax {
cdf[i] = cdf[i-1] + ideal_pdf[i] + tau[i];
}
let a = cdf[dmax] as f64;
let mut cdf32 = vec![0u32; dmax + 1];
for i in 1..dmax {
cdf32[i] = (cdf[i] / a * m as f64) as u32;
}
cdf32[dmax] = m as u32;
if cdf32[dmax-1] > cdf32[dmax] {
panic!("Invalid degree distribution: cdf[dmax-1] > cdf[dmax]");
}
cdf32
}
pub fn asymp_optimal_cdf(m: usize, d1: usize, dmax: usize) -> Vec<ProbValue> {
if d1 > m {
panic!("Invalid degree distribution: d1 > m");
}
if dmax < 1 {
panic!("dmax must be at least 1");
}
let mut cdf = vec![0usize; dmax + 1];
cdf[1] = d1;
let m2 = m - d1;
for i in 2..dmax {
cdf[i] = d1 + m2 - m2 / i;
}
cdf[dmax] = m;
cdf.iter().map(|x| *x as u32).collect()
}
pub const RAPTOR10_CDF: [ProbValue; 8] = [
0, 10241, 491582, 712794, 831695, 948446, 1032189, 1048576
];
pub const RAPTORQ_CDF: [ProbValue; 31] = [
0, 5243, 529531, 704294, 791675, 844104, 879057, 904023,
922747, 937111, 948962, 958494, 966438, 973160, 978921,
983914, 988283, 992138, 995565, 998631, 1001391, 1003887,
1006157, 1008229, 1010129, 1011876, 1013490, 1014983, 1016370,
1017662, 1048576
];
pub fn cdf_to_pdf(cdf: &[ProbValue]) -> Vec<ProbValue> {
let mut pdf = vec![0u32; cdf.len()];
for i in 1..cdf.len() {
pdf[i] = cdf[i] - cdf[i-1];
}
pdf
}
pub fn pdf_to_cdf(pdf: &[ProbValue]) -> Vec<ProbValue> {
let mut cdf = vec![0u32; pdf.len()];
for i in 1..pdf.len() {
cdf[i] = cdf[i-1] + pdf[i];
}
cdf
}
pub fn validate_pdf(pdf: &[ProbValue], m: usize) -> bool {
if pdf[0] != 0 {
return false;
}
let sum: u64 = pdf.iter().map(|&x| x as u64).sum();
if sum == 0 {
return false;
}
if sum != m as u64 {
return false;
}
true
}
pub fn validate_cdf(cdf: &[ProbValue], m: usize) -> bool {
if cdf.len() < 2 {
return false;
}
if cdf[0] != 0 {
return false;
}
if cdf[cdf.len() - 1] != m as u32 {
return false;
}
if m == 0 {
return false;
}
for win in cdf.windows(2) {
let a = win[0];
let b = win[1];
if a > m as u32 || b > m as u32 {
return false;
}
if a > b {
return false;
}
}
true
}
#[cfg(test)]
mod tests {
use super::*;
use rand::rngs::StdRng;
use rand::Rng;
use rand::SeedableRng;
#[test]
fn test_raptor10_cdf() {
assert!(validate_cdf(RAPTOR10_CDF.as_slice(), 1048576));
}
#[test]
fn test_raptorq_cdf() {
assert!(validate_cdf(RAPTORQ_CDF.as_slice(), 1048576));
}
#[test]
fn print_cdf() {
let m = 2usize << 19;
let cdf = asymp_optimal_cdf(m, 10241, 7);
println!("cdf of ideal soliton distribution with dmax=7: {:?}", cdf);
let cdf = asymp_optimal_cdf(m, 5243, 31);
println!("cdf of ideal soliton distribution with dmax=31: {:?}", cdf);
}
#[test]
fn test_asymp_optimal_cdf() {
for m in 32..=1024 {
for d1 in 0..=10 {
let cdf = asymp_optimal_cdf(m, d1, 100);
assert!(validate_cdf(&cdf, m));
}
}
}
fn random_pdf(entries: usize, m: usize, rng: &mut StdRng) -> Vec<u32> {
assert!(entries >= 2, "entries must be at least 2");
assert!((1..=32).contains(&m), "m must be in 1..=32");
let mut pdf = vec![0u32; entries];
let mut remaining = m as u32;
for item in pdf.iter_mut().take(entries - 1).skip(1) {
let v = rng.gen_range(0..=remaining);
*item = v;
remaining -= v;
}
pdf[entries - 1] = remaining;
pdf
}
#[test]
fn test_cdf_to_pdf() {
let mut rng = StdRng::seed_from_u64(20_260_315);
for m in [8usize, 12, 16, 24, 32] {
for entries in [2usize, 3, 5, 10, 32, 64] {
for _ in 0..20 {
let pdf = random_pdf(entries, m, &mut rng);
assert!(validate_pdf(&pdf, m));
let cdf = pdf_to_cdf(&pdf);
let new_pdf = cdf_to_pdf(&cdf);
let new_cdf = pdf_to_cdf(&new_pdf);
assert_eq!(new_cdf, cdf);
assert_eq!(new_pdf, pdf);
}
}
}
}
#[test]
fn test_ideal_soliton_pdf() {
for m in 8..=32 {
for k in 1..100 {
let pdf = ideal_soliton_pdf(m, k);
assert!(validate_pdf(&pdf, m));
}
}
}
#[test]
fn test_robust_soliton_pdf() {
for m in 8..=32 {
for k in 1..100 {
let pdf = robust_soliton_pdf(m, k, 0.1, 0.05);
assert!(validate_pdf(&pdf, m));
}
}
}
#[test]
fn test_ideal_soliton_cdf() {
for m in 8..=32 {
for dmax in 1..100 {
let cdf = ideal_soliton_cdf(m, dmax);
assert!(validate_cdf(&cdf, m));
}
}
}
#[test]
fn test_robust_soliton_cdf() {
for m in 8..=32 {
for dmax in 1..100 {
let cdf = robust_soliton_cdf(m, dmax, 0.1, 0.05);
assert!(validate_cdf(&cdf, m));
}
}
}
#[test]
fn test_validate_cdf_invalid() {
let dmax = 10;
let m = 1usize << 8;
let base = ideal_soliton_cdf(m, dmax);
let mut bad = base.clone();
bad[0] = 1;
assert!(!validate_cdf(&bad, m));
let mut bad = base.clone();
let prev = bad[1];
bad[2] = prev.saturating_sub(1); assert!(!validate_cdf(&bad, m));
let mut bad = base.clone();
bad[dmax] = (m - 1) as u32;
assert!(!validate_cdf(&bad, m));
let mut bad = base.clone();
bad[1] = (m + 1) as u32;
assert!(!validate_cdf(&bad, m));
}
}