1#[cfg(feature = "std")]
2use std::{mem::size_of, vec::Vec};
3
4#[cfg(not(feature = "std"))]
5use core::mem::size_of;
6
7#[cfg(not(feature = "std"))]
8use alloc::vec::Vec;
9
10use crate::arraymap::{ImmutableListMap, ImmutableListMapBuilder};
11use crate::iterators::OctetIter;
12use crate::matrix::BinaryMatrix;
13use crate::octet::Octet;
14use crate::octets::BinaryOctetVec;
15use crate::sparse_vec::SparseBinaryVec;
16use crate::util::get_both_indices;
17
18#[derive(Clone, Debug, PartialEq, PartialOrd, Eq, Ord, Hash)]
26pub struct SparseBinaryMatrix {
27 height: usize,
28 width: usize,
29 sparse_elements: Vec<SparseBinaryVec>,
30 dense_elements: Vec<u64>,
33 sparse_columnar_values: Option<ImmutableListMap>,
35 logical_row_to_physical: Vec<u32>,
37 physical_row_to_logical: Vec<u32>,
38 logical_col_to_physical: Vec<u16>,
39 physical_col_to_logical: Vec<u16>,
40 column_index_disabled: bool,
41 #[cfg(debug_assertions)]
43 debug_indexed_column_valid: Vec<bool>,
44 num_dense_columns: usize,
45}
46
47const WORD_WIDTH: usize = 64;
48
49impl SparseBinaryMatrix {
50 #[cfg(debug_assertions)]
51 fn verify(&self) {
52 if self.column_index_disabled {
53 return;
54 }
55 let columns = self.sparse_columnar_values.as_ref().unwrap();
56 for row in 0..self.height {
57 for (col, value) in self.sparse_elements[row].keys_values() {
58 if value != Octet::zero() {
59 debug_assert!(columns.get(col as u16).contains(&(row as u32)));
60 }
61 }
62 }
63 }
64
65 fn logical_col_to_dense_col(&self, col: usize) -> usize {
67 assert!(col >= self.width - self.num_dense_columns);
68 col - (self.width - self.num_dense_columns)
69 }
70
71 fn bit_position(&self, row: usize, col: usize) -> (usize, usize) {
73 return (
74 row * self.row_word_width() + self.word_offset(col),
75 (self.left_padding_bits() + col) % WORD_WIDTH,
76 );
77 }
78
79 fn row_word_width(&self) -> usize {
81 self.num_dense_columns.div_ceil(WORD_WIDTH)
82 }
83
84 fn left_padding_bits(&self) -> usize {
86 (WORD_WIDTH - (self.num_dense_columns % WORD_WIDTH)) % WORD_WIDTH
87 }
88
89 fn word_offset(&self, bit: usize) -> usize {
91 (self.left_padding_bits() + bit) / WORD_WIDTH
92 }
93
94 fn select_mask(bit: usize) -> u64 {
96 1u64 << (bit as u64)
97 }
98
99 fn clear_bit(word: &mut u64, bit: usize) {
100 *word &= !SparseBinaryMatrix::select_mask(bit);
101 }
102
103 fn set_bit(word: &mut u64, bit: usize) {
104 *word |= SparseBinaryMatrix::select_mask(bit);
105 }
106}
107
108impl BinaryMatrix for SparseBinaryMatrix {
109 fn new(height: usize, width: usize, trailing_dense_column_hint: usize) -> SparseBinaryMatrix {
110 debug_assert!(height < 16777216);
111 debug_assert!(width < 65536);
113 let mut col_mapping = vec![0; width];
114 let elements = vec![SparseBinaryVec::with_capacity(10); height];
115 let mut row_mapping = vec![0; height];
116 #[allow(clippy::needless_range_loop)]
117 for i in 0..height {
118 row_mapping[i] = i as u32;
119 }
120 #[allow(clippy::needless_range_loop)]
121 for i in 0..width {
122 col_mapping[i] = i as u16;
123 }
124 let dense_elements = if trailing_dense_column_hint > 0 {
125 vec![0; height * ((trailing_dense_column_hint - 1) / WORD_WIDTH + 1)]
126 } else {
127 vec![]
128 };
129 SparseBinaryMatrix {
130 height,
131 width,
132 sparse_elements: elements,
133 dense_elements,
134 sparse_columnar_values: None,
135 logical_row_to_physical: row_mapping.clone(),
136 physical_row_to_logical: row_mapping,
137 logical_col_to_physical: col_mapping.clone(),
138 physical_col_to_logical: col_mapping,
139 column_index_disabled: true,
140 num_dense_columns: trailing_dense_column_hint,
141 #[cfg(debug_assertions)]
142 debug_indexed_column_valid: vec![true; width],
143 }
144 }
145
146 fn set(&mut self, i: usize, j: usize, value: Octet) {
147 let physical_i = self.logical_row_to_physical[i] as usize;
148 let physical_j = self.logical_col_to_physical[j] as usize;
149 if self.width - j <= self.num_dense_columns {
150 let (word, bit) = self.bit_position(physical_i, self.logical_col_to_dense_col(j));
151 if value == Octet::zero() {
152 SparseBinaryMatrix::clear_bit(&mut self.dense_elements[word], bit);
153 } else {
154 SparseBinaryMatrix::set_bit(&mut self.dense_elements[word], bit);
155 }
156 } else {
157 self.sparse_elements[physical_i].insert(physical_j, value);
158 assert!(self.column_index_disabled);
159 }
160 }
161
162 fn height(&self) -> usize {
163 self.height
164 }
165
166 fn width(&self) -> usize {
167 self.width
168 }
169
170 fn count_ones(&self, row: usize, start_col: usize, end_col: usize) -> usize {
171 if end_col > self.width - self.num_dense_columns {
172 unimplemented!(
173 "It was assumed that this wouldn't be needed, because the method would only be called on the V section of matrix A"
174 );
175 }
176 let mut ones = 0;
177 let physical_row = self.logical_row_to_physical[row] as usize;
178 for (physical_col, value) in self.sparse_elements[physical_row].keys_values() {
179 let col = self.physical_col_to_logical[physical_col] as usize;
180 if col >= start_col && col < end_col && value == Octet::one() {
181 ones += 1;
182 }
183 }
184 return ones;
185 }
186
187 fn get_sub_row_as_octets(&self, row: usize, start_col: usize) -> BinaryOctetVec {
188 let first_dense_column = self.width - self.num_dense_columns;
189 assert_eq!(start_col, first_dense_column);
190 let physical_row = self.logical_row_to_physical[row] as usize;
194 let (first_word, _) =
195 self.bit_position(physical_row, self.logical_col_to_dense_col(start_col));
196 let last_word = first_word + self.row_word_width();
197
198 BinaryOctetVec::new(
199 self.dense_elements[first_word..last_word].to_vec(),
200 self.num_dense_columns,
201 )
202 }
203
204 fn query_non_zero_columns(&self, row: usize, start_col: usize) -> Vec<usize> {
205 let mut result = Vec::with_capacity(self.num_dense_columns);
206 self.query_non_zero_columns_into(row, start_col, &mut result);
207 result
208 }
209
210 fn query_non_zero_columns_into(&self, row: usize, start_col: usize, out: &mut Vec<usize>) {
211 assert_eq!(start_col, self.width - self.num_dense_columns);
215 out.clear();
216 out.reserve(self.num_dense_columns);
217 let physical_row = self.logical_row_to_physical[row] as usize;
218 let (mut word, bit) =
219 self.bit_position(physical_row, self.logical_col_to_dense_col(start_col));
220 let mut col = start_col;
221 let mut block = self.dense_elements[word];
225 while block.trailing_zeros() < WORD_WIDTH as u32 {
226 out.push(col + block.trailing_zeros() as usize - bit);
227 block &= !(SparseBinaryMatrix::select_mask(block.trailing_zeros() as usize));
228 }
229 col += WORD_WIDTH - bit;
230 word += 1;
231
232 while col < self.width() {
233 let mut block = self.dense_elements[word];
234 while block.trailing_zeros() < WORD_WIDTH as u32 {
236 out.push(col + block.trailing_zeros() as usize);
237 block &= !(SparseBinaryMatrix::select_mask(block.trailing_zeros() as usize));
238 }
239 col += WORD_WIDTH;
240 word += 1;
241 }
242 }
243
244 fn get(&self, i: usize, j: usize) -> Octet {
245 let physical_i = self.logical_row_to_physical[i] as usize;
246 let physical_j = self.logical_col_to_physical[j] as usize;
247 if self.width - j <= self.num_dense_columns {
248 let (word, bit) = self.bit_position(physical_i, self.logical_col_to_dense_col(j));
249 if self.dense_elements[word] & SparseBinaryMatrix::select_mask(bit) == 0 {
250 return Octet::zero();
251 } else {
252 return Octet::one();
253 }
254 } else {
255 return self.sparse_elements[physical_i]
256 .get(physical_j)
257 .unwrap_or_else(Octet::zero);
258 }
259 }
260
261 fn get_row_iter(&self, row: usize, start_col: usize, end_col: usize) -> OctetIter<'_> {
262 if end_col > self.width - self.num_dense_columns {
263 unimplemented!(
264 "It was assumed that this wouldn't be needed, because the method would only be called on the V section of matrix A"
265 );
266 }
267 let physical_row = self.logical_row_to_physical[row] as usize;
268 let sparse_elements = &self.sparse_elements[physical_row];
269 OctetIter::new_sparse(
270 start_col,
271 end_col,
272 sparse_elements,
273 &self.physical_col_to_logical,
274 )
275 }
276
277 fn get_ones_in_column(&self, col: usize, start_row: usize, end_row: usize) -> Vec<u32> {
278 let mut rows = Vec::with_capacity(end_row.saturating_sub(start_row));
279 self.get_ones_in_column_into(col, start_row, end_row, &mut rows);
280 rows
281 }
282
283 fn get_ones_in_column_into(
284 &self,
285 col: usize,
286 start_row: usize,
287 end_row: usize,
288 out: &mut Vec<u32>,
289 ) {
290 assert!(!self.column_index_disabled);
291 #[cfg(debug_assertions)]
292 debug_assert!(self.debug_indexed_column_valid[col]);
293 let physical_col = self.logical_col_to_physical[col];
294 out.clear();
295 out.reserve(end_row.saturating_sub(start_row));
296 for physical_row in self
297 .sparse_columnar_values
298 .as_ref()
299 .unwrap()
300 .get(physical_col)
301 {
302 let logical_row = self.physical_row_to_logical[*physical_row as usize];
303 if start_row <= logical_row as usize && logical_row < end_row as u32 {
304 out.push(logical_row);
305 }
306 }
307 }
308
309 fn swap_rows(&mut self, i: usize, j: usize) {
310 let physical_i = self.logical_row_to_physical[i] as usize;
311 let physical_j = self.logical_row_to_physical[j] as usize;
312 self.logical_row_to_physical.swap(i, j);
313 self.physical_row_to_logical.swap(physical_i, physical_j);
314 }
315
316 fn swap_columns(&mut self, i: usize, j: usize, _: usize) {
317 if j >= self.width - self.num_dense_columns {
318 unimplemented!(
319 "It was assumed that this wouldn't be needed, because the method would only be called on the V section of matrix A"
320 );
321 }
322
323 #[cfg(debug_assertions)]
324 self.debug_indexed_column_valid.swap(i, j);
325
326 let physical_i = self.logical_col_to_physical[i] as usize;
327 let physical_j = self.logical_col_to_physical[j] as usize;
328 self.logical_col_to_physical.swap(i, j);
329 self.physical_col_to_logical.swap(physical_i, physical_j);
330 }
331
332 fn enable_column_access_acceleration(&mut self) {
333 self.column_index_disabled = false;
334 let mut builder = ImmutableListMapBuilder::new(self.height);
335 for (physical_row, elements) in self.sparse_elements.iter().enumerate() {
336 for (physical_col, _) in elements.keys_values() {
337 builder.add(physical_col as u16, physical_row as u32);
338 }
339 }
340 self.sparse_columnar_values = Some(builder.build());
341 }
342
343 fn disable_column_access_acceleration(&mut self) {
344 self.column_index_disabled = true;
345 self.sparse_columnar_values = None;
346 }
347
348 fn hint_column_dense_and_frozen(&mut self, i: usize) {
349 assert_eq!(
350 self.width - self.num_dense_columns - 1,
351 i,
352 "Can only freeze the last sparse column"
353 );
354 assert!(!self.column_index_disabled);
355 self.num_dense_columns += 1;
356 let (last_word, _) = self.bit_position(self.height - 1, self.num_dense_columns - 1);
357 if last_word >= self.dense_elements.len() {
359 let mut src = self.dense_elements.len();
361 self.dense_elements.extend(vec![0; self.height]);
362 let mut dest = self.dense_elements.len();
363 while src > 0 {
365 src -= 1;
366 dest -= 1;
367 self.dense_elements[dest] = self.dense_elements[src];
368 if dest % self.row_word_width() == 1 {
369 dest -= 1;
370 self.dense_elements[dest] = 0;
371 }
372 }
373 assert_eq!(src, 0);
374 assert_eq!(dest, 0);
375 }
376 let physical_i = self.logical_col_to_physical[i] as usize;
377 for maybe_present_in_row in self
378 .sparse_columnar_values
379 .as_ref()
380 .unwrap()
381 .get(physical_i as u16)
382 {
383 let physical_row = *maybe_present_in_row as usize;
384 if let Some(value) = self.sparse_elements[physical_row].remove(physical_i) {
385 let (word, bit) = self.bit_position(physical_row, 0);
386 if value == Octet::zero() {
387 SparseBinaryMatrix::clear_bit(&mut self.dense_elements[word], bit);
388 } else {
389 SparseBinaryMatrix::set_bit(&mut self.dense_elements[word], bit);
390 }
391 }
392 }
393 }
394
395 fn add_assign_rows(&mut self, dest: usize, src: usize, start_col: usize) {
396 assert_ne!(dest, src);
397 assert!(
398 start_col == 0 || start_col == self.width - self.num_dense_columns,
399 "start_col must be zero or at the beginning of the U matrix"
400 );
401 let physical_dest = self.logical_row_to_physical[dest] as usize;
402 let physical_src = self.logical_row_to_physical[src] as usize;
403 if self.num_dense_columns > 0 {
405 let (dest_word, _) = self.bit_position(physical_dest, 0);
406 let (src_word, _) = self.bit_position(physical_src, 0);
407 for word in 0..self.row_word_width() {
408 self.dense_elements[dest_word + word] ^= self.dense_elements[src_word + word];
409 }
410 }
411
412 if start_col == 0 {
413 let (dest_row, temp_row) =
415 get_both_indices(&mut self.sparse_elements, physical_dest, physical_src);
416 assert!(self.column_index_disabled || temp_row.len() == 1);
419
420 let column_added = dest_row.add_assign(temp_row);
421 assert!(self.column_index_disabled || !column_added);
424
425 #[cfg(debug_assertions)]
426 {
427 if !self.column_index_disabled {
428 let col = self.physical_col_to_logical[temp_row.get_by_raw_index(0).0];
429 self.debug_indexed_column_valid[col as usize] = false;
430 }
431 }
432 }
433
434 #[cfg(debug_assertions)]
435 self.verify();
436 }
437
438 fn resize(&mut self, new_height: usize, new_width: usize) {
439 assert!(new_height <= self.height);
440 let mut columns_to_remove = self.width - new_width;
442 assert!(columns_to_remove == 0 || columns_to_remove >= self.num_dense_columns);
443 if !self.column_index_disabled {
444 unimplemented!(
445 "Resize should only be used in phase 2, after column indexing is no longer needed"
446 );
447 }
448 let mut new_sparse = vec![None; new_height];
449 for i in (0..self.sparse_elements.len()).rev() {
450 let logical_row = self.physical_row_to_logical[i] as usize;
451 let sparse = self.sparse_elements.pop();
452 if logical_row < new_height {
453 new_sparse[logical_row] = sparse;
454 }
455 }
456
457 if columns_to_remove == 0 && self.num_dense_columns > 0 {
458 let mut new_dense = vec![0; new_height * self.row_word_width()];
460 for logical_row in 0..new_height {
461 let physical_row = self.logical_row_to_physical[logical_row] as usize;
462 for word in 0..self.row_word_width() {
463 new_dense[logical_row * self.row_word_width() + word] =
464 self.dense_elements[physical_row * self.row_word_width() + word];
465 }
466 }
467 self.dense_elements = new_dense;
468 } else {
469 columns_to_remove -= self.num_dense_columns;
470 self.dense_elements.clear();
471 self.num_dense_columns = 0;
472 }
473
474 self.logical_row_to_physical.truncate(new_height);
475 self.physical_row_to_logical.truncate(new_height);
476 for i in 0..new_height {
477 self.logical_row_to_physical[i] = i as u32;
478 self.physical_row_to_logical[i] = i as u32;
479 }
480 for row in new_sparse.drain(0..new_height) {
481 self.sparse_elements.push(row.unwrap());
482 }
483
484 if columns_to_remove > 0 {
486 let physical_to_logical = &self.physical_col_to_logical;
487 for row in 0..self.sparse_elements.len() {
488 self.sparse_elements[row]
489 .retain(|(col, _)| physical_to_logical[*col] < new_width as u16);
490 }
491 }
492
493 self.height = new_height;
494 self.width = new_width;
495
496 #[cfg(debug_assertions)]
497 self.verify();
498 }
499
500 fn size_in_bytes(&self) -> usize {
501 let mut bytes = size_of::<Self>();
502 for x in self.sparse_elements.iter() {
503 bytes += x.size_in_bytes();
504 }
505 bytes += size_of::<u64>() * self.dense_elements.len();
506 if let Some(ref columns) = self.sparse_columnar_values {
507 bytes += columns.size_in_bytes();
508 }
509 bytes += size_of::<u32>() * self.logical_row_to_physical.len();
510 bytes += size_of::<u32>() * self.physical_row_to_logical.len();
511 bytes += size_of::<u16>() * self.logical_col_to_physical.len();
512 bytes += size_of::<u16>() * self.physical_col_to_logical.len();
513 #[cfg(debug_assertions)]
514 {
515 bytes += size_of::<bool>() * self.debug_indexed_column_valid.len();
516 }
517
518 bytes
519 }
520}
521
522#[cfg(test)]
523mod tests {
524 use crate::systematic_constants::{MAX_SOURCE_SYMBOLS_PER_BLOCK, num_intermediate_symbols};
525
526 #[test]
527 fn check_max_width_optimization() {
528 assert!(num_intermediate_symbols(MAX_SOURCE_SYMBOLS_PER_BLOCK) < 65536);
531 }
532}