Skip to main content

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}