Files
ldpc/srctmp/encoder.rs
2026-06-01 09:13:24 +02:00

203 lines
5.9 KiB
Rust
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

use crate::code::{LdpcCode, SystematicForm};
use crate::matrix::{DenseMatrixGF2, SparseMatrixGF2};
use crate::{BitVec, Gf2, LdpcError, Result};
// Trait Encoder
pub trait Encoder: Send + Sync {
fn encode(&self, message: &[Gf2]) -> Result<BitVec>;
fn message_len(&self) -> usize;
fn codeword_len(&self) -> usize;
fn check_input(&self, msg: &[Gf2]) -> Result<()> {
if msg.len() != self.message_len() {
return Err(LdpcError::DimensionMismatch {
expected: self.message_len(),
got: msg.len(),
});
}
Ok(())
}
}
// Méthode d'encodage
#[derive(Debug, Clone)]
pub enum EncodingMethod {
// Via G = [I_k | P]. Complexité O(n^2). Point de départ simple
Systematic,
// Richardson-Urbanke. Complexité O(n + g^2), g = gap << n
RichardsonUrbanke,
}
// Encodeur systématique
// c = m * G = [m | m*P]
// Les bits de parité p = m * P sont calculés par multiplication dense
// Le mot de code est ensuite réordonné selon la permutation inverse
pub struct SystematicEncoder {
k: usize,
n: usize,
g: DenseMatrixGF2,
perm_inv: Vec<usize>,
}
impl SystematicEncoder {
pub fn new(code: &mut LdpcCode) -> Result<Self> {
code.compute_systematic_form()?;
let sf = code.systematic_form.as_ref().unwrap();
Ok(Self {
k: code.k(),
n: code.n(),
g: sf.g.clone(),
perm_inv: sf.col_perm_inv.clone(),
})
}
}
impl Encoder for SystematicEncoder {
fn encode(&self, message: &[Gf2]) -> Result<BitVec> {
self.check_input(message)?;
let c_perm = self.g.multiply_vec(message);
// Réordonner les bits selon la permutation inverse
let mut c = vec![0u8; self.n];
for (i, &ci) in c_perm.iter().enumerate() {
c[self.perm_inv[i]] = ci;
}
Ok(c)
}
fn message_len(&self) -> usize {
self.k
}
fn codeword_len(&self) -> usize {
self.n
}
}
// Encodeur Richardson-Urbanke
// H est réarrangée par permutations en :
//
// ┌ ┐
// │ A B T │
// H = │ │ T = triangulaire inférieure, φ = -ET^{-1}B + D
// │ C D E │
// └ ┘
//
// Encodage en deux étapes :
// 1. p₂ = φ^{-1} * (-ET^{-1}*As - Cs) [dense g×g, g = gap]
// 2. p₁ = T^{-1} * (As + Bp₂) [substitution arrière]
// Complexité totale : O(n + g²)
pub struct RichardsonUrbankeEncoder {
k: usize,
n: usize,
a: SparseMatrixGF2,
b: SparseMatrixGF2,
c: SparseMatrixGF2,
d: SparseMatrixGF2,
e: SparseMatrixGF2,
// T^{-1} : résolution par substitution arrière (T triangulaire inférieure)
t_inv: DenseMatrixGF2,
// φ^{-1} : petit (g×g), calculé une fois offline
phi_inv: DenseMatrixGF2,
col_perm_inv: Vec<usize>,
}
impl RichardsonUrbankeEncoder {
pub fn new(code: &LdpcCode) -> Result<Self> {
// TODO: implémenter le pré-traitement de H par permutations de lignes/colonnes
// pour identifier les blocs A, B, C, D, E, T et calculer phi^{-1}.
todo!("Pré-traitement Richardson-Urbanke")
}
// Substitution arrière sur T triangulaire inférieure : résout T*x = b en GF(2)
fn backward_substitution(t_inv: &DenseMatrixGF2, b: &[Gf2]) -> Vec<Gf2> {
let n = b.len();
let mut x = vec![0u8; n];
for i in 0..n {
let mut s = b[i];
for j in 0..i {
s ^= t_inv.get(i, j) & x[j];
}
x[i] = s;
}
x
}
}
impl Encoder for RichardsonUrbankeEncoder {
fn encode(&self, message: &[Gf2]) -> Result<BitVec> {
self.check_input(message)?;
// Étape 1 : As
let a_s = self.a.multiply_vec(message);
// Étape 2 : p₂ = φ^{-1} * (E*T^{-1}*As + Cs)
let t_inv_as = Self::backward_substitution(&self.t_inv, &a_s);
let e_t_inv_as = self.e.multiply_vec(&t_inv_as);
let c_s = self.c.multiply_vec(message);
let rhs: Vec<Gf2> = e_t_inv_as
.iter()
.zip(c_s.iter())
.map(|(&a, &b)| a ^ b)
.collect();
let p2 = self.phi_inv.multiply_vec(&rhs);
// Étape 3 : p₁ = T^{-1} * (As + Bp₂)
let b_p2 = self.b.multiply_vec(&p2);
let sum: Vec<Gf2> = a_s.iter().zip(b_p2.iter()).map(|(&a, &b)| a ^ b).collect();
let p1 = Self::backward_substitution(&self.t_inv, &sum);
// Assemblage et dépermutation
let mut c_perm: Vec<Gf2> = message
.iter()
.chain(p1.iter())
.chain(p2.iter())
.cloned()
.collect();
let mut c = vec![0u8; self.n];
for (i, &ci) in c_perm.iter().enumerate() {
c[self.col_perm_inv[i]] = ci;
}
Ok(c)
}
fn message_len(&self) -> usize {
self.k
}
fn codeword_len(&self) -> usize {
self.n
}
}
// Factory
pub fn build_encoder(code: &mut LdpcCode, method: EncodingMethod) -> Result<Box<dyn Encoder>> {
match method {
EncodingMethod::Systematic => Ok(Box::new(SystematicEncoder::new(code)?)),
EncodingMethod::RichardsonUrbanke => Ok(Box::new(RichardsonUrbankeEncoder::new(code)?)),
}
}
// #[cfg(test)]
// mod tests {
// use super::*;
// use crate::code::{CodeTopology, GenerationMethod, LdpcCode, LdpcParams};
//
// fn make_code() -> LdpcCode {
// LdpcCode::new(LdpcParams {
// n: 12,
// k: 6,
// topology: CodeTopology::Regular { wc: 2, wr: 4 },
// generation: GenerationMethod::Gallager,
// seed: Some(42),
// })
// .unwrap()
// }
//
// #[test]
// fn test_encoder_wrong_input_length_errors() {
// let mut code = make_code();
// let enc = SystematicEncoder::new(&mut code).unwrap();
// let result = enc.encode(&vec![0u8; 5]); // devrait être 6
// assert!(result.is_err());
// }
// }