use comnum_core::{Error, Result};
pub trait BlockCode {
fn k(&self) -> usize;
fn n(&self) -> usize;
fn encode_block(&self, info: &[u8]) -> Result<Vec<u8>>;
fn decode_block(&self, llr: &[f64]) -> Result<Vec<u8>>;
fn encode_block_into(&self, info: &[u8], out: &mut [u8]) -> Result<()> {
let c = self.encode_block(info)?;
comnum_core::same_len(out.len(), c.len(), "coded bits")?;
out.copy_from_slice(&c);
Ok(())
}
fn decode_block_into(&self, llr: &[f64], out: &mut [u8]) -> Result<()> {
let d = self.decode_block(llr)?;
comnum_core::same_len(out.len(), d.len(), "decoded bits")?;
out.copy_from_slice(&d);
Ok(())
}
fn code_rate(&self) -> f64 {
self.k() as f64 / self.n() as f64
}
fn encode_stream(&self, info: &[u8]) -> Result<Vec<u8>> {
let k = self.k();
whole_blocks(info.len(), k, "bits")?;
let mut out = Vec::with_capacity(info.len() / k * self.n());
for b in info.chunks(k) {
out.extend(self.encode_block(b)?);
}
Ok(out)
}
fn decode_stream(&self, llr: &[f64]) -> Result<Vec<u8>> {
let n = self.n();
whole_blocks(llr.len(), n, "LLRs")?;
let mut out = Vec::with_capacity(llr.len() / n * self.k());
for b in llr.chunks(n) {
out.extend(self.decode_block(b)?);
}
Ok(out)
}
}
pub trait SoftDecode: BlockCode {
fn app_into(&self, llr: &[f64], apriori: Option<&[f64]>, out: &mut [f64]) -> Result<usize>;
fn app(&self, llr: &[f64], apriori: Option<&[f64]>) -> Result<Vec<f64>> {
let mut out = vec![0.0; self.k()];
self.app_into(llr, apriori, &mut out)?;
Ok(out)
}
}
pub(crate) use comnum_core::expect_len;
macro_rules! container_traits {
($t:ty) => {
$crate::block::container_traits!($t,);
};
($t:ty, $($g:tt)*) => {
impl<$($g)*> comnum_core::traits::Encode for $t {
fn rate(&self) -> f64 {
$crate::block::BlockCode::code_rate(self)
}
fn encode_into(
&self,
info: &comnum_core::signal::Bits,
out: &mut comnum_core::signal::Bits,
) -> comnum_core::Result<()> {
let (k, n) = ($crate::block::BlockCode::k(self), $crate::block::BlockCode::n(self));
$crate::block::whole_blocks(info.len(), k, "bits")?;
out.resize(info.n_streams(), info.len() / k * n);
for s in 0..info.n_streams() {
let o = out.stream_mut(s);
for (b, c) in info.stream(s).chunks(k).zip(o.chunks_mut(n)) {
$crate::block::BlockCode::encode_block_into(self, b, c)?;
}
}
Ok(())
}
}
impl<$($g)*> comnum_core::traits::Decode for $t {
fn decode_into(
&mut self,
llr: &comnum_core::signal::Llr,
out: &mut comnum_core::signal::Bits,
) -> comnum_core::Result<()> {
let (k, n) = ($crate::block::BlockCode::k(self), $crate::block::BlockCode::n(self));
$crate::block::whole_blocks(llr.len(), n, "LLRs")?;
out.resize(llr.n_streams(), llr.len() / n * k);
for s in 0..llr.n_streams() {
let o = out.stream_mut(s);
for (l, d) in llr.stream(s).chunks(n).zip(o.chunks_mut(k)) {
$crate::block::BlockCode::decode_block_into(self, l, d)?;
}
}
Ok(())
}
}
};
}
pub(crate) use container_traits;
pub(crate) fn whole_blocks(len: usize, block: usize, what: &str) -> Result<()> {
if block == 0 || len % block != 0 {
return Err(Error::Shape(format!("{len} {what} is not a multiple of the block length {block}")));
}
Ok(())
}
pub(crate) use comnum_math::{Jacobian, max_star};
#[cfg(test)]
pub(crate) mod testing {
use comnum_core::rng::Rng;
pub(crate) fn awgn_llr(c: &[u8], s2: f64, rng: &mut Rng) -> Vec<f64> {
let s = s2.sqrt();
c.iter().map(|&b| 2.0 * ((1.0 - 2.0 * f64::from(b)) + s * rng.gaussian()) / s2).collect()
}
}