Skip to main content

Module bits

Module bits 

Source
Expand description

Bit-packed GF(2) buffers: one element per bit.

GF(2) is the one field whose natural element width is sub-byte, so it does not fit the byte-per-element crate::ops surface. Instead, elements are packed eight per byte and the operations here are whole-buffer or masked bit ranges over plain &[u8]:

  • element i lives in bit i % 8 (LSB-first) of byte i / 8;
  • a word is the little-endian u64 assembly of eight bytes, so element i is bit i % 64 of word i / 64;
  • bits past the logical length are padding. The invariant is that they are zero; it is caller-maintained. Range operations leave bits outside their range untouched. Whole-buffer operations also process padding and preserve zero padding when their inputs satisfy the invariant. No operation promises to sanitize nonzero padding.

Because a byte holds eight elements and the last byte may be partial, the element count is not recoverable from the byte length: operations whose meaning depends on the exact bit count take an explicit bits, and every range operation checks from <= to <= bits. Buffers may be longer than bytes_for(bits) — a consumer with word-aligned storage passes its whole row and the surplus bits are padding like any other.

Multiplying by a coefficient is masked out of the API on purpose: the coefficient of GF(2) is a bit — multiply by zero is “skip”, by one is “XOR” — so there is no prepared-coefficient form and no crate::ops counterpart. What is prepared is geometry: a XorRange derives a bit range’s window and masks once for xor_range_with to apply to many buffer pairs. The scalar oracle for this surface is crate::gf2::Elem.

use fgf::bits;
use fgf::field::Elem;
// Seven elements: 1,0,0,1,1,1,1 -> LSB-first byte 0b0111_1001.
let mut a = [0u8; 1];
bits::set_range(&mut a, 7, 0, 7);
bits::clear_range(&mut a, 7, 1, 3);
assert_eq!(a[0], 0b0111_1001);
assert_eq!(bits::weight(&a, 7), 5);

// The GF(2) inner product of a vector with itself is its weight mod 2.
let parity = bits::dot_product(&a, &a, 7);
assert!(parity.is_one());

// Whole-buffer addition is XOR of equal-length buffers.
let b = [0xff];
bits::xor_assign(&mut a, &b);
assert_eq!(bits::weight(&a, 7), 2);

Structs§

XorRange
A prepared bit range for repeated dst ^= src: the window and end masks of [from, to) derived once, applied to many buffer pairs.

Functions§

and_assign
dst &= src: in-place elementwise GF(2) multiplication.
and_into
dst = a & b: elementwise GF(2) multiplication.
andnot_assign
dst &= !mask: clears every bit of dst where mask has a 1.
bytes_for
Number of bytes needed to hold bits packed elements.
clear_range
Zeros the bit range [from, to) of dst.
dot_product
The GF(2) inner product of two packed vectors: the parity of popcount(a & b) over the live bits.
set_range
Sets the bit range [from, to) of dst to one.
weight
Hamming weight of the live bits [0, bits).
xor_assign
dst ^= src: GF(2) addition over whole buffers.
xor_gather
dst ^= srcs[i] for every set bit i of selector: the combination step of subset-XOR over a dense row set.
xor_range
dst ^= src over the bit range [from, to).
xor_range_with
dst ^= src over a prepared XorRange: the per-row apply of the split form of xor_range.