#![no_std]
extern crate alloc;
use alloc::slice;
use alloc::string::String;
use alloc::vec::Vec;
use core::any::type_name;
use core::hint::unreachable_unchecked;
use core::mem::{ManuallyDrop, MaybeUninit};
use core::{iter, mem};
use crate::transpose::transpose_in_place_square;
pub mod array_serialization;
pub mod linear_map;
pub mod transpose;
pub mod zip_eq;
#[must_use]
pub const fn log2_ceil_usize(n: usize) -> usize {
(usize::BITS - n.saturating_sub(1).leading_zeros()) as usize
}
#[must_use]
pub const fn log2_floor_usize(n: usize) -> usize {
if n == 0 {
return 0;
}
(usize::BITS - 1 - n.leading_zeros()) as usize
}
#[must_use]
pub const fn log2_ceil_u64(n: u64) -> u64 {
(u64::BITS - n.saturating_sub(1).leading_zeros()) as u64
}
#[must_use]
pub const fn checked_pow2(log_degree: usize) -> Option<usize> {
if log_degree < usize::BITS as usize {
Some(1usize << log_degree)
} else {
None
}
}
#[must_use]
pub const fn checked_log_size_sum(a: usize, b: usize) -> Option<(usize, usize)> {
match a.checked_add(b) {
Some(sum) => match checked_pow2(sum) {
Some(size) => Some((sum, size)),
None => None,
},
None => None,
}
}
#[must_use]
#[inline]
pub const fn log2_strict_usize(n: usize) -> usize {
let res = n.trailing_zeros();
assert!(n.wrapping_shr(res) == 1, "Not a power of two");
unsafe {
assume(n == 1 << res);
}
res as usize
}
const POWERS_OF_3: [u64; 41] = {
let mut table = [0u64; 41];
table[0] = 1;
let mut i = 1;
while i < 41 {
table[i] = table[i - 1] * 3;
i += 1;
}
table
};
const LOG2_TO_EXP: [u8; 64] = {
let mut table = [0u8; 64];
let mut i = 0;
while i < 41 {
let log2 = (u64::BITS - 1 - POWERS_OF_3[i].leading_zeros()) as usize;
table[log2] = i as u8;
i += 1;
}
table
};
#[must_use]
#[inline]
pub const fn log3_strict_usize(n: usize) -> usize {
assert!(n != 0, "log3_strict_usize: input must be non-zero");
let log2 = (usize::BITS - 1 - n.leading_zeros()) as usize;
let res = LOG2_TO_EXP[log2] as usize;
assert!(
POWERS_OF_3[res] as usize == n,
"log3_strict_usize: input is not a power of 3"
);
res
}
#[must_use]
pub const fn indices_arr<const N: usize>() -> [usize; N] {
let mut indices_arr = [0; N];
let mut i = 0;
while i < N {
indices_arr[i] = i;
i += 1;
}
indices_arr
}
pub const fn assert_clone<T: Clone>() {}
pub const fn assert_send<T: Send>() {}
pub const fn assert_sync<T: Sync>() {}
#[inline]
pub const fn reverse_bits(x: usize, n: usize) -> usize {
debug_assert!(n.is_power_of_two());
reverse_bits_len(x, n.trailing_zeros() as usize)
}
#[inline]
pub const fn reverse_bits_len(x: usize, bit_len: usize) -> usize {
debug_assert!(bit_len <= usize::BITS as usize);
x.reverse_bits()
.overflowing_shr(usize::BITS - bit_len as u32)
.0
}
#[cfg(not(target_arch = "aarch64"))]
#[rustfmt::skip]
const BIT_REVERSE_6BIT: &[u8] = &[
0o00, 0o40, 0o20, 0o60, 0o10, 0o50, 0o30, 0o70,
0o04, 0o44, 0o24, 0o64, 0o14, 0o54, 0o34, 0o74,
0o02, 0o42, 0o22, 0o62, 0o12, 0o52, 0o32, 0o72,
0o06, 0o46, 0o26, 0o66, 0o16, 0o56, 0o36, 0o76,
0o01, 0o41, 0o21, 0o61, 0o11, 0o51, 0o31, 0o71,
0o05, 0o45, 0o25, 0o65, 0o15, 0o55, 0o35, 0o75,
0o03, 0o43, 0o23, 0o63, 0o13, 0o53, 0o33, 0o73,
0o07, 0o47, 0o27, 0o67, 0o17, 0o57, 0o37, 0o77,
];
const BIG_T_SIZE: usize = 1 << 14;
const SMALL_ARR_SIZE: usize = 1 << 16;
pub fn reverse_slice_index_bits<F>(vals: &mut [F])
where
F: Copy + Send + Sync,
{
let n = vals.len();
if n == 0 {
return;
}
let log_n = log2_strict_usize(n);
if core::mem::size_of::<F>() << log_n <= SMALL_ARR_SIZE
|| core::mem::size_of::<F>() >= BIG_T_SIZE
{
reverse_slice_index_bits_small(vals, log_n);
} else {
debug_assert!(n >= 4);
let lb_num_chunks = log_n >> 1;
let lb_chunk_size = log_n - lb_num_chunks;
unsafe {
reverse_slice_index_bits_chunks(vals, lb_num_chunks, lb_chunk_size);
transpose_in_place_square(vals, lb_chunk_size, lb_num_chunks, 0);
if lb_num_chunks != lb_chunk_size {
let vals_with_offset = &mut vals[1 << lb_num_chunks..];
transpose_in_place_square(vals_with_offset, lb_chunk_size, lb_num_chunks, 0);
}
reverse_slice_index_bits_chunks(vals, lb_num_chunks, lb_chunk_size);
}
}
}
#[cfg(not(target_arch = "aarch64"))]
fn reverse_slice_index_bits_small<F>(vals: &mut [F], lb_n: usize) {
if lb_n <= 6 {
let dst_shr_amt = 6 - lb_n as u32;
for (src, &br) in BIT_REVERSE_6BIT.iter().enumerate().take(vals.len()) {
let dst = (br as usize).wrapping_shr(dst_shr_amt);
if src < dst {
vals.swap(src, dst);
}
}
} else {
let dst_lo_shr_amt = usize::BITS - (lb_n - 6) as u32;
let dst_hi_shl_amt = lb_n - 6;
for src_chunk in 0..(vals.len() >> 6) {
let src_hi = src_chunk << 6;
let dst_lo = src_chunk.reverse_bits().wrapping_shr(dst_lo_shr_amt);
for (src_lo, &br) in BIT_REVERSE_6BIT.iter().enumerate() {
let dst_hi = (br as usize) << dst_hi_shl_amt;
let src = src_hi + src_lo;
let dst = dst_hi + dst_lo;
if src < dst {
vals.swap(src, dst);
}
}
}
}
}
#[cfg(all(target_arch = "aarch64", target_feature = "neon"))]
const fn reverse_slice_index_bits_small<F>(vals: &mut [F], lb_n: usize) {
let mut src = 0;
while src < vals.len() {
let dst = src.reverse_bits().wrapping_shr(usize::BITS - lb_n as u32);
if src < dst {
vals.swap(src, dst);
}
src += 1;
}
}
unsafe fn reverse_slice_index_bits_chunks<F>(
vals: &mut [F],
lb_num_chunks: usize,
lb_chunk_size: usize,
) {
for i in 0..1usize << lb_num_chunks {
let j = i
.reverse_bits()
.wrapping_shr(usize::BITS - lb_num_chunks as u32);
if i < j {
unsafe {
core::ptr::swap_nonoverlapping(
vals.get_unchecked_mut(i << lb_chunk_size),
vals.get_unchecked_mut(j << lb_chunk_size),
1 << lb_chunk_size,
);
}
}
}
}
#[inline(always)]
pub const unsafe fn assume(p: bool) {
debug_assert!(p);
if !p {
unsafe {
unreachable_unchecked();
}
}
}
#[inline(always)]
pub fn branch_hint() {
#[cfg(any(
target_arch = "aarch64",
target_arch = "arm",
target_arch = "riscv32",
target_arch = "riscv64",
target_arch = "x86",
target_arch = "x86_64",
))]
unsafe {
core::arch::asm!("", options(nomem, nostack, preserves_flags));
}
}
pub fn pretty_name<T>() -> String {
let name = type_name::<T>();
let mut result = String::new();
for qual in name.split_inclusive(&['<', '>', ',']) {
result.push_str(qual.split("::").last().unwrap());
}
result
}
#[inline]
fn iter_next_chunk_erased<const BUFLEN: usize, I: Iterator>(
iter: &mut I,
) -> ([MaybeUninit<I::Item>; BUFLEN], usize)
where
I::Item: Copy,
{
let mut buf = [const { MaybeUninit::<I::Item>::uninit() }; BUFLEN];
let mut i = 0;
while i < BUFLEN {
if let Some(c) = iter.next() {
unsafe {
buf.get_unchecked_mut(i).write(c);
i = i.unchecked_add(1);
}
} else {
break;
}
}
(buf, i)
}
#[inline]
pub fn apply_to_chunks<const BUFLEN: usize, I, H>(input: I, mut func: H)
where
I: IntoIterator<Item = u8>,
H: FnMut(&[I::Item]),
{
let mut iter = input.into_iter();
loop {
let (buf, n) = iter_next_chunk_erased::<BUFLEN, _>(&mut iter);
if n == 0 {
break;
}
func(unsafe { buf.get_unchecked(..n).assume_init_ref() });
}
}
#[inline]
fn iter_next_chunk_padded<T: Copy, const N: usize>(
iter: &mut impl Iterator<Item = T>,
default: T, ) -> Option<[T; N]> {
let (mut arr, n) = iter_next_chunk_erased::<N, _>(iter);
(n != 0).then(|| {
arr[n..].fill(MaybeUninit::new(default));
unsafe { mem::transmute_copy::<_, [T; N]>(&arr) }
})
}
#[inline]
pub fn iter_array_chunks_padded<T: Copy, const N: usize>(
iter: impl IntoIterator<Item = T>,
default: T, ) -> impl Iterator<Item = [T; N]> {
let mut iter = iter.into_iter();
iter::from_fn(move || iter_next_chunk_padded(&mut iter, default))
}
#[inline]
pub const unsafe fn as_base_slice<Base, BaseArray>(buf: &[BaseArray]) -> &[Base] {
const {
assert!(align_of::<Base>() == align_of::<BaseArray>());
assert!(size_of::<BaseArray>().is_multiple_of(size_of::<Base>()));
}
let d = size_of::<BaseArray>() / size_of::<Base>();
let buf_ptr = buf.as_ptr().cast::<Base>();
let n = buf.len() * d;
unsafe { slice::from_raw_parts(buf_ptr, n) }
}
#[inline]
pub const unsafe fn as_base_slice_mut<Base, BaseArray>(buf: &mut [BaseArray]) -> &mut [Base] {
const {
assert!(align_of::<Base>() == align_of::<BaseArray>());
assert!(size_of::<BaseArray>().is_multiple_of(size_of::<Base>()));
}
let d = size_of::<BaseArray>() / size_of::<Base>();
let buf_ptr = buf.as_mut_ptr().cast::<Base>();
let n = buf.len() * d;
unsafe { slice::from_raw_parts_mut(buf_ptr, n) }
}
#[inline]
pub unsafe fn flatten_to_base<Base, BaseArray>(vec: Vec<BaseArray>) -> Vec<Base> {
const {
assert!(align_of::<Base>() == align_of::<BaseArray>());
assert!(size_of::<BaseArray>().is_multiple_of(size_of::<Base>()));
}
let d = size_of::<BaseArray>() / size_of::<Base>();
let mut values = ManuallyDrop::new(vec);
let new_len = values.len() * d;
let new_cap = values.capacity() * d;
let ptr = values.as_mut_ptr() as *mut Base;
unsafe {
Vec::from_raw_parts(ptr, new_len, new_cap)
}
}
#[inline]
pub unsafe fn reconstitute_from_base<Base, BaseArray: Clone>(mut vec: Vec<Base>) -> Vec<BaseArray> {
const {
assert!(align_of::<Base>() == align_of::<BaseArray>());
assert!(size_of::<BaseArray>().is_multiple_of(size_of::<Base>()));
}
let d = size_of::<BaseArray>() / size_of::<Base>();
assert!(
vec.len().is_multiple_of(d),
"Vector length (got {}) must be a multiple of the extension field dimension ({}).",
vec.len(),
d
);
let new_len = vec.len() / d;
let cap = vec.capacity();
if cap.is_multiple_of(d) {
let mut values = ManuallyDrop::new(vec);
let new_cap = cap / d;
let ptr = values.as_mut_ptr() as *mut BaseArray;
unsafe {
Vec::from_raw_parts(ptr, new_len, new_cap)
}
} else {
let buf_ptr = vec.as_mut_ptr().cast::<BaseArray>();
let slice = unsafe {
slice::from_raw_parts(buf_ptr, new_len)
};
slice.to_vec()
}
}
#[inline(always)]
pub const fn relatively_prime_u64(mut u: u64, mut v: u64) -> bool {
if u == 0 || v == 0 {
return false;
}
if (u | v) & 1 == 0 {
return false;
}
u >>= u.trailing_zeros();
if u == 1 {
return true;
}
while v != 0 {
v >>= v.trailing_zeros();
if v == 1 {
return true;
}
if u > v {
core::mem::swap(&mut u, &mut v);
}
v -= u;
}
false
}
#[inline]
pub const fn gcd_inner<const NUM_ROUNDS: usize>(a: &mut u64, b: &mut u64) -> (i64, i64, i64, i64) {
let (mut f0, mut g0, mut f1, mut g1) = (1, 0, 0, 1);
let mut round = 0;
while round < NUM_ROUNDS {
if *a & 1 == 0 {
*a >>= 1;
} else {
if *a < *b {
core::mem::swap(a, b);
(f0, f1) = (f1, f0);
(g0, g1) = (g1, g0);
}
*a -= *b;
*a >>= 1;
f0 -= f1;
g0 -= g1;
}
f1 <<= 1;
g1 <<= 1;
round += 1;
}
(f0, g0, f1, g1)
}
#[inline]
pub const fn gcd_inversion_prime_field_32<const FIELD_BITS: u32>(mut a: u32, mut b: u32) -> i64 {
const {
assert!(FIELD_BITS <= 32);
}
debug_assert!(((1_u64 << FIELD_BITS) - 1) >= b as u64);
let (mut u, mut v) = (1_i64, 0_i64);
let mut i = 0;
while i < 2 * FIELD_BITS - 2 {
if a & 1 != 0 {
if a < b {
(a, b) = (b, a);
(u, v) = (v, u);
}
a -= b;
u -= v;
}
a >>= 1;
v <<= 1;
i += 1;
}
v
}
#[derive(Clone, Copy)]
pub struct DisjointMutPtr<T>(*mut T);
unsafe impl<T> Send for DisjointMutPtr<T> {}
unsafe impl<T> Sync for DisjointMutPtr<T> {}
impl<T> DisjointMutPtr<T> {
#[inline]
pub const fn new(slice: &mut [T]) -> Self {
Self(slice.as_mut_ptr())
}
#[inline]
pub const unsafe fn slice_mut(self, offset: usize, len: usize) -> &'static mut [T] {
unsafe { core::slice::from_raw_parts_mut(self.0.add(offset), len) }
}
}
#[cfg(test)]
mod tests {
use alloc::vec;
use alloc::vec::Vec;
use proptest::prelude::*;
use rand::rngs::SmallRng;
use rand::{RngExt, SeedableRng};
use super::*;
#[test]
fn test_reverse_bits_len() {
assert_eq!(reverse_bits_len(0b0000000000, 10), 0b0000000000);
assert_eq!(reverse_bits_len(0b0000000001, 10), 0b1000000000);
assert_eq!(reverse_bits_len(0b1000000000, 10), 0b0000000001);
assert_eq!(reverse_bits_len(0b00000, 5), 0b00000);
assert_eq!(reverse_bits_len(0b01011, 5), 0b11010);
}
#[test]
fn test_reverse_bits_len_full_width() {
let bits = usize::BITS as usize;
assert_eq!(reverse_bits_len(1, bits), 1 << (bits - 1));
assert_eq!(reverse_bits_len(1 << (bits - 1), bits), 1);
}
#[test]
#[cfg(debug_assertions)]
#[should_panic(expected = "bit_len <= usize::BITS")]
fn test_reverse_bits_len_rejects_oversized_bit_len() {
let _ = reverse_bits_len(0, usize::BITS as usize + 1);
}
#[test]
fn test_reverse_index_bits() {
let mut arg = vec![10, 20, 30, 40];
reverse_slice_index_bits(&mut arg);
assert_eq!(arg, vec![10, 30, 20, 40]);
let mut input256: Vec<u64> = (0..256).collect();
#[rustfmt::skip]
let output256: Vec<u64> = vec![
0x00, 0x80, 0x40, 0xc0, 0x20, 0xa0, 0x60, 0xe0, 0x10, 0x90, 0x50, 0xd0, 0x30, 0xb0, 0x70, 0xf0,
0x08, 0x88, 0x48, 0xc8, 0x28, 0xa8, 0x68, 0xe8, 0x18, 0x98, 0x58, 0xd8, 0x38, 0xb8, 0x78, 0xf8,
0x04, 0x84, 0x44, 0xc4, 0x24, 0xa4, 0x64, 0xe4, 0x14, 0x94, 0x54, 0xd4, 0x34, 0xb4, 0x74, 0xf4,
0x0c, 0x8c, 0x4c, 0xcc, 0x2c, 0xac, 0x6c, 0xec, 0x1c, 0x9c, 0x5c, 0xdc, 0x3c, 0xbc, 0x7c, 0xfc,
0x02, 0x82, 0x42, 0xc2, 0x22, 0xa2, 0x62, 0xe2, 0x12, 0x92, 0x52, 0xd2, 0x32, 0xb2, 0x72, 0xf2,
0x0a, 0x8a, 0x4a, 0xca, 0x2a, 0xaa, 0x6a, 0xea, 0x1a, 0x9a, 0x5a, 0xda, 0x3a, 0xba, 0x7a, 0xfa,
0x06, 0x86, 0x46, 0xc6, 0x26, 0xa6, 0x66, 0xe6, 0x16, 0x96, 0x56, 0xd6, 0x36, 0xb6, 0x76, 0xf6,
0x0e, 0x8e, 0x4e, 0xce, 0x2e, 0xae, 0x6e, 0xee, 0x1e, 0x9e, 0x5e, 0xde, 0x3e, 0xbe, 0x7e, 0xfe,
0x01, 0x81, 0x41, 0xc1, 0x21, 0xa1, 0x61, 0xe1, 0x11, 0x91, 0x51, 0xd1, 0x31, 0xb1, 0x71, 0xf1,
0x09, 0x89, 0x49, 0xc9, 0x29, 0xa9, 0x69, 0xe9, 0x19, 0x99, 0x59, 0xd9, 0x39, 0xb9, 0x79, 0xf9,
0x05, 0x85, 0x45, 0xc5, 0x25, 0xa5, 0x65, 0xe5, 0x15, 0x95, 0x55, 0xd5, 0x35, 0xb5, 0x75, 0xf5,
0x0d, 0x8d, 0x4d, 0xcd, 0x2d, 0xad, 0x6d, 0xed, 0x1d, 0x9d, 0x5d, 0xdd, 0x3d, 0xbd, 0x7d, 0xfd,
0x03, 0x83, 0x43, 0xc3, 0x23, 0xa3, 0x63, 0xe3, 0x13, 0x93, 0x53, 0xd3, 0x33, 0xb3, 0x73, 0xf3,
0x0b, 0x8b, 0x4b, 0xcb, 0x2b, 0xab, 0x6b, 0xeb, 0x1b, 0x9b, 0x5b, 0xdb, 0x3b, 0xbb, 0x7b, 0xfb,
0x07, 0x87, 0x47, 0xc7, 0x27, 0xa7, 0x67, 0xe7, 0x17, 0x97, 0x57, 0xd7, 0x37, 0xb7, 0x77, 0xf7,
0x0f, 0x8f, 0x4f, 0xcf, 0x2f, 0xaf, 0x6f, 0xef, 0x1f, 0x9f, 0x5f, 0xdf, 0x3f, 0xbf, 0x7f, 0xff,
];
reverse_slice_index_bits(&mut input256[..]);
assert_eq!(input256, output256);
}
#[test]
fn test_apply_to_chunks_exact_fit() {
const CHUNK_SIZE: usize = 4;
let input: Vec<u8> = vec![1, 2, 3, 4, 5, 6, 7, 8];
let mut results: Vec<Vec<u8>> = Vec::new();
apply_to_chunks::<CHUNK_SIZE, _, _>(input, |chunk| {
results.push(chunk.to_vec());
});
assert_eq!(results, vec![vec![1, 2, 3, 4], vec![5, 6, 7, 8]]);
}
#[test]
fn test_apply_to_chunks_with_remainder() {
const CHUNK_SIZE: usize = 3;
let input: Vec<u8> = vec![1, 2, 3, 4, 5, 6, 7];
let mut results: Vec<Vec<u8>> = Vec::new();
apply_to_chunks::<CHUNK_SIZE, _, _>(input, |chunk| {
results.push(chunk.to_vec());
});
assert_eq!(results, vec![vec![1, 2, 3], vec![4, 5, 6], vec![7]]);
}
#[test]
fn test_apply_to_chunks_empty_input() {
const CHUNK_SIZE: usize = 4;
let input: Vec<u8> = vec![];
let mut results: Vec<Vec<u8>> = Vec::new();
apply_to_chunks::<CHUNK_SIZE, _, _>(input, |chunk| {
results.push(chunk.to_vec());
});
assert!(results.is_empty());
}
#[test]
fn test_apply_to_chunks_single_chunk() {
const CHUNK_SIZE: usize = 10;
let input: Vec<u8> = vec![1, 2, 3, 4, 5];
let mut results: Vec<Vec<u8>> = Vec::new();
apply_to_chunks::<CHUNK_SIZE, _, _>(input, |chunk| {
results.push(chunk.to_vec());
});
assert_eq!(results, vec![vec![1, 2, 3, 4, 5]]);
}
#[test]
fn test_apply_to_chunks_large_chunk_size() {
const CHUNK_SIZE: usize = 100;
let input: Vec<u8> = vec![1, 2, 3, 4, 5, 6, 7, 8];
let mut results: Vec<Vec<u8>> = Vec::new();
apply_to_chunks::<CHUNK_SIZE, _, _>(input, |chunk| {
results.push(chunk.to_vec());
});
assert_eq!(results, vec![vec![1, 2, 3, 4, 5, 6, 7, 8]]);
}
#[test]
fn test_apply_to_chunks_large_input() {
const CHUNK_SIZE: usize = 5;
let input: Vec<u8> = (1..=20).collect();
let mut results: Vec<Vec<u8>> = Vec::new();
apply_to_chunks::<CHUNK_SIZE, _, _>(input, |chunk| {
results.push(chunk.to_vec());
});
assert_eq!(
results,
vec![
vec![1, 2, 3, 4, 5],
vec![6, 7, 8, 9, 10],
vec![11, 12, 13, 14, 15],
vec![16, 17, 18, 19, 20]
]
);
}
#[test]
fn test_reverse_slice_index_bits_random() {
let lengths = [32, 128, 1 << 16];
let mut rng = SmallRng::seed_from_u64(1);
for _ in 0..32 {
for &length in &lengths {
let mut rand_list: Vec<u32> = Vec::with_capacity(length);
rand_list.resize_with(length, || rng.random());
let expect = reverse_index_bits_naive(&rand_list);
let mut actual = rand_list.clone();
reverse_slice_index_bits(&mut actual);
assert_eq!(actual, expect);
}
}
}
#[test]
fn test_log2_strict_usize_edge_cases() {
assert_eq!(log2_strict_usize(1), 0);
assert_eq!(log2_strict_usize(2), 1);
assert_eq!(log2_strict_usize(1 << 18), 18);
assert_eq!(log2_strict_usize(1 << 31), 31);
assert_eq!(
log2_strict_usize(1 << (usize::BITS - 1)),
usize::BITS as usize - 1
);
}
#[test]
fn test_checked_pow2() {
assert_eq!(checked_pow2(0), Some(1));
assert_eq!(checked_pow2(1), Some(2));
assert_eq!(checked_pow2(5), Some(32));
assert_eq!(checked_pow2(10), Some(1024));
assert_eq!(checked_pow2(20), Some(1_048_576));
let max_exp = usize::BITS as usize - 1;
assert_eq!(checked_pow2(max_exp), Some(1usize << max_exp));
assert_eq!(checked_pow2(usize::BITS as usize), None);
assert_eq!(checked_pow2(usize::BITS as usize + 1), None);
assert_eq!(checked_pow2(usize::MAX), None);
}
#[test]
fn test_checked_log_size_sum() {
assert_eq!(checked_log_size_sum(0, 0), Some((0, 1)));
assert_eq!(checked_log_size_sum(5, 0), Some((5, 32)));
assert_eq!(checked_log_size_sum(0, 10), Some((10, 1024)));
assert_eq!(checked_log_size_sum(10, 2), Some((12, 4096)));
assert_eq!(checked_log_size_sum(2, 10), Some((12, 4096)));
assert_eq!(checked_log_size_sum(20, 3), Some((23, 8_388_608)));
let almost_max = usize::BITS as usize - 2;
let max_exp = usize::BITS as usize - 1;
assert_eq!(
checked_log_size_sum(almost_max, 1),
Some((max_exp, 1usize << max_exp))
);
assert_eq!(checked_log_size_sum(max_exp, 1), None);
let half = usize::BITS as usize / 2;
let other_half = max_exp - half;
assert_eq!(
checked_log_size_sum(half, other_half),
Some((max_exp, 1usize << max_exp))
);
assert_eq!(checked_log_size_sum(usize::MAX, 1), None);
assert_eq!(checked_log_size_sum(usize::MAX, usize::MAX), None);
}
#[test]
#[should_panic]
fn test_log2_strict_usize_zero() {
let _ = log2_strict_usize(0);
}
#[test]
#[should_panic]
fn test_log2_strict_usize_nonpower_2() {
let _ = log2_strict_usize(0x78c341c65ae6d262);
}
#[test]
#[should_panic]
fn test_log2_strict_usize_max() {
let _ = log2_strict_usize(usize::MAX);
}
#[test]
fn test_log3_strict_powers_of_3() {
assert_eq!(log3_strict_usize(1), 0);
assert_eq!(log3_strict_usize(3), 1);
assert_eq!(log3_strict_usize(9), 2);
assert_eq!(log3_strict_usize(27), 3);
assert_eq!(log3_strict_usize(81), 4);
assert_eq!(log3_strict_usize(243), 5);
assert_eq!(log3_strict_usize(729), 6);
assert_eq!(log3_strict_usize(2187), 7);
assert_eq!(log3_strict_usize(6561), 8);
assert_eq!(log3_strict_usize(19683), 9);
assert_eq!(log3_strict_usize(59049), 10);
assert_eq!(log3_strict_usize(177_147), 11);
assert_eq!(log3_strict_usize(531_441), 12);
}
#[test]
#[should_panic(expected = "input must be non-zero")]
fn test_log3_strict_panics_on_zero() {
let _ = log3_strict_usize(0);
}
#[test]
#[should_panic(expected = "is not a power of 3")]
fn test_log3_strict_panics_on_non_power_of_3() {
let _ = log3_strict_usize(2);
}
#[test]
#[should_panic(expected = "is not a power of 3")]
fn test_log3_strict_panics_on_power_of_2() {
let _ = log3_strict_usize(8);
}
#[test]
#[should_panic(expected = "is not a power of 3")]
fn test_log3_strict_panics_on_product_with_other_primes() {
let _ = log3_strict_usize(6);
}
proptest! {
#[test]
fn test_log3_strict_roundtrip(k in 0u32..25u32) {
let n = 3usize.pow(k);
assert_eq!(log3_strict_usize(n), k as usize);
}
}
#[test]
fn test_log2_ceil_usize_comprehensive() {
assert_eq!(log2_ceil_usize(0), 0);
assert_eq!(log2_ceil_usize(1), 0);
assert_eq!(log2_ceil_usize(2), 1);
assert_eq!(log2_ceil_usize(1 << 18), 18);
assert_eq!(log2_ceil_usize(1 << 31), 31);
assert_eq!(
log2_ceil_usize(1 << (usize::BITS - 1)),
usize::BITS as usize - 1
);
assert_eq!(log2_ceil_usize(3), 2);
assert_eq!(log2_ceil_usize(0x14fe901b), 29);
assert_eq!(
log2_ceil_usize((1 << (usize::BITS - 1)) + 1),
usize::BITS as usize
);
assert_eq!(log2_ceil_usize(usize::MAX - 1), usize::BITS as usize);
assert_eq!(log2_ceil_usize(usize::MAX), usize::BITS as usize);
}
fn reverse_index_bits_naive<T: Copy>(arr: &[T]) -> Vec<T> {
let n = arr.len();
let n_power = log2_strict_usize(n);
let mut out = vec![None; n];
for (i, v) in arr.iter().enumerate() {
let dst = i.reverse_bits() >> (usize::BITS - n_power as u32);
out[dst] = Some(*v);
}
out.into_iter().map(|x| x.unwrap()).collect()
}
#[test]
fn test_relatively_prime_u64() {
assert!(!relatively_prime_u64(0, 0));
assert!(!relatively_prime_u64(10, 0));
assert!(!relatively_prime_u64(0, 10));
assert!(!relatively_prime_u64(0, 123456789));
assert!(relatively_prime_u64(1, 1));
assert!(!relatively_prime_u64(10, 10));
assert!(!relatively_prime_u64(99999, 99999));
assert!(!relatively_prime_u64(2, 4));
assert!(!relatively_prime_u64(16, 32));
assert!(!relatively_prime_u64(64, 128));
assert!(!relatively_prime_u64(1024, 4096));
assert!(!relatively_prime_u64(u64::MAX, u64::MAX));
assert!(!relatively_prime_u64(5, 10));
assert!(!relatively_prime_u64(12, 36));
assert!(!relatively_prime_u64(15, 45));
assert!(!relatively_prime_u64(100, 500));
assert!(relatively_prime_u64(17, 31));
assert!(relatively_prime_u64(97, 43));
assert!(relatively_prime_u64(7919, 65537));
assert!(relatively_prime_u64(15485863, 32452843));
assert!(relatively_prime_u64(13, 17));
assert!(relatively_prime_u64(101, 103));
assert!(relatively_prime_u64(1009, 1013));
assert!(!relatively_prime_u64(
190266297176832000,
10430732356495263744
));
assert!(!relatively_prime_u64(
2040134905096275968,
5701159354248194048
));
assert!(!relatively_prime_u64(
16611311494648745984,
7514969329383038976
));
assert!(!relatively_prime_u64(
14863931409971066880,
7911906750992527360
));
assert!(relatively_prime_u64(u64::MAX, 1));
assert!(relatively_prime_u64(u64::MAX, u64::MAX - 1));
assert!(!relatively_prime_u64(u64::MAX, u64::MAX));
}
}