use crate::groups::multiplier::ToLittleEndianBytes;
use num_bigint::BigUint;
pub fn compute_base_2w_expansion(bytes: &[u8], bits_per_digit: usize) -> Vec<usize> {
assert!(0 < bits_per_digit && bits_per_digit <= usize::BITS as usize);
let mut digits = Vec::new();
let n = bytes.len();
let digits_count = div_ceil(8 * n, bits_per_digit);
for i in 0..digits_count {
digits.push(get_bits_from_bytes(
bytes,
bits_per_digit * i,
bits_per_digit * (i + 1),
));
}
digits
}
#[inline]
fn get_lendian_from_substring(byte: &u8, start: usize, end: usize) -> u8 {
assert!(start <= end);
if start > 7 {
return 0;
} else if end > 8 {
return get_lendian_from_substring(byte, start, 8);
}
byte >> start & ((1 << (end - start)) - 1) as u8
}
pub(crate) fn div_ceil(numerator: usize, denominator: usize) -> usize {
assert!(denominator > 0);
if numerator == 0 {
return 0;
}
1 + ((numerator - 1) / denominator)
}
#[inline]
pub fn get_bits_from_bytes(bytes: &[u8], start: usize, end: usize) -> usize {
assert!(start <= end && start < 8 * bytes.len());
let mut result: usize = 0;
let mut bits_added = 0;
let mut current_bit = start % 8;
let mut current_byte = start / 8;
while bits_added < end - start && current_byte < bytes.len() {
let remaining_bits = end - start - bits_added;
let (bits_to_read, next_byte, next_bit) = if remaining_bits < 8 - current_bit {
(remaining_bits, current_byte, current_bit + remaining_bits)
} else {
(8 - current_bit, current_byte + 1, 0)
};
result += (get_lendian_from_substring(
&bytes[current_byte],
current_bit,
current_bit + bits_to_read,
) as usize)
<< bits_added;
bits_added += bits_to_read;
current_bit = next_bit;
current_byte = next_byte;
}
result
}
#[inline]
pub fn test_bit(bytes: &[u8], index: usize) -> bool {
if index >= 8 * bytes.len() {
return false;
}
let byte = index >> 3;
let shifted = bytes[byte] >> (index & 7);
shifted & 1 != 0
}
pub const fn log2(x: usize) -> usize {
(usize::BITS - x.leading_zeros() - 1) as usize
}
pub fn is_power_of_2(x: usize) -> bool {
if x == 0 {
return false;
}
x & (x - 1) == 0
}
impl ToLittleEndianBytes for BigUint {
fn to_le_bytes(&self) -> Vec<u8> {
self.to_bytes_le()
}
}
#[cfg(test)]
mod tests {
use super::*;
use std::assert_eq;
#[test]
fn test_lendian_from_substring() {
let byte = 0b00000001;
assert_eq!(0, get_lendian_from_substring(&byte, 0, 0));
assert_eq!(1, get_lendian_from_substring(&byte, 0, 1));
assert_eq!(1, get_lendian_from_substring(&byte, 0, 3));
assert_eq!(1, get_lendian_from_substring(&byte, 0, 8));
assert_eq!(0, get_lendian_from_substring(&byte, 1, 8));
let byte = 0b00000011;
assert_eq!(1, get_lendian_from_substring(&byte, 0, 1));
assert_eq!(3, get_lendian_from_substring(&byte, 0, 2));
assert_eq!(3, get_lendian_from_substring(&byte, 0, 3));
assert_eq!(1, get_lendian_from_substring(&byte, 1, 8));
assert_eq!(0, get_lendian_from_substring(&byte, 2, 8));
let byte = 0b10000001;
assert_eq!(1, get_lendian_from_substring(&byte, 0, 1));
assert_eq!(1, get_lendian_from_substring(&byte, 0, 7));
assert_eq!(129, get_lendian_from_substring(&byte, 0, 8));
assert_eq!(64, get_lendian_from_substring(&byte, 1, 8));
assert_eq!(16, get_lendian_from_substring(&byte, 3, 8));
assert_eq!(129, get_lendian_from_substring(&byte, 0, 100));
assert_eq!(1, get_lendian_from_substring(&byte, 7, 8));
assert_eq!(0, get_lendian_from_substring(&byte, 8, 8));
}
#[test]
fn test_base_2w_expansion() {
let value: u128 = 123812341234567;
let bytes = value.to_le_bytes();
let expansion = compute_base_2w_expansion(&bytes, 8);
assert_eq!(
bytes.to_vec(),
expansion.iter().map(|x| *x as u8).collect::<Vec<u8>>()
);
for window_size in 1..=64 {
let expansion = compute_base_2w_expansion(&bytes, window_size);
let mut sum = 0u128;
for (i, value) in expansion.iter().enumerate() {
sum += (1 << (window_size * i)) * *value as u128;
}
assert_eq!(value, sum);
}
}
#[test]
fn test_bits_form_bytes() {
let bytes = [0b00000001, 0b00000011, 0b10000001];
assert_eq!(0, get_bits_from_bytes(&bytes, 0, 0));
assert_eq!(1, get_bits_from_bytes(&bytes, 0, 1));
assert_eq!(3, get_bits_from_bytes(&bytes, 8, 10));
assert_eq!(1, get_bits_from_bytes(&bytes, 16, 17));
assert_eq!(0, get_bits_from_bytes(&bytes, 17, 23));
assert_eq!(1, get_bits_from_bytes(&bytes, 23, 100));
}
#[test]
fn test_is_power_of_two() {
assert!(!is_power_of_2(0));
assert!(is_power_of_2(1));
assert!(is_power_of_2(2));
assert!(!is_power_of_2(3));
assert!(is_power_of_2(4));
assert!(!is_power_of_2(511));
assert!(is_power_of_2(512));
assert!(!is_power_of_2(513));
assert!(is_power_of_2(4096));
}
}