1#[cfg(feature = "std")]
2use std::vec::Vec;
3
4#[cfg(not(feature = "std"))]
5use alloc::vec::Vec;
6
7use crate::base::intermediate_tuple;
8use crate::matrix::BinaryMatrix;
9use crate::octet::Octet;
10use crate::octet_matrix::DenseOctetMatrix;
11use crate::rng::rand;
12use crate::systematic_constants::extended_source_block_symbols;
13use crate::systematic_constants::num_hdpc_symbols;
14use crate::systematic_constants::num_intermediate_symbols;
15use crate::systematic_constants::num_ldpc_symbols;
16use crate::systematic_constants::num_lt_symbols;
17use crate::systematic_constants::num_pi_symbols;
18use crate::systematic_constants::{calculate_p1, systematic_index};
19
20#[allow(clippy::many_single_char_names)]
22pub fn enc_indices(
23 source_tuple: (u32, u32, u32, u32, u32, u32),
24 lt_symbols: u32,
25 pi_symbols: u32,
26 p1: u32,
27) -> Vec<usize> {
28 let w = lt_symbols;
29 let p = pi_symbols;
30 let (d, a, mut b, d1, a1, mut b1) = source_tuple;
31
32 assert!(d > 0);
33 assert!(1 <= a && a < w);
34 assert!(b < w);
35 assert!(d1 == 2 || d1 == 3);
36 assert!(1 <= a1 && a1 < p1);
37 assert!(b1 < p1);
38
39 let mut indices = Vec::with_capacity((d + d1) as usize);
40 indices.push(b as usize);
41
42 for _ in 1..d {
43 b = (b + a) % w;
44 indices.push(b as usize);
45 }
46
47 while b1 >= p {
48 b1 = (b1 + a1) % p1;
49 }
50
51 indices.push((w + b1) as usize);
52
53 for _ in 1..d1 {
54 b1 = (b1 + a1) % p1;
55 while b1 >= p {
56 b1 = (b1 + a1) % p1;
57 }
58 indices.push((w + b1) as usize);
59 }
60
61 indices
62}
63
64#[allow(non_snake_case)]
65fn generate_hdpc_rows(Kprime: usize, S: usize, H: usize) -> DenseOctetMatrix {
66 let mut matrix = DenseOctetMatrix::new(H, Kprime + S + H, 0);
67 let mut result: Vec<Vec<u8>> = vec![vec![0; Kprime + S]; H];
71 #[allow(clippy::needless_range_loop)]
74 for i in 0..H {
75 result[i][Kprime + S - 1] = Octet::alpha(i).byte();
76 }
77
78 for j in (0..=(Kprime + S - 2)).rev() {
83 #[allow(clippy::needless_range_loop)]
84 for i in 0..H {
85 result[i][j] = (Octet::alpha(1) * Octet::new(result[i][j + 1])).byte();
86 }
87 let rand6 = rand((j + 1) as u32, 6u32, H as u32) as usize;
88 let rand7 = rand((j + 1) as u32, 7u32, (H - 1) as u32) as usize;
89 let i1 = rand6;
90 let i2 = (rand6 + rand7 + 1) % H;
91 result[i1][j] ^= Octet::one().byte();
92 result[i2][j] ^= Octet::one().byte();
93 }
94
95 #[allow(clippy::needless_range_loop)]
97 for i in 0..H {
98 #[allow(clippy::needless_range_loop)]
99 for j in 0..(Kprime + S) {
100 if result[i][j] != 0 {
101 matrix.set(i, j, Octet::new(result[i][j]));
102 }
103 }
104 }
105
106 for i in 0..H {
108 matrix.set(i, i + (Kprime + S), Octet::one());
109 }
110
111 matrix
112}
113
114#[allow(non_snake_case)]
118pub fn generate_constraint_matrix<T: BinaryMatrix>(
119 source_block_symbols: u32,
120 encoded_symbol_indices: &[u32],
121) -> (T, DenseOctetMatrix) {
122 let Kprime = extended_source_block_symbols(source_block_symbols) as usize;
123 let S = num_ldpc_symbols(source_block_symbols) as usize;
124 let H = num_hdpc_symbols(source_block_symbols) as usize;
125 let W = num_lt_symbols(source_block_symbols) as usize;
126 let B = W - S;
127 let P = num_pi_symbols(source_block_symbols) as usize;
128 let L = num_intermediate_symbols(source_block_symbols) as usize;
129
130 assert!(S + H + encoded_symbol_indices.len() >= L);
131 let mut matrix = T::new(S + H + encoded_symbol_indices.len(), L, P);
132
133 for i in 0..B {
136 let a = 1 + i / S;
137
138 let b = i % S;
139 matrix.set(b, i, Octet::one());
140
141 let b = (b + a) % S;
142 matrix.set(b, i, Octet::one());
143
144 let b = (b + a) % S;
145 matrix.set(b, i, Octet::one());
146 }
147
148 for i in 0..S {
150 matrix.set(i, i + B, Octet::one());
151 }
152
153 for i in 0..S {
156 matrix.set(i, (i % P) + W, Octet::one());
157 matrix.set(i, ((i + 1) % P) + W, Octet::one());
158 }
159
160 let lt_symbols = num_lt_symbols(Kprime as u32);
162 let pi_symbols = num_pi_symbols(Kprime as u32);
163 let sys_index = systematic_index(Kprime as u32);
164 let p1 = calculate_p1(Kprime as u32);
165 for (row, &i) in encoded_symbol_indices.iter().enumerate() {
166 let tuple = intermediate_tuple(i, lt_symbols, sys_index, p1);
168
169 for j in enc_indices(tuple, lt_symbols, pi_symbols, p1) {
170 matrix.set(row + S + H, j, Octet::one());
171 }
172 }
173
174 (matrix, generate_hdpc_rows(Kprime, S, H))
175}
176
177#[cfg(feature = "std")]
178#[cfg(test)]
179mod tests {
180 use crate::constraint_matrix::generate_hdpc_rows;
181 use crate::octet::Octet;
182 use crate::octet_matrix::DenseOctetMatrix;
183 use crate::octets::{add_assign, fused_addassign_mul_scalar};
184 use crate::rng::rand;
185 use crate::systematic_constants::{
186 extended_source_block_symbols, num_hdpc_symbols, num_ldpc_symbols,
187 };
188 use rand::Rng;
189 use std::vec::Vec;
190
191 #[allow(non_snake_case)]
192 fn reference_generate_hdpc_rows(Kprime: usize, S: usize, H: usize) -> DenseOctetMatrix {
193 let mut matrix = DenseOctetMatrix::new(H, Kprime + S + H, 0);
194 let mut mt: Vec<Vec<u8>> = vec![vec![0; Kprime + S]; H];
199 #[allow(clippy::needless_range_loop)]
200 for i in 0..H {
201 #[allow(clippy::needless_range_loop)]
202 for j in 0..=(Kprime + S - 2) {
203 let rand6 = rand((j + 1) as u32, 6u32, H as u32) as usize;
204 let rand7 = rand((j + 1) as u32, 7u32, (H - 1) as u32) as usize;
205 if i == rand6 || i == (rand6 + rand7 + 1) % H {
206 mt[i][j] = 1;
207 }
208 }
209 mt[i][Kprime + S - 1] = Octet::alpha(i).byte();
210 }
211 let mut gamma_row = vec![0; Kprime + S];
214 #[allow(clippy::needless_range_loop)]
216 for j in 0..(Kprime + S) {
217 gamma_row[j] = Octet::alpha((Kprime + S - 1 - j) % 255).byte();
222 }
223 #[allow(clippy::needless_range_loop)]
224 for i in 0..H {
225 let mut result_row = vec![0; Kprime + S];
226 for j in 0..(Kprime + S) {
227 let scalar = Octet::new(mt[i][j]);
228 if scalar == Octet::zero() {
229 continue;
230 }
231 if scalar == Octet::one() {
232 add_assign(
233 &mut result_row[0..=j],
234 &gamma_row[(Kprime + S - j - 1)..(Kprime + S)],
235 );
236 } else {
237 fused_addassign_mul_scalar(
238 &mut result_row[0..=j],
239 &gamma_row[(Kprime + S - j - 1)..(Kprime + S)],
240 &scalar,
241 );
242 }
243 }
244 #[allow(clippy::needless_range_loop)]
245 for j in 0..(Kprime + S) {
246 if result_row[j] != 0 {
247 matrix.set(i, j, Octet::new(result_row[j]));
248 }
249 }
250 }
251
252 for i in 0..H {
254 matrix.set(i, i + (Kprime + S), Octet::one());
255 }
256
257 matrix
258 }
259
260 fn assert_matrices_eq(matrix1: &DenseOctetMatrix, matrix2: &DenseOctetMatrix) {
261 assert_eq!(matrix1.height(), matrix2.height());
262 assert_eq!(matrix1.width(), matrix2.width());
263 for i in 0..matrix1.height() {
264 for j in 0..matrix1.width() {
265 assert_eq!(
266 matrix1.get(i, j),
267 matrix2.get(i, j),
268 "Matrices are not equal at row={i} col={j}"
269 );
270 }
271 }
272 }
273
274 #[test]
275 #[allow(non_snake_case)]
276 fn fast_hdpc() {
277 let source_block_symbols = rand::thread_rng().gen_range(1..50000);
278 let Kprime = extended_source_block_symbols(source_block_symbols) as usize;
279 let S = num_ldpc_symbols(source_block_symbols) as usize;
280 let H = num_hdpc_symbols(source_block_symbols) as usize;
281 let expected = reference_generate_hdpc_rows(Kprime, S, H);
282 let generated = generate_hdpc_rows(Kprime, S, H);
283 assert_matrices_eq(&expected, &generated);
284 }
285}