1use core::borrow::BorrowMut;
2
3use p3_maybe_rayon::PARALLEL_ENABLED;
4use p3_maybe_rayon::prelude::*;
5use p3_util::{log2_strict_usize, reverse_bits_len, reverse_slice_index_bits};
6use tracing::instrument;
7
8use crate::Matrix;
9use crate::dense::{DenseMatrix, DenseStorage, RowMajorMatrix};
10
11#[instrument(level = "debug", skip_all)]
37pub fn reverse_matrix_index_bits<'a, F, S>(mat: &mut DenseMatrix<F, S>)
38where
39 F: Clone + Send + Sync + 'a,
40 S: DenseStorage<F> + BorrowMut<[F]>,
41{
42 let w = mat.width();
43 let h = mat.height();
44 let log_h = log2_strict_usize(h);
45 let values = mat.values.borrow_mut();
46 let total_bytes = core::mem::size_of_val(values);
47
48 let use_slice = w == 1 && core::mem::size_of::<F>() <= 8;
52 #[cfg(target_arch = "aarch64")]
53 let use_slice = use_slice && (total_bytes <= 64 * 1024 || current_num_threads() == 1);
54 if use_slice {
55 reverse_slice_index_bits(values);
56 return;
57 }
58
59 let values = values.as_mut_ptr() as usize;
60
61 let swap = |i, use_outline| {
65 let values = values as *mut F;
66 let j = reverse_bits_len(i, log_h);
67 if i < j {
68 unsafe { swap_rows_raw(values, w, i, j, use_outline) };
69 }
70 };
71 if total_bytes <= 32 * 1024 {
73 (0..h).for_each(|i| swap(i, true));
74 } else {
75 const CHUNK_BYTES: usize = 64 * 1024;
78 let rows_per_chunk = CHUNK_BYTES.div_ceil(w * core::mem::size_of::<F>());
79 (0..h.div_ceil(rows_per_chunk))
80 .into_par_iter()
81 .for_each(|chunk| {
82 let start = chunk * rows_per_chunk;
83 let end = start + rows_per_chunk.min(h - start);
84 for i in start..end {
86 swap(i, !PARALLEL_ENABLED);
87 }
88 });
89 }
90}
91
92pub fn swap_rows<F: Clone + Send + Sync>(mat: &mut RowMajorMatrix<F>, i: usize, j: usize) {
102 let w = mat.width();
103 let (upper, lower) = mat.values.split_at_mut(j * w);
104 let row_i = &mut upper[i * w..(i + 1) * w];
105 let row_j = &mut lower[..w];
106 row_i.swap_with_slice(row_j);
107}
108
109unsafe fn swap_rows_raw<F>(mat: *mut F, w: usize, i: usize, j: usize, use_outline: bool) {
124 unsafe {
125 let row_i = core::slice::from_raw_parts_mut(mat.add(i * w), w);
126 let row_j = core::slice::from_raw_parts_mut(mat.add(j * w), w);
127 if use_outline && core::mem::size_of_val(row_i) >= 64 {
129 swap_row_slices(row_i, row_j);
130 } else {
131 row_i.swap_with_slice(row_j);
132 }
133 }
134}
135
136#[inline(never)]
138fn swap_row_slices<F>(row_i: &mut [F], row_j: &mut [F]) {
139 row_i.swap_with_slice(row_j);
140}
141
142#[cfg(test)]
143mod tests {
144 use alloc::vec;
145
146 use super::*;
147 use crate::dense::RowMajorMatrix;
148
149 #[test]
150 fn test_swap_rows_basic() {
151 let mut matrix = RowMajorMatrix::new(
152 vec![
153 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, ],
158 3,
159 );
160
161 swap_rows(&mut matrix, 0, 2);
163
164 assert_eq!(
165 matrix.values,
166 vec![
167 7, 8, 9, 4, 5, 6, 1, 2, 3, 10, 11, 12, ]
172 );
173 }
174
175 #[test]
176 fn test_swap_rows_raw_basic() {
177 let mut matrix = RowMajorMatrix::new(
178 vec![
179 1, 2, 3, 4, 5, 6, 7, 8, 9, ],
183 3,
184 );
185 let ptr = matrix.values.as_mut_ptr();
186 unsafe {
187 swap_rows_raw(ptr, matrix.width(), 0, 2, false);
188 }
189
190 assert_eq!(
191 matrix.values,
192 vec![
193 7, 8, 9, 4, 5, 6, 1, 2, 3, ]
197 );
198 }
199
200 #[test]
201 fn test_reverse_matrix_index_bits_pow2_height() {
202 let mut matrix = RowMajorMatrix::new(
203 vec![
204 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, ],
213 2,
214 );
215
216 reverse_matrix_index_bits(&mut matrix);
217
218 assert_eq!(
219 matrix.values,
220 vec![
221 0, 1, 8, 9, 4, 5, 12, 13, 2, 3, 10, 11, 6, 7, 14, 15, ]
230 );
231 }
232
233 #[test]
234 fn test_reverse_matrix_index_bits_strings() {
235 use alloc::string::ToString;
236 use alloc::vec::Vec;
237
238 for width in [1, 3, 17] {
239 for log_h in 0..=14 {
240 let height = 1 << log_h;
241 let original: Vec<_> = (0..height * width).map(|i| i.to_string()).collect();
242 let expected: Vec<_> = (0..height)
243 .flat_map(|row| {
244 let start = reverse_bits_len(row, log_h) * width;
245 original[start..start + width].iter().cloned()
246 })
247 .collect();
248 let mut matrix = RowMajorMatrix::new(original.clone(), width);
249 reverse_matrix_index_bits(&mut matrix);
250 assert_eq!(matrix.values, expected, "width={width}, log_h={log_h}");
251 reverse_matrix_index_bits(&mut matrix);
252 assert_eq!(
253 matrix.values, original,
254 "involution: width={width}, log_h={log_h}"
255 );
256 }
257 }
258 }
259
260 #[test]
261 fn test_reverse_matrix_index_bits_preserves_owners_without_cloning() {
262 use alloc::vec::Vec;
263 use core::sync::atomic::{AtomicUsize, Ordering};
264
265 struct Owner<'a>(&'a AtomicUsize);
267 impl Clone for Owner<'_> {
268 fn clone(&self) -> Self {
269 panic!("bit reversal must not clone elements");
270 }
271 }
272 impl Drop for Owner<'_> {
273 fn drop(&mut self) {
274 self.0.fetch_add(1, Ordering::Relaxed);
275 }
276 }
277
278 for width in [1, 3, 17] {
279 for log_h in 0..=15 {
280 let height = 1 << log_h;
281 let drops: Vec<_> = (0..height * width).map(|_| AtomicUsize::new(0)).collect();
282 let mut matrix = RowMajorMatrix::new(drops.iter().map(Owner).collect(), width);
283 reverse_matrix_index_bits(&mut matrix);
284 for (i, value) in matrix.values.iter().enumerate() {
285 let source = reverse_bits_len(i / width, log_h) * width + i % width;
286 assert!(core::ptr::eq(value.0, &drops[source]));
287 }
288 reverse_matrix_index_bits(&mut matrix);
289 for (value, count) in matrix.values.iter().zip(&drops) {
290 assert!(core::ptr::eq(value.0, count));
291 assert_eq!(count.load(Ordering::Relaxed), 0);
292 }
293 drop(matrix);
294 assert!(drops.iter().all(|count| count.load(Ordering::Relaxed) == 1));
295 }
296 }
297 }
298
299 #[test]
300 fn test_reverse_matrix_index_bits_work_unit_boundaries() {
301 use alloc::vec::Vec;
302
303 for (width, height) in [(3, 8192), (16_383, 8), (16_384, 8), (16_385, 8)] {
305 let original: Vec<u32> = (0..width * height).map(|i| i as u32).collect();
306 let mut matrix = RowMajorMatrix::new(original.clone(), width);
307
308 reverse_matrix_index_bits(&mut matrix.as_view_mut());
309 for (row, values) in matrix.values.chunks_exact(width).enumerate() {
310 let source = row.reverse_bits() >> (usize::BITS - height.ilog2());
311 assert_eq!(
312 values,
313 &original[source * width..(source + 1) * width],
314 "width={width}, height={height}, row={row}"
315 );
316 }
317 reverse_matrix_index_bits(&mut matrix);
318 assert_eq!(matrix.values, original, "width={width}, height={height}");
319 }
320 }
321
322 #[test]
323 fn test_reverse_matrix_index_bits_zero_sized_elements() {
324 let mut matrix = RowMajorMatrix::new(vec![(); 3 * 8192], 3);
325 reverse_matrix_index_bits(&mut matrix);
326 assert_eq!(matrix.width(), 3);
327 assert_eq!(matrix.height(), 8192);
328 assert_eq!(matrix.values.len(), 3 * 8192);
329 }
330
331 #[test]
332 fn test_reverse_matrix_index_bits_height_1() {
333 let mut matrix = RowMajorMatrix::new(
334 vec![
335 42, 43, ],
337 2,
338 );
339
340 reverse_matrix_index_bits(&mut matrix);
342
343 assert_eq!(
344 matrix.values,
345 vec![
346 42, 43, ]
348 );
349 }
350
351 #[test]
352 #[should_panic]
353 fn test_reverse_matrix_index_bits_non_power_of_two_should_panic() {
354 let mut matrix = RowMajorMatrix::new(
356 vec![
357 1, 2, 3, 4, 5, 6, ],
361 2,
362 );
363
364 reverse_matrix_index_bits(&mut matrix);
365 }
366}