use super::degree_dist::*;
use crate::types::ProbValue;
pub fn sample_degree_from_cdf(cdf: &[ProbValue], r: usize) -> usize {
let mut i = 0;
while r >= cdf[i] as usize {
i += 1;
}
i
}
pub fn sample_degree_from_pdf(pdf: &[ProbValue], r: usize) -> usize {
let cdf = pdf_to_cdf(pdf);
sample_degree_from_cdf(&cdf, r)
}
pub struct DeterministicDegreeValues {
pdf: Vec<u32>,
m: u32,
n_d: Vec<usize>,
last_n: u32,
}
impl DeterministicDegreeValues {
pub fn new_from_cdf(cdf: Vec<u32>) -> Self {
assert!(cdf.len() >= 2, "CDF must cover at least degree 1 (len >= 2)");
let m = *cdf.last().expect("cdf non-empty");
if !validate_cdf(&cdf, m as usize) {
panic!("Invalid CDF: {:?}", cdf);
}
let pdf = cdf_to_pdf(&cdf);
let len = pdf.len();
Self {
pdf,
m,
n_d: vec![0; len],
last_n: 0,
}
}
pub fn new_from_pdf(pdf: Vec<u32>, m: u32) -> Self {
let sum: u64 = pdf.iter().map(|&x| x as u64).sum();
assert_eq!(
sum,
m as u64,
"PDF sum must equal m"
);
let len = pdf.len();
Self {
pdf,
m,
n_d: vec![0; len],
last_n: 0,
}
}
#[inline]
pub fn counts(&self) -> &[usize] {
&self.n_d
}
pub fn recompute_counts(&mut self, n: u32) {
let n_usize = n as usize;
let mut counts = vec![0usize; self.pdf.len()];
if n == 0 {
self.n_d = counts;
self.last_n = 0;
return;
}
let mut sum_q = 0usize;
let mut meta: Vec<(u32, usize)> = Vec::new();
let m_u64 = self.m as u64;
let n_u64 = n as u64;
for d in 1..self.pdf.len() {
let pd_u64 = self.pdf[d] as u64;
let prod_u64 = n_u64 * pd_u64;
let q = (prod_u64 / m_u64) as usize;
let r = (prod_u64 % m_u64) as u32;
counts[d] = q;
sum_q = sum_q.saturating_add(q);
meta.push((r, d));
}
meta.sort_by(|a, b| b.0.cmp(&a.0).then_with(|| a.1.cmp(&b.1)));
let mut deficit = n_usize.saturating_sub(sum_q);
debug_assert!(deficit <= meta.len() || meta.is_empty());
let mut i = 0;
while deficit > 0 && i < meta.len() {
let d = meta[i].1;
counts[d] += 1;
deficit -= 1;
i += 1;
}
assert_eq!(
deficit, 0,
"apportionment deficit should be zero for a valid PDF (sum pdf == m)"
);
self.n_d = counts;
self.last_n = n;
}
pub fn generate(&mut self, n: u32) -> Vec<usize> {
self.recompute_counts(n);
let mut seq = Vec::with_capacity(n as usize);
for d in 1..self.n_d.len() {
for _ in 0..self.n_d[d] {
seq.push(d);
}
}
debug_assert_eq!(seq.len(), n as usize);
seq
}
pub fn additional_degrees(&mut self, n: u32, n_prime: u32) -> Vec<usize> {
assert!(
n_prime >= n,
"n_prime must be >= n"
);
self.recompute_counts(n);
let old = self.n_d.clone();
self.recompute_counts(n_prime);
let mut seq = Vec::with_capacity((n_prime - n) as usize);
for d in 1..self.n_d.len() {
let add = self.n_d[d].saturating_sub(old[d]);
for _ in 0..add {
seq.push(d);
}
}
debug_assert_eq!(seq.len(), (n_prime - n) as usize);
seq
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_deterministic_degree_values_uniform() {
let pdf = vec![0u32, 1, 1, 1];
let mut g = DeterministicDegreeValues::new_from_pdf(pdf, 3);
let seq = g.generate(3);
assert_eq!(seq, vec![1, 2, 3]);
}
#[test]
fn test_deterministic_degree_values_counts_sum() {
let m = 256u32;
for dmax in 2..15 {
let pdf = ideal_soliton_pdf(m as usize, dmax);
for n in [0usize, 1, 7, 100, 1000] {
let mut g = DeterministicDegreeValues::new_from_pdf(pdf.clone(), m);
let seq = g.generate(n as u32);
assert_eq!(seq.len(), n);
let s: usize = g.counts().iter().sum();
assert_eq!(s, n);
}
}
}
#[test]
fn test_deterministic_additional_matches_full() {
let pdf = vec![0u32, 90, 10];
let m = 100u32;
let mut g = DeterministicDegreeValues::new_from_pdf(pdf.clone(), m);
let first = g.generate(10);
let add = g.additional_degrees(10, 20);
let mut comb = first.clone();
comb.extend_from_slice(&add);
comb.sort_unstable();
let mut g2 = DeterministicDegreeValues::new_from_pdf(pdf, m);
let full = g2.generate(20);
assert_eq!(comb, full);
}
#[test]
fn test_deterministic_from_cdf_ideal_soliton() {
let m = 64usize;
let dmax = 8usize;
let cdf = ideal_soliton_cdf(m, dmax);
let mut g = DeterministicDegreeValues::new_from_cdf(cdf);
let seq = g.generate(50);
assert_eq!(seq.len(), 50);
assert_eq!(g.counts().iter().sum::<usize>(), 50);
}
}