use crate::pauli_sum::hash::B_MAX_BITS;
fn highest_bit(v: u32) -> u32 {
debug_assert!(v != 0, "highest_bit: zero has no highest set bit");
31 - v.leading_zeros()
}
fn pext(value: u32, mask: u32) -> u32 {
let mut out = 0u32;
let mut out_bit = 0u32;
let mut remaining = mask;
while remaining != 0 {
let bit = remaining & remaining.wrapping_neg();
if value & bit != 0 {
out |= 1 << out_bit;
}
out_bit += 1;
remaining ^= bit;
}
out
}
#[derive(Clone, Debug)]
pub(crate) struct Gf2Span {
basis: Vec<u32>,
pivot_mask: u32,
bits: u8,
nonpivot_mask: u32,
}
impl Gf2Span {
pub(crate) fn new(deltas: &[u32], bits: u8) -> Self {
assert!(bits <= B_MAX_BITS, "Gf2Span: bits {bits} exceeds B_MAX_BITS {B_MAX_BITS}");
let space_mask = if bits == 0 { 0 } else { (1u32 << bits) - 1 };
let mut basis: Vec<u32> = Vec::new();
let mut pivot_mask = 0u32;
for &delta in deltas {
assert!(
delta & !space_mask == 0,
"Gf2Span: delta {delta} has bits outside the {bits}-bit bucket space"
);
let mut reduced = delta;
for &vector in &basis {
if reduced & (1 << highest_bit(vector)) != 0 {
reduced ^= vector;
}
}
if reduced == 0 {
continue;
}
let pivot = highest_bit(reduced);
for vector in basis.iter_mut() {
if *vector & (1 << pivot) != 0 {
*vector ^= reduced;
}
}
let position = basis.partition_point(|&vector| highest_bit(vector) < pivot);
basis.insert(position, reduced);
pivot_mask |= 1 << pivot;
}
Self { basis, pivot_mask, bits, nonpivot_mask: space_mask & !pivot_mask }
}
pub(crate) fn dimension(&self) -> usize {
self.basis.len()
}
pub(crate) fn coset_size(&self) -> usize {
1usize << self.basis.len()
}
pub(crate) fn num_cosets(&self) -> usize {
(1usize << self.bits) >> self.basis.len()
}
fn is_representative(&self, beta: u32) -> bool {
beta & self.pivot_mask == 0
}
pub(crate) fn representative_of(&self, beta: u32) -> u32 {
debug_assert!(
beta >> self.bits == 0,
"Gf2Span::representative_of: beta outside the bucket space"
);
let mut reduced = beta;
for &vector in &self.basis {
if reduced & (1 << highest_bit(vector)) != 0 {
reduced ^= vector;
}
}
reduced
}
pub(crate) fn coordinate_of(&self, delta: u32) -> u32 {
debug_assert!(
self.representative_of(delta) == 0,
"Gf2Span::coordinate_of: delta {delta} is not in the span"
);
pext(delta, self.pivot_mask)
}
fn rank_of_representative(&self, representative: u32) -> u32 {
debug_assert!(
self.is_representative(representative),
"Gf2Span::rank_of_representative: {representative} is not a representative"
);
pext(representative, self.nonpivot_mask)
}
pub(crate) fn permuted_index(&self, beta: u32) -> u32 {
let representative = self.representative_of(beta);
(self.rank_of_representative(representative) << self.basis.len())
| self.coordinate_of(beta ^ representative)
}
}
#[derive(Clone, Debug, Default)]
pub(crate) struct CosetOrder {
permutation: Vec<u32>,
inverse: Vec<u32>,
positions: u32,
dimension: u32,
}
impl CosetOrder {
pub(crate) fn rebuild(&mut self, span: &Gf2Span, with_inverse: bool) {
self.positions = 1u32 << span.bits;
self.dimension = span.dimension() as u32;
self.permutation.clear();
self.inverse.clear();
if self.dimension == 0 {
return;
}
self.permutation.extend((0..self.positions).map(|beta| span.permuted_index(beta)));
if with_inverse {
self.inverse.resize(self.permutation.len(), 0);
for (beta, &position) in self.permutation.iter().enumerate() {
self.inverse[position as usize] = beta as u32;
}
}
}
pub(crate) fn is_identity(&self) -> bool {
self.dimension == 0
}
pub(crate) fn position_of(&self, beta: u32) -> u32 {
if self.permutation.is_empty() {
beta
} else {
self.permutation[beta as usize]
}
}
pub(crate) fn bucket_at(&self, position: u32) -> u32 {
debug_assert!(
self.inverse.len() == self.permutation.len(),
"CosetOrder::bucket_at: the inverse was not built",
);
if self.inverse.is_empty() {
position
} else {
self.inverse[position as usize]
}
}
pub(crate) fn positions(&self) -> usize {
self.positions as usize
}
pub(crate) fn dimension(&self) -> usize {
self.dimension as usize
}
#[cfg(test)]
pub(crate) fn capacity(&self) -> usize {
self.permutation.capacity() + self.inverse.capacity()
}
}
#[cfg(test)]
mod tests;