const fn low_mask(bits: u32) -> u64 {
if bits == 64 {
u64::MAX
} else {
(1u64 << bits) - 1
}
}
const fn assert_tower_width(bits: u32) {
assert!(bits.is_power_of_two() && bits <= 64);
}
#[must_use]
pub const fn mul_generator(value: u64, bits: u32) -> u64 {
assert_tower_width(bits);
if bits == 1 {
return value & 1;
}
let half = bits / 2;
let mask = low_mask(half);
let constant = value & mask;
let coefficient = (value >> half) & mask;
coefficient | ((constant ^ mul_generator(coefficient, half)) << half)
}
#[must_use]
pub const fn multiply(lhs: u64, rhs: u64, bits: u32) -> u64 {
assert_tower_width(bits);
if bits == 1 {
return lhs & rhs & 1;
}
let half = bits / 2;
let mask = low_mask(half);
let lhs_constant = lhs & mask;
let lhs_coefficient = (lhs >> half) & mask;
let rhs_constant = rhs & mask;
let rhs_coefficient = (rhs >> half) & mask;
let constant_product = multiply(lhs_constant, rhs_constant, half);
let outer_product = multiply(lhs_constant, rhs_coefficient, half);
let inner_product = multiply(lhs_coefficient, rhs_constant, half);
let coefficient_product = multiply(lhs_coefficient, rhs_coefficient, half);
let constant = constant_product ^ coefficient_product;
let coefficient = outer_product ^ inner_product ^ mul_generator(coefficient_product, half);
constant | (coefficient << half)
}
#[allow(dead_code)]
#[must_use]
pub const fn square(value: u64, bits: u32) -> u64 {
multiply(value, value, bits)
}
#[allow(dead_code)]
#[must_use]
pub const fn invert(value: u64, bits: u32) -> u64 {
assert_tower_width(bits);
if value & low_mask(bits) == 0 {
return 0;
}
if bits == 1 {
return 1;
}
let mut exponent = if bits == 64 {
u64::MAX - 1
} else {
(1u64 << bits) - 2
};
let mut base = value & low_mask(bits);
let mut result = 1;
while exponent != 0 {
if exponent & 1 != 0 {
result = multiply(result, base, bits);
}
base = square(base, bits);
exponent >>= 1;
}
result
}
#[allow(dead_code)]
fn decode(bytes: &[u8]) -> u64 {
let mut encoded = [0u8; 8];
encoded[..bytes.len()].copy_from_slice(bytes);
u64::from_le_bytes(encoded)
}
#[allow(dead_code)]
fn encode(bytes: &mut [u8], value: u64) {
bytes.copy_from_slice(&value.to_le_bytes()[..bytes.len()]);
}
#[allow(dead_code)]
fn element_bytes(bits: u32) -> usize {
assert!(matches!(bits, 8 | 16 | 32 | 64));
usize::try_from(bits / 8).expect("tower width fits usize")
}
#[allow(dead_code)]
pub fn mul_add(dst: &mut [u8], coefficient: u64, src: &[u8], bits: u32) {
let bytes = element_bytes(bits);
assert_eq!(dst.len(), src.len());
assert_eq!(dst.len() % bytes, 0);
for (dst_element, src_element) in dst.chunks_exact_mut(bytes).zip(src.chunks_exact(bytes)) {
let value = decode(dst_element) ^ multiply(decode(src_element), coefficient, bits);
encode(dst_element, value);
}
}
#[allow(dead_code)]
pub fn mul_assign(dst: &mut [u8], coefficient: u64, bits: u32) {
let bytes = element_bytes(bits);
assert_eq!(dst.len() % bytes, 0);
for element in dst.chunks_exact_mut(bytes) {
let value = multiply(decode(element), coefficient, bits);
encode(element, value);
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn published_basis_vectors_match() {
assert_eq!(multiply(0b10, 0b11, 2), 0b01);
assert_eq!(square(0x10, 8), 0x41);
assert_eq!(invert(0b0100, 4), 0b0110);
assert_eq!(invert(0x10, 8), 0x14);
}
#[test]
fn every_level_satisfies_its_defining_quadratic() {
for bits in [2, 4, 8, 16, 32, 64] {
let half = bits / 2;
let generator = 1u64 << half;
let subfield_generator = if half == 1 { 1 } else { 1u64 << (half / 2) };
assert_eq!(square(generator, bits), 1 | (subfield_generator << half));
}
}
}