203 lines
5.9 KiB
Rust
203 lines
5.9 KiB
Rust
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());
|
||
// }
|
||
// }
|