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; 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, } impl SystematicEncoder { pub fn new(code: &mut LdpcCode) -> Result { 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 { 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, } impl RichardsonUrbankeEncoder { pub fn new(code: &LdpcCode) -> Result { // 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 { 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 { 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 = 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 = 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 = 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> { 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()); // } // }