pub fn compute_r(rs_bit_count: usize, num_units: usize) -> usize {
if rs_bit_count == 0 {
return 1;
}
let r = num_units / rs_bit_count;
let r = r.min(255);
if r >= 3 {
let r_odd = r | 1;
if r_odd.saturating_mul(rs_bit_count) <= num_units {
r_odd
} else {
(r - 1).max(1)
}
} else if r >= 2 {
if 3usize.saturating_mul(rs_bit_count) <= num_units { 3 } else { 1 }
} else {
1
}
}
pub fn repetition_encode(rs_bits: &[u8], num_units: usize) -> (Vec<u8>, usize) {
let r = compute_r(rs_bits.len(), num_units);
let mut output = vec![0u8; num_units];
for copy in 0..r {
let start = copy * rs_bits.len();
for (i, &bit) in rs_bits.iter().enumerate() {
if start + i < num_units {
output[start + i] = bit;
}
}
}
(output, r)
}
#[cfg(test)]
pub fn repetition_decode_soft(llrs: &[f64], rs_bit_count: usize) -> Vec<u8> {
if rs_bit_count == 0 {
return Vec::new();
}
let r = llrs.len() / rs_bit_count;
let mut voted = Vec::with_capacity(rs_bit_count);
for i in 0..rs_bit_count {
let mut total_llr = 0.0;
for copy in 0..r {
let idx = copy * rs_bit_count + i;
if idx < llrs.len() {
total_llr += llrs[idx];
}
}
voted.push(if total_llr >= 0.0 { 0 } else { 1 });
}
voted
}
#[derive(Debug, Clone)]
pub struct RepetitionQuality {
pub avg_abs_llr_per_copy: f64,
}
pub fn repetition_decode_soft_with_quality(
llrs: &[f64],
rs_bit_count: usize,
) -> (Vec<u8>, RepetitionQuality) {
if rs_bit_count == 0 {
return (
Vec::new(),
RepetitionQuality {
avg_abs_llr_per_copy: 0.0,
},
);
}
let r = llrs.len() / rs_bit_count;
let mut voted = Vec::with_capacity(rs_bit_count);
let mut sum_abs_llr_per_copy = 0.0;
for i in 0..rs_bit_count {
let mut total_llr = 0.0;
for copy in 0..r {
let idx = copy * rs_bit_count + i;
if idx < llrs.len() {
total_llr += llrs[idx];
}
}
voted.push(if total_llr >= 0.0 { 0 } else { 1 });
if r > 0 {
sum_abs_llr_per_copy += total_llr.abs() / r as f64;
}
}
let avg_abs_llr_per_copy = if rs_bit_count > 0 {
sum_abs_llr_per_copy / rs_bit_count as f64
} else {
0.0
};
(
voted,
RepetitionQuality {
avg_abs_llr_per_copy,
},
)
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn compute_r_basic() {
assert_eq!(compute_r(100, 1000), 9);
assert_eq!(compute_r(100, 1100), 11);
assert_eq!(compute_r(100, 300), 3);
assert_eq!(compute_r(100, 150), 1);
assert_eq!(compute_r(100, 100), 1);
assert_eq!(compute_r(0, 1000), 1);
for bits in [8, 50, 100, 200, 500] {
for units in [100, 500, 1000, 5000, 50000] {
if units >= bits {
let r = compute_r(bits, units);
assert!(r * bits <= units, "r={r}, bits={bits}, units={units}: product {} > {units}", r * bits);
}
}
}
}
#[test]
fn compute_r_caps_at_255() {
assert_eq!(compute_r(1, 1000), 255);
}
#[test]
fn repetition_encode_decode_no_noise() {
let rs_bits = vec![0, 1, 1, 0, 1, 0, 0, 1]; let num_units = 100;
let (encoded, r) = repetition_encode(&rs_bits, num_units);
assert!(r >= 2);
assert_eq!(encoded.len(), num_units);
let used = r * rs_bits.len();
assert!(used <= num_units, "r*bits should fit within num_units");
let llrs: Vec<f64> = encoded[..used]
.iter()
.map(|&bit| if bit == 0 { 5.0 } else { -5.0 })
.collect();
let voted = repetition_decode_soft(&llrs, rs_bits.len());
assert_eq!(voted, rs_bits);
}
#[test]
fn soft_voting_corrects_noisy_copies() {
let rs_bits = vec![0, 1, 0, 1];
let r = 5;
let rs_bit_count = rs_bits.len();
let total = rs_bit_count * r;
let mut llrs = vec![0.0f64; total];
for i in 0..rs_bit_count {
let base_llr = if rs_bits[i] == 0 { 3.0 } else { -3.0 };
for copy in 0..r {
let idx = copy * rs_bit_count + i;
if i == 0 && copy < 2 {
llrs[idx] = -base_llr;
} else {
llrs[idx] = base_llr;
}
}
}
let voted = repetition_decode_soft(&llrs, rs_bit_count);
assert_eq!(voted, rs_bits);
}
#[test]
fn r_equals_1_passthrough() {
let rs_bits = vec![1, 0, 1, 1, 0];
let num_units = 5; let (encoded, r) = repetition_encode(&rs_bits, num_units);
assert_eq!(r, 1);
assert_eq!(&encoded[..5], &rs_bits);
let llrs: Vec<f64> = encoded
.iter()
.map(|&bit| if bit == 0 { 2.0 } else { -2.0 })
.collect();
let voted = repetition_decode_soft(&llrs, rs_bits.len());
assert_eq!(voted, rs_bits);
}
#[test]
fn with_quality_matches_soft_decode() {
let rs_bits = vec![0, 1, 1, 0, 1, 0, 0, 1];
let num_units = 100;
let (encoded, r) = repetition_encode(&rs_bits, num_units);
let used = r * rs_bits.len();
let llrs: Vec<f64> = encoded[..used]
.iter()
.map(|&bit| if bit == 0 { 5.0 } else { -5.0 })
.collect();
let voted_plain = repetition_decode_soft(&llrs, rs_bits.len());
let (voted_quality, quality) = repetition_decode_soft_with_quality(&llrs, rs_bits.len());
assert_eq!(voted_plain, voted_quality, "Quality variant must produce same bits");
assert_eq!(voted_quality, rs_bits);
assert!((quality.avg_abs_llr_per_copy - 5.0).abs() < 0.01,
"Expected avg_abs_llr_per_copy ≈ 5.0, got {}", quality.avg_abs_llr_per_copy);
}
#[test]
fn with_quality_noisy_signal_lower() {
let rs_bits = vec![0, 1, 0, 1];
let r = 5;
let rs_bit_count = rs_bits.len();
let total = rs_bit_count * r;
let pristine_llrs: Vec<f64> = (0..total)
.map(|idx| {
let bit_idx = idx % rs_bit_count;
if rs_bits[bit_idx] == 0 { 3.0 } else { -3.0 }
})
.collect();
let mut noisy_llrs = pristine_llrs.clone();
for copy in 0..2 {
let idx = copy * rs_bit_count; noisy_llrs[idx] = -noisy_llrs[idx]; }
let (_, pristine_q) = repetition_decode_soft_with_quality(&pristine_llrs, rs_bit_count);
let (voted, noisy_q) = repetition_decode_soft_with_quality(&noisy_llrs, rs_bit_count);
assert_eq!(voted, rs_bits);
assert!(noisy_q.avg_abs_llr_per_copy < pristine_q.avg_abs_llr_per_copy,
"Noisy signal ({}) should be weaker than pristine ({})",
noisy_q.avg_abs_llr_per_copy, pristine_q.avg_abs_llr_per_copy);
}
#[test]
fn with_quality_empty_input() {
let (voted, quality) = repetition_decode_soft_with_quality(&[], 0);
assert!(voted.is_empty());
assert_eq!(quality.avg_abs_llr_per_copy, 0.0);
}
}