p3_util/lib.rs
1//! Various simple utilities.
2
3#![no_std]
4
5extern crate alloc;
6
7use alloc::slice;
8use alloc::string::String;
9use alloc::vec::Vec;
10use core::any::type_name;
11use core::hint::assert_unchecked;
12use core::mem::{ManuallyDrop, MaybeUninit};
13use core::{iter, mem};
14
15use crate::transpose::transpose_in_place_square;
16
17pub mod array_serialization;
18pub mod linear_map;
19pub mod transpose;
20pub mod zip_eq;
21
22/// Computes `ceil(log_2(n))`.
23#[must_use]
24pub const fn log2_ceil_usize(n: usize) -> usize {
25 (usize::BITS - n.saturating_sub(1).leading_zeros()) as usize
26}
27
28/// Computes `floor(log_2(n))`.
29///
30/// Returns `0` for `n == 0` (matching `log2_ceil_usize(0) == 0`); `floor(log2(0))`
31/// is undefined mathematically and the saturating behaviour is the convention used
32/// elsewhere in the workspace.
33#[must_use]
34pub const fn log2_floor_usize(n: usize) -> usize {
35 if n == 0 {
36 return 0;
37 }
38 (usize::BITS - 1 - n.leading_zeros()) as usize
39}
40
41#[must_use]
42pub const fn log2_ceil_u64(n: u64) -> u64 {
43 (u64::BITS - n.saturating_sub(1).leading_zeros()) as u64
44}
45
46/// Returns `2^log_degree` if it can be represented by `usize`.
47#[must_use]
48pub const fn checked_pow2(log_degree: usize) -> Option<usize> {
49 if log_degree < usize::BITS as usize {
50 Some(1usize << log_degree)
51 } else {
52 None
53 }
54}
55
56/// Adds two log-sizes and computes the resulting power of two.
57///
58/// Returns:
59/// - `(a + b, 2^(a + b))` when the sum fits in a `usize` shift,
60/// - `None` if the addition overflows or the resulting power exceeds the representable range.
61#[must_use]
62pub const fn checked_log_size_sum(a: usize, b: usize) -> Option<(usize, usize)> {
63 match a.checked_add(b) {
64 Some(sum) => match checked_pow2(sum) {
65 Some(size) => Some((sum, size)),
66 None => None,
67 },
68 None => None,
69 }
70}
71
72/// Computes `log_2(n)`
73///
74/// # Panics
75/// Panics if `n` is not a power of two.
76#[must_use]
77#[inline]
78pub const fn log2_strict_usize(n: usize) -> usize {
79 let res = n.trailing_zeros();
80 assert!(n.wrapping_shr(res) == 1, "Not a power of two");
81 // Tell the optimizer about the semantics of `log2_strict`. i.e. it can replace `n` with
82 // `1 << res` and vice versa.
83 unsafe {
84 assert_unchecked(n == 1 << res);
85 }
86 res as usize
87}
88
89/// Precomputed table of all powers of 3 that fit in a `u64`.
90///
91/// The maximum power is `3^40 = 12_157_665_459_056_928_801`.
92///
93/// We use `u64` instead of `usize` so the table compiles safely on 32-bit targets,
94/// where `3^40` would overflow a 32-bit `usize`.
95const POWERS_OF_3: [u64; 41] = {
96 // Start with 3^0 = 1.
97 let mut table = [0u64; 41];
98 table[0] = 1;
99
100 // Fill iteratively: each entry is 3 times the previous one.
101 let mut i = 1;
102 while i < 41 {
103 table[i] = table[i - 1] * 3;
104 i += 1;
105 }
106 table
107};
108
109/// Maps a bit-position (i.e. `floor(log2(n))`) to the corresponding base-3 exponent.
110///
111/// Because `3^k` grows faster than `2^k`, every power of 3 has a unique highest set
112/// bit position. This lets us use `leading_zeros()` to jump straight to the answer
113/// in O(1) without any loop or binary search.
114///
115/// Entries that don't correspond to any power of 3 are unused (left as 0).
116const LOG2_TO_EXP: [u8; 64] = {
117 // Initialize every slot to 0.
118 let mut table = [0u8; 64];
119
120 // For each power of 3, record which log2 bucket it falls into.
121 let mut i = 0;
122 while i < 41 {
123 // Compute floor(log2(3^i)) via the highest set bit.
124 let log2 = (u64::BITS - 1 - POWERS_OF_3[i].leading_zeros()) as usize;
125
126 // Store the exponent i at the corresponding bit-position.
127 table[log2] = i as u8;
128 i += 1;
129 }
130 table
131};
132
133/// Computes the strict base-3 logarithm of `n`.
134///
135/// Returns `k` such that `3^k == n`. Panics if `n` is not a power of 3.
136///
137/// This is the base-3 analogue of [`log2_strict_usize`].
138///
139/// # Arguments
140///
141/// * `n` - A positive integer that must be a power of 3 (i.e., 1, 3, 9, 27, 81, ...).
142///
143/// # Returns
144///
145/// The exponent `k` where `3^k == n`.
146///
147/// # Panics
148///
149/// Panics if:
150/// - `n` is zero
151/// - `n` is not a power of 3
152#[must_use]
153#[inline]
154pub const fn log3_strict_usize(n: usize) -> usize {
155 // Zero has no logarithm - check explicitly for a clear error message.
156 assert!(n != 0, "log3_strict_usize: input must be non-zero");
157
158 // Instantly find the candidate exponent via the highest set bit.
159 //
160 // Because every power of 3 occupies a unique log2 bucket, this single
161 // lookup gives us the answer in O(1) with zero branches.
162 let log2 = (usize::BITS - 1 - n.leading_zeros()) as usize;
163 let res = LOG2_TO_EXP[log2] as usize;
164
165 // Verify the result: catches non-powers of 3 in a single O(1) check.
166 assert!(
167 POWERS_OF_3[res] as usize == n,
168 "log3_strict_usize: input is not a power of 3"
169 );
170
171 res
172}
173
174/// Returns `[0, ..., N - 1]`.
175#[must_use]
176pub const fn indices_arr<const N: usize>() -> [usize; N] {
177 let mut indices_arr = [0; N];
178 let mut i = 0;
179 while i < N {
180 indices_arr[i] = i;
181 i += 1;
182 }
183 indices_arr
184}
185
186/// Statically asserts that `T` implements [`Clone`].
187pub const fn assert_clone<T: Clone>() {}
188
189/// Statically asserts that `T` implements [`Send`].
190pub const fn assert_send<T: Send>() {}
191
192/// Statically asserts that `T` implements [`Sync`].
193pub const fn assert_sync<T: Sync>() {}
194
195#[inline]
196pub const fn reverse_bits(x: usize, n: usize) -> usize {
197 // Assert that n is a power of 2
198 debug_assert!(n.is_power_of_two());
199 reverse_bits_len(x, n.trailing_zeros() as usize)
200}
201
202#[inline]
203pub const fn reverse_bits_len(x: usize, bit_len: usize) -> usize {
204 // A `bit_len` wider than the word would underflow the shift below.
205 // That yields a wrong, non-panicking permutation in release, so reject it up front.
206 debug_assert!(bit_len <= usize::BITS as usize);
207 // NB: The only reason we need overflowing_shr() here as opposed
208 // to plain '>>' is to accommodate the case n == num_bits == 0,
209 // which would become `0 >> 64`. Rust thinks that any shift of 64
210 // bits causes overflow, even when the argument is zero.
211 x.reverse_bits()
212 .overflowing_shr(usize::BITS - bit_len as u32)
213 .0
214}
215
216// Lookup table of 6-bit reverses.
217// NB: 2^6=64 bytes is a cache line. A smaller table wastes cache space.
218#[cfg(not(all(target_arch = "aarch64", target_feature = "neon")))]
219#[rustfmt::skip]
220const BIT_REVERSE_6BIT: &[u8] = &[
221 0o00, 0o40, 0o20, 0o60, 0o10, 0o50, 0o30, 0o70,
222 0o04, 0o44, 0o24, 0o64, 0o14, 0o54, 0o34, 0o74,
223 0o02, 0o42, 0o22, 0o62, 0o12, 0o52, 0o32, 0o72,
224 0o06, 0o46, 0o26, 0o66, 0o16, 0o56, 0o36, 0o76,
225 0o01, 0o41, 0o21, 0o61, 0o11, 0o51, 0o31, 0o71,
226 0o05, 0o45, 0o25, 0o65, 0o15, 0o55, 0o35, 0o75,
227 0o03, 0o43, 0o23, 0o63, 0o13, 0o53, 0o33, 0o73,
228 0o07, 0o47, 0o27, 0o67, 0o17, 0o57, 0o37, 0o77,
229];
230
231const BIG_T_SIZE: usize = 1 << 14;
232const SMALL_ARR_SIZE: usize = 1 << 16;
233const _: () = assert!(SMALL_ARR_SIZE >= 4 * BIG_T_SIZE);
234
235/// Permutes `arr` such that each index is mapped to its reverse in binary.
236///
237/// This permutation swaps elements without cloning or dropping them.
238///
239/// If the whole array fits in fast cache, then the trivial algorithm is cache friendly. Also, if
240/// `T` is really big, then the trivial algorithm is cache-friendly, no matter the size of the array.
241pub fn reverse_slice_index_bits<F>(vals: &mut [F])
242where
243 F: Send + Sync,
244{
245 let n = vals.len();
246 if n == 0 {
247 return;
248 }
249 let log_n = log2_strict_usize(n);
250
251 // If the whole array fits in fast cache, then the trivial algorithm is cache friendly. Also, if
252 // `T` is really big, then the trivial algorithm is cache-friendly, no matter the size of the array.
253 if core::mem::size_of::<F>() << log_n <= SMALL_ARR_SIZE
254 || core::mem::size_of::<F>() >= BIG_T_SIZE
255 {
256 reverse_slice_index_bits_small(vals, log_n);
257 } else {
258 debug_assert!(n >= 4); // By our choice of `BIG_T_SIZE` and `SMALL_ARR_SIZE`.
259
260 // Algorithm:
261 //
262 // Treat `arr` as a `sqrt(n)` by `sqrt(n)` row-major matrix. (Assume for now that `lb_n` is
263 // even, i.e., `n` is a square number.) To perform bit-order reversal we:
264 // 1. Bit-reverse the order of the rows. (They are contiguous in memory, so this is
265 // basically a series of large `memcpy`s.)
266 // 2. Transpose the matrix.
267 // 3. Bit-reverse the order of the rows.
268 //
269 // This is equivalent to, for every index `0 <= i < n`:
270 // 1. bit-reversing `i[lb_n / 2..lb_n]`,
271 // 2. swapping `i[0..lb_n / 2]` and `i[lb_n / 2..lb_n]`,
272 // 3. bit-reversing `i[lb_n / 2..lb_n]`.
273 //
274 // If `lb_n` is odd, i.e., `n` is not a square number, then the above procedure requires
275 // slight modification. At steps 1 and 3 we bit-reverse bits `ceil(lb_n / 2)..lb_n`, of the
276 // index (shuffling `floor(lb_n / 2)` chunks of length `ceil(lb_n / 2)`). At step 2, we
277 // perform _two_ transposes. We treat `arr` as two matrices, one where the middle bit of the
278 // index is `0` and another, where the middle bit is `1`; we transpose each individually.
279
280 let lb_num_chunks = log_n >> 1;
281 let lb_chunk_size = log_n - lb_num_chunks;
282 unsafe {
283 reverse_slice_index_bits_chunks(vals, lb_num_chunks, lb_chunk_size);
284 transpose_in_place_square(vals, lb_chunk_size, lb_num_chunks, 0);
285 if lb_num_chunks != lb_chunk_size {
286 // `arr` cannot be interpreted as a square matrix. We instead interpret it as a
287 // `1 << lb_num_chunks` by `2` by `1 << lb_num_chunks` tensor, in row-major order.
288 // The above transpose acted on `tensor[..., 0, ...]` (all indices with middle bit
289 // `0`). We still need to transpose `tensor[..., 1, ...]`. To do so, we advance
290 // arr by `1 << lb_num_chunks` effectively, adding that to every index.
291 let vals_with_offset = &mut vals[1 << lb_num_chunks..];
292 transpose_in_place_square(vals_with_offset, lb_chunk_size, lb_num_chunks, 0);
293 }
294 reverse_slice_index_bits_chunks(vals, lb_num_chunks, lb_chunk_size);
295 }
296 }
297}
298
299// Both functions below are semantically equivalent to:
300// for i in 0..n {
301// result.push(arr[reverse_bits(i, n_power)]);
302// }
303// where reverse_bits(i, n_power) computes the n_power-bit reverse. The complications are there
304// to guide the compiler to generate optimal assembly.
305
306#[cfg(not(all(target_arch = "aarch64", target_feature = "neon")))]
307fn reverse_slice_index_bits_small<F>(vals: &mut [F], lb_n: usize) {
308 if lb_n <= 6 {
309 // BIT_REVERSE_6BIT holds 6-bit reverses. This shift makes them lb_n-bit reverses.
310 let dst_shr_amt = 6 - lb_n as u32;
311 for (src, &br) in BIT_REVERSE_6BIT.iter().enumerate().take(vals.len()) {
312 let dst = (br as usize).wrapping_shr(dst_shr_amt);
313 if src < dst {
314 vals.swap(src, dst);
315 }
316 }
317 } else {
318 // LLVM does not know that it does not need to reverse src at each iteration (which is
319 // expensive on x86). We take advantage of the fact that the low bits of dst change rarely and the high
320 // bits of dst are dependent only on the low bits of src.
321 let dst_lo_shr_amt = usize::BITS - (lb_n - 6) as u32;
322 let dst_hi_shl_amt = lb_n - 6;
323 for src_chunk in 0..(vals.len() >> 6) {
324 let src_hi = src_chunk << 6;
325 let dst_lo = src_chunk.reverse_bits().wrapping_shr(dst_lo_shr_amt);
326 for (src_lo, &br) in BIT_REVERSE_6BIT.iter().enumerate() {
327 let dst_hi = (br as usize) << dst_hi_shl_amt;
328 let src = src_hi + src_lo;
329 let dst = dst_hi + dst_lo;
330 if src < dst {
331 vals.swap(src, dst);
332 }
333 }
334 }
335 }
336}
337
338#[cfg(all(target_arch = "aarch64", target_feature = "neon"))]
339const fn reverse_slice_index_bits_small<F>(vals: &mut [F], lb_n: usize) {
340 // Aarch64 can reverse bits in one instruction, so the trivial version works best.
341 let mut src = 0;
342 while src < vals.len() {
343 let dst = src.reverse_bits().wrapping_shr(usize::BITS - lb_n as u32);
344 if src < dst {
345 vals.swap(src, dst);
346 }
347
348 src += 1;
349 }
350}
351
352/// Split `arr` chunks and bit-reverse the order of the chunks. There are `1 << lb_num_chunks`
353/// chunks, each of length `1 << lb_chunk_size`.
354/// SAFETY: ensure that `arr.len() == 1 << lb_num_chunks + lb_chunk_size`.
355unsafe fn reverse_slice_index_bits_chunks<F>(
356 vals: &mut [F],
357 lb_num_chunks: usize,
358 lb_chunk_size: usize,
359) {
360 // Derive both chunk pointers from the entire slice: separate element borrows
361 // would not grant access to the whole chunk and could invalidate each other.
362 let ptr = vals.as_mut_ptr();
363 for i in 0..1usize << lb_num_chunks {
364 // `wrapping_shr` handles the silly case when `lb_num_chunks == 0`.
365 let j = i
366 .reverse_bits()
367 .wrapping_shr(usize::BITS - lb_num_chunks as u32);
368 if i < j {
369 // SAFETY: Both indices select complete chunks within the slice by the
370 // length precondition; i < j ensures the chunks do not overlap.
371 unsafe {
372 core::ptr::swap_nonoverlapping(
373 ptr.add(i << lb_chunk_size),
374 ptr.add(j << lb_chunk_size),
375 1 << lb_chunk_size,
376 );
377 }
378 }
379 }
380}
381
382/// Try to force Rust to emit a branch. Example:
383///
384/// ```no_run
385/// let x = 100;
386/// if x > 20 {
387/// println!("x is big!");
388/// p3_util::branch_hint();
389/// } else {
390/// println!("x is small!");
391/// }
392/// ```
393///
394/// This function has no semantics. It is a hint only.
395#[inline(always)]
396pub fn branch_hint() {
397 // NOTE: These are the currently supported assembly architectures. See the
398 // [nightly reference](https://doc.rust-lang.org/nightly/reference/inline-assembly.html) for
399 // the most up-to-date list.
400 #[cfg(any(
401 target_arch = "aarch64",
402 target_arch = "arm",
403 target_arch = "riscv32",
404 target_arch = "riscv64",
405 target_arch = "x86",
406 target_arch = "x86_64",
407 ))]
408 unsafe {
409 core::arch::asm!("", options(nomem, nostack, preserves_flags));
410 }
411}
412
413/// Return a String containing the name of T but with all the crate
414/// and module prefixes removed.
415pub fn pretty_name<T>() -> String {
416 let name = type_name::<T>();
417 let mut result = String::new();
418 for qual in name.split_inclusive(&['<', '>', ',']) {
419 result.push_str(qual.split("::").last().unwrap());
420 }
421 result
422}
423
424/// A C-style buffered input reader, similar to
425/// `core::iter::Iterator::next_chunk()` from nightly.
426///
427/// Returns an array of `MaybeUninit<T>` and the number of items in the
428/// array which have been correctly initialized.
429#[inline]
430fn iter_next_chunk_erased<const BUFLEN: usize, I: Iterator>(
431 iter: &mut I,
432) -> ([MaybeUninit<I::Item>; BUFLEN], usize)
433where
434 I::Item: Copy,
435{
436 let mut buf = [const { MaybeUninit::<I::Item>::uninit() }; BUFLEN];
437 let mut i = 0;
438
439 while i < BUFLEN {
440 if let Some(c) = iter.next() {
441 // Copy the next Item into `buf`.
442 unsafe {
443 buf.get_unchecked_mut(i).write(c);
444 i = i.unchecked_add(1);
445 }
446 } else {
447 // No more items in the iterator.
448 break;
449 }
450 }
451 (buf, i)
452}
453
454/// Split an iterator into small arrays and apply `func` to each.
455///
456/// Repeatedly read `BUFLEN` elements from `input` into an array and
457/// pass the array to `func` as a slice. If less than `BUFLEN`
458/// elements are remaining, that smaller slice is passed to `func` (if
459/// it is non-empty) and the function returns.
460#[inline]
461pub fn apply_to_chunks<const BUFLEN: usize, I, H>(input: I, mut func: H)
462where
463 I: IntoIterator<Item = u8>,
464 H: FnMut(&[u8]),
465{
466 let mut iter = input.into_iter();
467 loop {
468 let (buf, n) = iter_next_chunk_erased::<BUFLEN, _>(&mut iter);
469 if n == 0 {
470 break;
471 }
472 func(unsafe { buf.get_unchecked(..n).assume_init_ref() });
473 }
474}
475
476/// Pulls `N` items from `iter` and returns them as an array. If the iterator
477/// yields fewer than `N` items (but more than `0`), pads by the given default value.
478///
479/// Since the iterator is passed as a mutable reference and this function calls
480/// `next` at most `N` times, the iterator can still be used afterwards to
481/// retrieve the remaining items.
482///
483/// If `iter.next()` panics, all items already yielded by the iterator are
484/// dropped.
485#[inline]
486fn iter_next_chunk_padded<T: Copy, const N: usize>(
487 iter: &mut impl Iterator<Item = T>,
488 default: T, // Needed due to [T; M] not always implementing Default. Can probably be dropped if const generics stabilize.
489) -> Option<[T; N]> {
490 let (mut arr, n) = iter_next_chunk_erased::<N, _>(iter);
491 (n != 0).then(|| {
492 // Fill the rest of the array with default values.
493 arr[n..].fill(MaybeUninit::new(default));
494 unsafe { mem::transmute_copy::<_, [T; N]>(&arr) }
495 })
496}
497
498/// Returns an iterator over `N` elements of the iterator at a time.
499///
500/// The chunks do not overlap. If `N` does not divide the length of the
501/// iterator, then the last chunk is padded with up to `N-1` copies of the given default value.
502///
503/// This is essentially a copy pasted version of the nightly `array_chunks` function.
504/// <https://doc.rust-lang.org/std/iter/trait.Iterator.html#method.array_chunks>
505/// Once that is stabilized this and the functions above it should be removed.
506#[inline]
507pub fn iter_array_chunks_padded<T: Copy, const N: usize>(
508 iter: impl IntoIterator<Item = T>,
509 default: T, // Needed due to [T; M] not always implementing Default. Can probably be dropped if const generics stabilize.
510) -> impl Iterator<Item = [T; N]> {
511 let mut iter = iter.into_iter();
512 iter::from_fn(move || iter_next_chunk_padded(&mut iter, default))
513}
514
515/// Reinterpret a slice of `BaseArray` elements as a slice of `Base` elements
516///
517/// This is useful to convert `&[F; N]` to `&[F]` or `&[A]` to `&[F]` where
518/// `A` has the same size, alignment and memory layout as `[F; N]` for some `N`.
519///
520/// # Safety
521///
522/// This is assumes that `BaseArray` has the same alignment and memory layout as `[Base; N]`.
523/// As Rust guarantees that arrays elements are contiguous in memory and the alignment of
524/// the array is the same as the alignment of its elements, this means that `BaseArray`
525/// must have the same alignment as `Base`.
526///
527/// # Panics
528///
529/// This panics if the size of `BaseArray` is not a multiple of the size of `Base`.
530#[inline]
531pub const unsafe fn as_base_slice<Base, BaseArray>(buf: &[BaseArray]) -> &[Base] {
532 const {
533 assert!(align_of::<Base>() == align_of::<BaseArray>());
534 assert!(size_of::<BaseArray>().is_multiple_of(size_of::<Base>()));
535 }
536
537 let d = size_of::<BaseArray>() / size_of::<Base>();
538
539 let buf_ptr = buf.as_ptr().cast::<Base>();
540 let n = buf.len() * d;
541 unsafe { slice::from_raw_parts(buf_ptr, n) }
542}
543
544/// Reinterpret a mutable slice of `BaseArray` elements as a slice of `Base` elements
545///
546/// This is useful to convert `&[F; N]` to `&[F]` or `&[A]` to `&[F]` where
547/// `A` has the same size, alignment and memory layout as `[F; N]` for some `N`.
548///
549/// # Safety
550///
551/// This is assumes that `BaseArray` has the same alignment and memory layout as `[Base; N]`.
552/// As Rust guarantees that arrays elements are contiguous in memory and the alignment of
553/// the array is the same as the alignment of its elements, this means that `BaseArray`
554/// must have the same alignment as `Base`.
555///
556/// # Panics
557///
558/// This panics if the size of `BaseArray` is not a multiple of the size of `Base`.
559#[inline]
560pub const unsafe fn as_base_slice_mut<Base, BaseArray>(buf: &mut [BaseArray]) -> &mut [Base] {
561 const {
562 assert!(align_of::<Base>() == align_of::<BaseArray>());
563 assert!(size_of::<BaseArray>().is_multiple_of(size_of::<Base>()));
564 }
565
566 let d = size_of::<BaseArray>() / size_of::<Base>();
567
568 let buf_ptr = buf.as_mut_ptr().cast::<Base>();
569 let n = buf.len() * d;
570 unsafe { slice::from_raw_parts_mut(buf_ptr, n) }
571}
572
573/// Convert a vector of `BaseArray` elements to a vector of `Base` elements without any
574/// reallocations.
575///
576/// This is useful to convert `Vec<[F; N]>` to `Vec<F>` or `Vec<A>` to `Vec<F>` where
577/// `A` has the same size, alignment and memory layout as `[F; N]` for some `N`. It can also,
578/// be used to safely convert `Vec<u32>` to `Vec<F>` if `F` is a `32` bit field
579/// or `Vec<u64>` to `Vec<F>` if `F` is a `64` bit field.
580///
581/// # Safety
582///
583/// This is assumes that `BaseArray` has the same alignment and memory layout as `[Base; N]`.
584/// As Rust guarantees that arrays elements are contiguous in memory and the alignment of
585/// the array is the same as the alignment of its elements, this means that `BaseArray`
586/// must have the same alignment as `Base`.
587///
588/// # Panics
589///
590/// This panics if the size of `BaseArray` is not a multiple of the size of `Base`.
591#[inline]
592pub unsafe fn flatten_to_base<Base, BaseArray>(vec: Vec<BaseArray>) -> Vec<Base> {
593 const {
594 assert!(align_of::<Base>() == align_of::<BaseArray>());
595 assert!(size_of::<BaseArray>().is_multiple_of(size_of::<Base>()));
596 }
597
598 let d = size_of::<BaseArray>() / size_of::<Base>();
599 // Prevent running `vec`'s destructor so we are in complete control
600 // of the allocation.
601 let mut values = ManuallyDrop::new(vec);
602
603 // Each `Self` is an array of `d` elements, so the length and capacity of
604 // the new vector will be multiplied by `d`.
605 let new_len = values.len() * d;
606 let new_cap = values.capacity() * d;
607
608 // Safe as BaseArray and Base have the same alignment.
609 let ptr = values.as_mut_ptr() as *mut Base;
610
611 unsafe {
612 // Safety:
613 // - BaseArray and Base have the same alignment.
614 // - As size_of::<BaseArray>() == size_of::<Base>() * d:
615 // -- The capacity of the new vector is equal to the capacity of the old vector.
616 // -- The first new_len elements of the new vector correspond to the first
617 // len elements of the old vector and so are properly initialized.
618 Vec::from_raw_parts(ptr, new_len, new_cap)
619 }
620}
621
622/// Convert a vector of `Base` elements to a vector of `BaseArray` elements ideally without any
623/// reallocations.
624///
625/// This is an inverse of `flatten_to_base`. Unfortunately, unlike `flatten_to_base`, it may not be
626/// possible to avoid allocations. This issue is that there is not way to guarantee that the capacity
627/// of the vector is a multiple of `d`.
628///
629/// # Safety
630///
631/// This is assumes that `BaseArray` has the same alignment and memory layout as `[Base; N]`.
632/// As Rust guarantees that arrays elements are contiguous in memory and the alignment of
633/// the array is the same as the alignment of its elements, this means that `BaseArray`
634/// must have the same alignment as `Base`.
635///
636/// # Panics
637///
638/// This panics if the size of `BaseArray` is not a multiple of the size of `Base`.
639/// This panics if the length of the vector is not a multiple of the ratio of the sizes.
640#[inline]
641pub unsafe fn reconstitute_from_base<Base, BaseArray: Clone>(mut vec: Vec<Base>) -> Vec<BaseArray> {
642 const {
643 assert!(align_of::<Base>() == align_of::<BaseArray>());
644 assert!(size_of::<BaseArray>().is_multiple_of(size_of::<Base>()));
645 }
646
647 let d = size_of::<BaseArray>() / size_of::<Base>();
648
649 assert!(
650 vec.len().is_multiple_of(d),
651 "Vector length (got {}) must be a multiple of the extension field dimension ({}).",
652 vec.len(),
653 d
654 );
655
656 let new_len = vec.len() / d;
657
658 // We could call vec.shrink_to_fit() here to try and increase the probability that
659 // the capacity is a multiple of d. That might cause a reallocation though which
660 // would defeat the whole purpose.
661 let cap = vec.capacity();
662
663 // The assumption is that basically all callers of `reconstitute_from_base_vec` will be calling it
664 // with a vector constructed from `flatten_to_base` and so the capacity should be a multiple of `d`.
665 // But capacities can do strange things so we need to support both possibilities.
666 // Note that the `else` branch would also work if the capacity is a multiple of `d` but it is slower.
667 if cap.is_multiple_of(d) {
668 // Prevent running `vec`'s destructor so we are in complete control
669 // of the allocation.
670 let mut values = ManuallyDrop::new(vec);
671
672 // If we are on this branch then the capacity is a multiple of `d`.
673 let new_cap = cap / d;
674
675 // Safe as BaseArray and Base have the same alignment.
676 let ptr = values.as_mut_ptr() as *mut BaseArray;
677
678 unsafe {
679 // Safety:
680 // - BaseArray and Base have the same alignment.
681 // - As size_of::<Base>() == size_of::<BaseArray>() / d:
682 // -- If we have reached this point, the length and capacity are both divisible by `d`.
683 // -- The capacity of the new vector is equal to the capacity of the old vector.
684 // -- The first new_len elements of the new vector correspond to the first
685 // len elements of the old vector and so are properly initialized.
686 Vec::from_raw_parts(ptr, new_len, new_cap)
687 }
688 } else {
689 // If the capacity is not a multiple of `D`, we go via slices.
690
691 let buf_ptr = vec.as_mut_ptr().cast::<BaseArray>();
692 let slice = unsafe {
693 // Safety:
694 // - BaseArray and Base have the same alignment.
695 // - As size_of::<Base>() == size_of::<BaseArray>() / D:
696 // -- If we have reached this point, the length is divisible by `D`.
697 // -- The first new_len elements of the slice correspond to the first
698 // len elements of the old slice and so are properly initialized.
699 slice::from_raw_parts(buf_ptr, new_len)
700 };
701
702 // Ideally the compiler could optimize this away to avoid the copy but it appears not to.
703 slice.to_vec()
704 }
705}
706
707#[inline(always)]
708pub const fn relatively_prime_u64(mut u: u64, mut v: u64) -> bool {
709 // Check that neither input is 0.
710 if u == 0 || v == 0 {
711 return false;
712 }
713
714 // Check divisibility by 2.
715 if (u | v) & 1 == 0 {
716 return false;
717 }
718
719 // Remove factors of 2 from `u` and `v`
720 u >>= u.trailing_zeros();
721 if u == 1 {
722 return true;
723 }
724
725 while v != 0 {
726 v >>= v.trailing_zeros();
727 if v == 1 {
728 return true;
729 }
730
731 // Ensure u <= v
732 if u > v {
733 core::mem::swap(&mut u, &mut v);
734 }
735
736 // This looks inefficient for v >> u but thanks to the fact that we remove
737 // trailing_zeros of v in every iteration, it ends up much more performative
738 // than first glance implies.
739 v -= u;
740 }
741 // If we made it through the loop, at no point is u or v equal to 1 and so the gcd
742 // must be greater than 1.
743 false
744}
745
746/// Inner loop of the deferred GCD algorithm.
747///
748/// See: <https://eprint.iacr.org/2020/972.pdf> for more information.
749///
750/// This is basically a mini GCD algorithm which builds up a transformation to apply to the larger
751/// numbers in the main loop. The key point is that this small loop only uses u64s, subtractions and
752/// bit shifts, which are very fast operations.
753///
754/// The bottom `NUM_ROUNDS` bits of `a` and `b` should match the bottom `NUM_ROUNDS` bits of
755/// the corresponding big-ints and the top `NUM_ROUNDS + 2` should match the top bits including
756/// zeroes if the original numbers have different sizes.
757#[inline]
758pub const fn gcd_inner<const NUM_ROUNDS: usize>(a: &mut u64, b: &mut u64) -> (i64, i64, i64, i64) {
759 // Initialise update factors.
760 // At the start of round 0: -1 < f0, g0, f1, g1 <= 1
761 let (mut f0, mut g0, mut f1, mut g1) = (1, 0, 0, 1);
762
763 // If at the start of a round: -2^i < f0, g0, f1, g1 <= 2^i
764 // Then, at the end of the round: -2^{i + 1} < f0, g0, f1, g1 <= 2^{i + 1}
765 // use manual `while` loop to enable `const`
766 let mut round = 0;
767 while round < NUM_ROUNDS {
768 if *a & 1 == 0 {
769 *a >>= 1;
770 } else {
771 if *a < *b {
772 core::mem::swap(a, b);
773 (f0, f1) = (f1, f0);
774 (g0, g1) = (g1, g0);
775 }
776 *a -= *b;
777 *a >>= 1;
778 f0 -= f1;
779 g0 -= g1;
780 }
781 f1 <<= 1;
782 g1 <<= 1;
783
784 round += 1;
785 }
786
787 // -2^NUM_ROUNDS < f0, g0, f1, g1 <= 2^NUM_ROUNDS
788 // Hence provided NUM_ROUNDS <= 62, we will not get any overflow.
789 // Additionally, if NUM_ROUNDS <= 63, then the only source of overflow will be
790 // if a variable is meant to equal 2^{63} in which case it will overflow to -2^{63}.
791 (f0, g0, f1, g1)
792}
793
794/// Inverts elements inside the prime field `F_P` with `P < 2^FIELD_BITS`.
795///
796/// Arguments:
797/// - a: The value we want to invert. It must be < P.
798/// - b: The value of the prime `P > 2`.
799///
800/// Output:
801/// - A `64-bit` signed integer `v` equal to `2^{2 * FIELD_BITS - 2} a^{-1} mod P` with
802/// size `|v| < 2^{2 * FIELD_BITS - 2}`.
803///
804/// It is up to the user to ensure that `b` is an odd prime with at most `FIELD_BITS` bits and
805/// `a < b`. If either of these assumptions break, the output is undefined.
806#[inline]
807pub const fn gcd_inversion_prime_field_32<const FIELD_BITS: u32>(mut a: u32, mut b: u32) -> i64 {
808 const {
809 assert!(FIELD_BITS <= 32);
810 }
811 debug_assert!(((1_u64 << FIELD_BITS) - 1) >= b as u64);
812
813 // Initialise u, v. Note that |u|, |v| <= 2^0
814 let (mut u, mut v) = (1_i64, 0_i64);
815
816 // Let a0 and P denote the initial values of a and b. Observe:
817 // `a = u * a0 mod P`
818 // `b = v * a0 mod P`
819 // `len(a) + len(b) <= 2 * len(P) <= 2 * FIELD_BITS`
820
821 // use manual `while` loop to enable `const`
822 let mut i = 0;
823 while i < 2 * FIELD_BITS - 2 {
824 // Assume at the start of the loop i:
825 // (1) `|u|, |v| <= 2^{i}`
826 // (2) `2^i * a = u * a0 mod P`
827 // (3) `2^i * b = v * a0 mod P`
828 // (4) `gcd(a, b) = 1`
829 // (5) `b` is odd.
830 // (6) `len(a) + len(b) <= max(n - i, 1)`
831
832 if a & 1 != 0 {
833 if a < b {
834 (a, b) = (b, a);
835 (u, v) = (v, u);
836 }
837 // As b < a, this subtraction cannot increase `len(a) + len(b)`
838 a -= b;
839 // Observe |u'| = |u - v| <= |u| + |v| <= 2^{i + 1}
840 u -= v;
841
842 // As (1) and (2) hold, we have
843 // `2^i a' = 2^i * (a - b) = (u - v) * a0 mod P = u' * a0 mod P`
844 }
845 // As b is odd, a must now be even.
846 // This reduces `len(a) + len(b)` by 1 (unless `a = 0` in which case `b = 1` and the sum of the lengths is always 1)
847 a >>= 1;
848
849 // Observe |v'| = 2|v| <= 2^{i + 1}
850 v <<= 1;
851
852 // Thus as the end of loop i:
853 // (1) `|u|, |v| <= 2^{i + 1}`
854 // (2) `2^{i + 1} * a = u * a0 mod P` (As we have halved a)
855 // (3) `2^{i + 1} * b = v * a0 mod P` (As we have doubled v)
856 // (4) `gcd(a, b) = 1`
857 // (5) `b` is odd.
858 // (6) `len(a) + len(b) <= max(n - i - 1, 1)`
859
860 i += 1;
861 }
862
863 // After the loops, we see that:
864 // |u|, |v| <= 2^{2 * FIELD_BITS - 2}: Hence for FIELD_BITS <= 32 we will not overflow an i64.
865 // `2^{2 * FIELD_BITS - 2} * b = v * a0 mod P`
866 // `len(a) + len(b) <= 2` with `gcd(a, b) = 1` and `b` odd.
867 // This implies that `b` must be `1` and so `v = 2^{2 * FIELD_BITS - 2} a0^{-1} mod P` as desired.
868 v
869}
870
871/// A raw mutable pointer wrapper that implements [`Send`] and [`Sync`].
872///
873/// Used to enable parallel writes to disjoint slices of a pre-allocated buffer
874/// from within closures that require `Send + Sync` (e.g. `rayon::ParallelIterator::for_each_init`).
875///
876/// # Safety
877///
878/// The caller must ensure that concurrent accesses through this pointer always
879/// target **non-overlapping** memory regions.
880#[derive(Clone, Copy)]
881pub struct DisjointMutPtr<T>(*mut T);
882
883// SAFETY: The contract of DisjointMutPtr guarantees that each thread writes to
884// a disjoint region, so sharing the pointer across threads is safe.
885unsafe impl<T> Send for DisjointMutPtr<T> {}
886unsafe impl<T> Sync for DisjointMutPtr<T> {}
887
888impl<T> DisjointMutPtr<T> {
889 /// Create a new `DisjointMutPtr` from a mutable slice.
890 #[inline]
891 pub const fn new(slice: &mut [T]) -> Self {
892 Self(slice.as_mut_ptr())
893 }
894
895 /// Get a mutable slice starting at `offset` with `len` elements.
896 ///
897 /// # Safety
898 ///
899 /// The caller must ensure the range `[offset, offset+len)` is within bounds
900 /// and does not overlap with any other concurrent access. The returned
901 /// slice must not outlive the buffer passed to [`Self::new`].
902 #[inline]
903 pub const unsafe fn slice_mut<'a>(self, offset: usize, len: usize) -> &'a mut [T] {
904 unsafe { core::slice::from_raw_parts_mut(self.0.add(offset), len) }
905 }
906
907 /// Get a shared slice starting at `offset` with `len` elements.
908 ///
909 /// # Safety
910 ///
911 /// The caller must ensure the range `[offset, offset+len)` is within bounds
912 /// and is not written by any concurrent access. The returned slice must not
913 /// outlive the buffer passed to [`Self::new`].
914 #[inline]
915 pub const unsafe fn slice<'a>(self, offset: usize, len: usize) -> &'a [T] {
916 unsafe { core::slice::from_raw_parts(self.0.add(offset), len) }
917 }
918}
919
920#[cfg(test)]
921mod tests {
922 use alloc::vec;
923 use alloc::vec::Vec;
924
925 use proptest::prelude::*;
926 use rand::rngs::SmallRng;
927 use rand::{RngExt, SeedableRng};
928
929 use super::*;
930
931 #[test]
932 fn test_reverse_bits_len() {
933 assert_eq!(reverse_bits_len(0b0000000000, 10), 0b0000000000);
934 assert_eq!(reverse_bits_len(0b0000000001, 10), 0b1000000000);
935 assert_eq!(reverse_bits_len(0b1000000000, 10), 0b0000000001);
936 assert_eq!(reverse_bits_len(0b00000, 5), 0b00000);
937 assert_eq!(reverse_bits_len(0b01011, 5), 0b11010);
938 }
939
940 #[test]
941 fn test_reverse_bits_len_full_width() {
942 // A full-width reversal is the largest valid bit length and must reverse every bit.
943 let bits = usize::BITS as usize;
944 assert_eq!(reverse_bits_len(1, bits), 1 << (bits - 1));
945 assert_eq!(reverse_bits_len(1 << (bits - 1), bits), 1);
946 }
947
948 #[test]
949 #[cfg(debug_assertions)]
950 #[should_panic(expected = "bit_len <= usize::BITS")]
951 fn test_reverse_bits_len_rejects_oversized_bit_len() {
952 // One bit past the word width: the shift would underflow into a wrong permutation.
953 // The expected message pins the guard, not the incidental subtraction-overflow panic.
954 let _ = reverse_bits_len(0, usize::BITS as usize + 1);
955 }
956
957 #[test]
958 fn test_reverse_index_bits() {
959 let mut arg = vec![10, 20, 30, 40];
960 reverse_slice_index_bits(&mut arg);
961 assert_eq!(arg, vec![10, 30, 20, 40]);
962
963 let mut input256: Vec<u64> = (0..256).collect();
964 #[rustfmt::skip]
965 let output256: Vec<u64> = vec![
966 0x00, 0x80, 0x40, 0xc0, 0x20, 0xa0, 0x60, 0xe0, 0x10, 0x90, 0x50, 0xd0, 0x30, 0xb0, 0x70, 0xf0,
967 0x08, 0x88, 0x48, 0xc8, 0x28, 0xa8, 0x68, 0xe8, 0x18, 0x98, 0x58, 0xd8, 0x38, 0xb8, 0x78, 0xf8,
968 0x04, 0x84, 0x44, 0xc4, 0x24, 0xa4, 0x64, 0xe4, 0x14, 0x94, 0x54, 0xd4, 0x34, 0xb4, 0x74, 0xf4,
969 0x0c, 0x8c, 0x4c, 0xcc, 0x2c, 0xac, 0x6c, 0xec, 0x1c, 0x9c, 0x5c, 0xdc, 0x3c, 0xbc, 0x7c, 0xfc,
970 0x02, 0x82, 0x42, 0xc2, 0x22, 0xa2, 0x62, 0xe2, 0x12, 0x92, 0x52, 0xd2, 0x32, 0xb2, 0x72, 0xf2,
971 0x0a, 0x8a, 0x4a, 0xca, 0x2a, 0xaa, 0x6a, 0xea, 0x1a, 0x9a, 0x5a, 0xda, 0x3a, 0xba, 0x7a, 0xfa,
972 0x06, 0x86, 0x46, 0xc6, 0x26, 0xa6, 0x66, 0xe6, 0x16, 0x96, 0x56, 0xd6, 0x36, 0xb6, 0x76, 0xf6,
973 0x0e, 0x8e, 0x4e, 0xce, 0x2e, 0xae, 0x6e, 0xee, 0x1e, 0x9e, 0x5e, 0xde, 0x3e, 0xbe, 0x7e, 0xfe,
974 0x01, 0x81, 0x41, 0xc1, 0x21, 0xa1, 0x61, 0xe1, 0x11, 0x91, 0x51, 0xd1, 0x31, 0xb1, 0x71, 0xf1,
975 0x09, 0x89, 0x49, 0xc9, 0x29, 0xa9, 0x69, 0xe9, 0x19, 0x99, 0x59, 0xd9, 0x39, 0xb9, 0x79, 0xf9,
976 0x05, 0x85, 0x45, 0xc5, 0x25, 0xa5, 0x65, 0xe5, 0x15, 0x95, 0x55, 0xd5, 0x35, 0xb5, 0x75, 0xf5,
977 0x0d, 0x8d, 0x4d, 0xcd, 0x2d, 0xad, 0x6d, 0xed, 0x1d, 0x9d, 0x5d, 0xdd, 0x3d, 0xbd, 0x7d, 0xfd,
978 0x03, 0x83, 0x43, 0xc3, 0x23, 0xa3, 0x63, 0xe3, 0x13, 0x93, 0x53, 0xd3, 0x33, 0xb3, 0x73, 0xf3,
979 0x0b, 0x8b, 0x4b, 0xcb, 0x2b, 0xab, 0x6b, 0xeb, 0x1b, 0x9b, 0x5b, 0xdb, 0x3b, 0xbb, 0x7b, 0xfb,
980 0x07, 0x87, 0x47, 0xc7, 0x27, 0xa7, 0x67, 0xe7, 0x17, 0x97, 0x57, 0xd7, 0x37, 0xb7, 0x77, 0xf7,
981 0x0f, 0x8f, 0x4f, 0xcf, 0x2f, 0xaf, 0x6f, 0xef, 0x1f, 0x9f, 0x5f, 0xdf, 0x3f, 0xbf, 0x7f, 0xff,
982 ];
983 reverse_slice_index_bits(&mut input256[..]);
984 assert_eq!(input256, output256);
985 }
986
987 #[test]
988 fn test_apply_to_chunks_exact_fit() {
989 const CHUNK_SIZE: usize = 4;
990 let input: Vec<u8> = vec![1, 2, 3, 4, 5, 6, 7, 8];
991 let mut results: Vec<Vec<u8>> = Vec::new();
992
993 apply_to_chunks::<CHUNK_SIZE, _, _>(input, |chunk| {
994 results.push(chunk.to_vec());
995 });
996
997 assert_eq!(results, vec![vec![1, 2, 3, 4], vec![5, 6, 7, 8]]);
998 }
999
1000 #[test]
1001 fn test_apply_to_chunks_with_remainder() {
1002 const CHUNK_SIZE: usize = 3;
1003 let input: Vec<u8> = vec![1, 2, 3, 4, 5, 6, 7];
1004 let mut results: Vec<Vec<u8>> = Vec::new();
1005
1006 apply_to_chunks::<CHUNK_SIZE, _, _>(input, |chunk| {
1007 results.push(chunk.to_vec());
1008 });
1009
1010 assert_eq!(results, vec![vec![1, 2, 3], vec![4, 5, 6], vec![7]]);
1011 }
1012
1013 #[test]
1014 fn test_apply_to_chunks_empty_input() {
1015 const CHUNK_SIZE: usize = 4;
1016 let input: Vec<u8> = vec![];
1017 let mut results: Vec<Vec<u8>> = Vec::new();
1018
1019 apply_to_chunks::<CHUNK_SIZE, _, _>(input, |chunk| {
1020 results.push(chunk.to_vec());
1021 });
1022
1023 assert!(results.is_empty());
1024 }
1025
1026 #[test]
1027 fn test_apply_to_chunks_single_chunk() {
1028 const CHUNK_SIZE: usize = 10;
1029 let input: Vec<u8> = vec![1, 2, 3, 4, 5];
1030 let mut results: Vec<Vec<u8>> = Vec::new();
1031
1032 apply_to_chunks::<CHUNK_SIZE, _, _>(input, |chunk| {
1033 results.push(chunk.to_vec());
1034 });
1035
1036 assert_eq!(results, vec![vec![1, 2, 3, 4, 5]]);
1037 }
1038
1039 #[test]
1040 fn test_apply_to_chunks_large_chunk_size() {
1041 const CHUNK_SIZE: usize = 100;
1042 let input: Vec<u8> = vec![1, 2, 3, 4, 5, 6, 7, 8];
1043 let mut results: Vec<Vec<u8>> = Vec::new();
1044
1045 apply_to_chunks::<CHUNK_SIZE, _, _>(input, |chunk| {
1046 results.push(chunk.to_vec());
1047 });
1048
1049 assert_eq!(results, vec![vec![1, 2, 3, 4, 5, 6, 7, 8]]);
1050 }
1051
1052 #[test]
1053 fn test_apply_to_chunks_large_input() {
1054 const CHUNK_SIZE: usize = 5;
1055 let input: Vec<u8> = (1..=20).collect();
1056 let mut results: Vec<Vec<u8>> = Vec::new();
1057
1058 apply_to_chunks::<CHUNK_SIZE, _, _>(input, |chunk| {
1059 results.push(chunk.to_vec());
1060 });
1061
1062 assert_eq!(
1063 results,
1064 vec![
1065 vec![1, 2, 3, 4, 5],
1066 vec![6, 7, 8, 9, 10],
1067 vec![11, 12, 13, 14, 15],
1068 vec![16, 17, 18, 19, 20]
1069 ]
1070 );
1071 }
1072
1073 #[test]
1074 fn test_reverse_slice_index_bits_strings() {
1075 use alloc::string::ToString;
1076
1077 for log_n in 0..=14 {
1078 let original: Vec<_> = (0..1 << log_n).map(|i| i.to_string()).collect();
1079 let expected: Vec<_> = (0..original.len())
1080 .map(|i| original[reverse_bits_len(i, log_n)].clone())
1081 .collect();
1082 let mut values = original.clone();
1083 reverse_slice_index_bits(&mut values);
1084 assert_eq!(values, expected, "log_n={log_n}");
1085 reverse_slice_index_bits(&mut values);
1086 assert_eq!(values, original, "involution at log_n={log_n}");
1087 }
1088 }
1089
1090 #[test]
1091 fn test_reverse_slice_index_bits_preserves_owners() {
1092 use core::sync::atomic::{AtomicUsize, Ordering};
1093
1094 // Pointer-sized, non-Copy and non-Clone: large cases exercise the cache decomposition.
1095 struct Owner<'a>(&'a AtomicUsize);
1096 impl Drop for Owner<'_> {
1097 fn drop(&mut self) {
1098 self.0.fetch_add(1, Ordering::Relaxed);
1099 }
1100 }
1101
1102 for log_n in 0..=15 {
1103 let drops: Vec<_> = (0..1 << log_n).map(|_| AtomicUsize::new(0)).collect();
1104 let mut values: Vec<_> = drops.iter().map(Owner).collect();
1105 reverse_slice_index_bits(&mut values);
1106 for (i, value) in values.iter().enumerate() {
1107 assert!(core::ptr::eq(value.0, &drops[reverse_bits_len(i, log_n)]));
1108 }
1109 reverse_slice_index_bits(&mut values);
1110 for (value, count) in values.iter().zip(&drops) {
1111 assert!(core::ptr::eq(value.0, count));
1112 assert_eq!(count.load(Ordering::Relaxed), 0);
1113 }
1114 drop(values);
1115 assert!(drops.iter().all(|count| count.load(Ordering::Relaxed) == 1));
1116 }
1117 }
1118
1119 #[test]
1120 fn test_reverse_slice_index_bits_random() {
1121 let lengths = [32, 128, 1 << 16];
1122 let mut rng = SmallRng::seed_from_u64(1);
1123 for _ in 0..32 {
1124 for &length in &lengths {
1125 let mut rand_list: Vec<u32> = Vec::with_capacity(length);
1126 rand_list.resize_with(length, || rng.random());
1127 let expect = reverse_index_bits_naive(&rand_list);
1128
1129 let mut actual = rand_list.clone();
1130 reverse_slice_index_bits(&mut actual);
1131
1132 assert_eq!(actual, expect);
1133 }
1134 }
1135 }
1136
1137 #[test]
1138 fn test_log2_strict_usize_edge_cases() {
1139 assert_eq!(log2_strict_usize(1), 0);
1140 assert_eq!(log2_strict_usize(2), 1);
1141 assert_eq!(log2_strict_usize(1 << 18), 18);
1142 assert_eq!(log2_strict_usize(1 << 31), 31);
1143 assert_eq!(
1144 log2_strict_usize(1 << (usize::BITS - 1)),
1145 usize::BITS as usize - 1
1146 );
1147 }
1148
1149 #[test]
1150 fn test_checked_pow2() {
1151 // 2^0 = 1, the smallest valid exponent.
1152 assert_eq!(checked_pow2(0), Some(1));
1153
1154 // 2^1 = 2.
1155 assert_eq!(checked_pow2(1), Some(2));
1156
1157 // 2^5 = 32, a typical small power.
1158 assert_eq!(checked_pow2(5), Some(32));
1159
1160 // 2^10 = 1024, commonly used as a domain size in FRI.
1161 assert_eq!(checked_pow2(10), Some(1024));
1162
1163 // 2^20 = 1_048_576, a realistic large trace length.
1164 assert_eq!(checked_pow2(20), Some(1_048_576));
1165
1166 // Largest representable power: 2^(BITS - 1).
1167 // On a 64-bit platform this is 2^63 = 0x8000_0000_0000_0000.
1168 let max_exp = usize::BITS as usize - 1;
1169 assert_eq!(checked_pow2(max_exp), Some(1usize << max_exp));
1170
1171 // Exponent equal to the bit width would shift 1 out of range.
1172 //
1173 // 1_usize << 64 (on 64-bit) → overflow
1174 //
1175 // Must return `None`.
1176 assert_eq!(checked_pow2(usize::BITS as usize), None);
1177
1178 // One past the maximum: also out of range.
1179 assert_eq!(checked_pow2(usize::BITS as usize + 1), None);
1180
1181 // Extreme exponent: usize::MAX is astronomically beyond
1182 // representable range — must return `None`.
1183 assert_eq!(checked_pow2(usize::MAX), None);
1184 }
1185
1186 #[test]
1187 fn test_checked_log_size_sum() {
1188 // Both zero: 0 + 0 = 0, 2^0 = 1.
1189 assert_eq!(checked_log_size_sum(0, 0), Some((0, 1)));
1190
1191 // Identity cases: adding zero to either side is a no-op.
1192 assert_eq!(checked_log_size_sum(5, 0), Some((5, 32)));
1193 assert_eq!(checked_log_size_sum(0, 10), Some((10, 1024)));
1194
1195 // Typical FRI scenario: degree_bits=10, log_quotient_chunks=2.
1196 //
1197 // 10 + 2 = 12, 2^12 = 4096
1198 assert_eq!(checked_log_size_sum(10, 2), Some((12, 4096)));
1199
1200 // Commutativity: order of operands must not matter.
1201 assert_eq!(checked_log_size_sum(2, 10), Some((12, 4096)));
1202
1203 // Large realistic case: degree_bits=20, log_chunks=3.
1204 //
1205 // 20 + 3 = 23, 2^23 = 8_388_608
1206 assert_eq!(checked_log_size_sum(20, 3), Some((23, 8_388_608)));
1207
1208 // Largest representable sum: (BITS - 2) + 1 = BITS - 1.
1209 let almost_max = usize::BITS as usize - 2;
1210 let max_exp = usize::BITS as usize - 1;
1211 assert_eq!(
1212 checked_log_size_sum(almost_max, 1),
1213 Some((max_exp, 1usize << max_exp))
1214 );
1215
1216 // Sum exactly at the bit width: overflows the shift.
1217 //
1218 // (BITS - 1) + 1 = BITS → 2^BITS is unrepresentable → None
1219 assert_eq!(checked_log_size_sum(max_exp, 1), None);
1220
1221 // Both operands large but sum still within range.
1222 //
1223 // 32 + 31 = 63 (on 64-bit) → 2^63 is representable
1224 let half = usize::BITS as usize / 2;
1225 let other_half = max_exp - half;
1226 assert_eq!(
1227 checked_log_size_sum(half, other_half),
1228 Some((max_exp, 1usize << max_exp))
1229 );
1230
1231 // Addition itself overflows usize, not just the shift.
1232 //
1233 // usize::MAX + 1 → checked_add returns None → None
1234 assert_eq!(checked_log_size_sum(usize::MAX, 1), None);
1235
1236 // Both operands at usize::MAX: addition doubly overflows.
1237 assert_eq!(checked_log_size_sum(usize::MAX, usize::MAX), None);
1238 }
1239
1240 #[test]
1241 #[should_panic]
1242 fn test_log2_strict_usize_zero() {
1243 let _ = log2_strict_usize(0);
1244 }
1245
1246 #[test]
1247 #[should_panic]
1248 fn test_log2_strict_usize_nonpower_2() {
1249 let _ = log2_strict_usize(0x78c341c65ae6d262);
1250 }
1251
1252 #[test]
1253 #[should_panic]
1254 fn test_log2_strict_usize_max() {
1255 let _ = log2_strict_usize(usize::MAX);
1256 }
1257
1258 #[test]
1259 fn test_log3_strict_powers_of_3() {
1260 // Test all powers of 3 up to 3^12 = 531441.
1261 assert_eq!(log3_strict_usize(1), 0);
1262 assert_eq!(log3_strict_usize(3), 1);
1263 assert_eq!(log3_strict_usize(9), 2);
1264 assert_eq!(log3_strict_usize(27), 3);
1265 assert_eq!(log3_strict_usize(81), 4);
1266 assert_eq!(log3_strict_usize(243), 5);
1267 assert_eq!(log3_strict_usize(729), 6);
1268 assert_eq!(log3_strict_usize(2187), 7);
1269 assert_eq!(log3_strict_usize(6561), 8);
1270 assert_eq!(log3_strict_usize(19683), 9);
1271 assert_eq!(log3_strict_usize(59049), 10);
1272 assert_eq!(log3_strict_usize(177_147), 11);
1273 assert_eq!(log3_strict_usize(531_441), 12);
1274 }
1275
1276 #[test]
1277 #[should_panic(expected = "input must be non-zero")]
1278 fn test_log3_strict_panics_on_zero() {
1279 let _ = log3_strict_usize(0);
1280 }
1281
1282 #[test]
1283 #[should_panic(expected = "is not a power of 3")]
1284 fn test_log3_strict_panics_on_non_power_of_3() {
1285 // 2 is not a power of 3.
1286 let _ = log3_strict_usize(2);
1287 }
1288
1289 #[test]
1290 #[should_panic(expected = "is not a power of 3")]
1291 fn test_log3_strict_panics_on_power_of_2() {
1292 // 8 = 2^3 is not a power of 3.
1293 let _ = log3_strict_usize(8);
1294 }
1295
1296 #[test]
1297 #[should_panic(expected = "is not a power of 3")]
1298 fn test_log3_strict_panics_on_product_with_other_primes() {
1299 // 6 = 2 * 3 is not a power of 3.
1300 let _ = log3_strict_usize(6);
1301 }
1302
1303 proptest! {
1304 #[test]
1305 fn test_log3_strict_roundtrip(k in 0u32..25u32) {
1306 // Roundtrip: 3^k -> log3_strict_usize -> k
1307 let n = 3usize.pow(k);
1308 assert_eq!(log3_strict_usize(n), k as usize);
1309 }
1310 }
1311
1312 #[test]
1313 fn test_log2_ceil_usize_comprehensive() {
1314 // Powers of 2
1315 assert_eq!(log2_ceil_usize(0), 0);
1316 assert_eq!(log2_ceil_usize(1), 0);
1317 assert_eq!(log2_ceil_usize(2), 1);
1318 assert_eq!(log2_ceil_usize(1 << 18), 18);
1319 assert_eq!(log2_ceil_usize(1 << 31), 31);
1320 assert_eq!(
1321 log2_ceil_usize(1 << (usize::BITS - 1)),
1322 usize::BITS as usize - 1
1323 );
1324
1325 // Nonpowers; want to round up
1326 assert_eq!(log2_ceil_usize(3), 2);
1327 assert_eq!(log2_ceil_usize(0x14fe901b), 29);
1328 assert_eq!(
1329 log2_ceil_usize((1 << (usize::BITS - 1)) + 1),
1330 usize::BITS as usize
1331 );
1332 assert_eq!(log2_ceil_usize(usize::MAX - 1), usize::BITS as usize);
1333 assert_eq!(log2_ceil_usize(usize::MAX), usize::BITS as usize);
1334 }
1335
1336 fn reverse_index_bits_naive<T: Copy>(arr: &[T]) -> Vec<T> {
1337 let n = arr.len();
1338 let n_power = log2_strict_usize(n);
1339
1340 let mut out = vec![None; n];
1341 for (i, v) in arr.iter().enumerate() {
1342 let dst = i.reverse_bits() >> (usize::BITS - n_power as u32);
1343 out[dst] = Some(*v);
1344 }
1345
1346 out.into_iter().map(|x| x.unwrap()).collect()
1347 }
1348
1349 #[test]
1350 fn test_relatively_prime_u64() {
1351 // Zero cases (should always return false)
1352 assert!(!relatively_prime_u64(0, 0));
1353 assert!(!relatively_prime_u64(10, 0));
1354 assert!(!relatively_prime_u64(0, 10));
1355 assert!(!relatively_prime_u64(0, 123456789));
1356
1357 // Number with itself (if greater than 1, not relatively prime)
1358 assert!(relatively_prime_u64(1, 1));
1359 assert!(!relatively_prime_u64(10, 10));
1360 assert!(!relatively_prime_u64(99999, 99999));
1361
1362 // Powers of 2 (always false since they share factor 2)
1363 assert!(!relatively_prime_u64(2, 4));
1364 assert!(!relatively_prime_u64(16, 32));
1365 assert!(!relatively_prime_u64(64, 128));
1366 assert!(!relatively_prime_u64(1024, 4096));
1367 assert!(!relatively_prime_u64(u64::MAX, u64::MAX));
1368
1369 // One number is a multiple of the other (always false)
1370 assert!(!relatively_prime_u64(5, 10));
1371 assert!(!relatively_prime_u64(12, 36));
1372 assert!(!relatively_prime_u64(15, 45));
1373 assert!(!relatively_prime_u64(100, 500));
1374
1375 // Co-prime numbers (should be true)
1376 assert!(relatively_prime_u64(17, 31));
1377 assert!(relatively_prime_u64(97, 43));
1378 assert!(relatively_prime_u64(7919, 65537));
1379 assert!(relatively_prime_u64(15485863, 32452843));
1380
1381 // Small prime numbers (should be true)
1382 assert!(relatively_prime_u64(13, 17));
1383 assert!(relatively_prime_u64(101, 103));
1384 assert!(relatively_prime_u64(1009, 1013));
1385
1386 // Large numbers (some cases where they are relatively prime or not)
1387 assert!(!relatively_prime_u64(
1388 190266297176832000,
1389 10430732356495263744
1390 ));
1391 assert!(!relatively_prime_u64(
1392 2040134905096275968,
1393 5701159354248194048
1394 ));
1395 assert!(!relatively_prime_u64(
1396 16611311494648745984,
1397 7514969329383038976
1398 ));
1399 assert!(!relatively_prime_u64(
1400 14863931409971066880,
1401 7911906750992527360
1402 ));
1403
1404 // Max values
1405 assert!(relatively_prime_u64(u64::MAX, 1));
1406 assert!(relatively_prime_u64(u64::MAX, u64::MAX - 1));
1407 assert!(!relatively_prime_u64(u64::MAX, u64::MAX));
1408 }
1409}