1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
//! 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
//! [`Elem`] over GF(2) as [`Binary<1, Polynomial<3>>`](Binary).
//!
//! ```
//! use fgf::bits;
//! use fgf::field::FieldElem;
//! // 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);
//! ```
use crate;
use cratekernel;
/// Number of bytes needed to hold `bits` packed elements.
///
/// ```text
/// bits 0 1 8 9 64 65
/// bytes 0 1 1 2 8 9
/// ```
pub const
/// `dst ^= src`: GF(2) addition over whole buffers.
///
/// XOR of zero padding stays zero, so no bit count is needed. Buffers must
/// not alias.
///
/// # Panics
/// Panics if the slices differ in length.
/// `dst ^= src` over the bit range `[from, to)`.
///
/// Bits outside the range — including padding — are untouched. `dst` and
/// `src` must not alias.
///
/// The checked one-shot form of [`xor_range_with`]: an operation applied
/// once pays one construction; an operation applied to many buffer pairs
/// over the same range should prepare a [`XorRange`] instead.
///
/// # Panics
/// Panics if the slices differ in length, if `from > to`, if `to > bits`,
/// or if the buffers are shorter than [`bytes_for`]`(bits)`.
/// A prepared bit range for repeated `dst ^= src`: the window and end
/// masks of `[from, to)` derived once, applied to many buffer pairs.
///
/// The prepared form of [`xor_range`], in the same split [`crate::ops`]
/// uses for prepared coefficients: an elimination that XORs the same
/// column range into many rows pays the range checks and mask arithmetic
/// once per range instead of once per row. An empty range is valid and
/// [`xor_range_with`] applies it as a no-op.
/// The plan is independent of the buffers it is applied to: buffers may be
/// longer than the planned window (the surplus is padding like any other),
/// and bits outside the window are untouched.
///
/// That is the flexibility the prepared form buys over the one-shot
/// [`xor_range`]: the one-shot validates `from <= to <= bits` against the
/// logical bit count of every call, while a prepared range is validated
/// once — against its own `[from, to)` at construction — and then applies
/// to any buffer pair covering its window, whatever each buffer's logical
/// length. A caller folding the same column range into many equal-length
/// rows derives the window and masks exactly once.
/// `dst ^= src` over a prepared [`XorRange`]: the per-row apply of the
/// split form of [`xor_range`].
///
/// Bits outside the planned window — including padding — are untouched.
/// `dst` and `src` must not alias. The buffers may differ in length; each
/// must cover the planned window.
///
/// # Panics
///
/// Panics if either buffer is shorter than the planned window.
/// `dst = a & b`: elementwise GF(2) multiplication.
///
/// AND of zero padding stays zero, so no bit count is needed. The output
/// buffers may alias neither input.
///
/// # Panics
/// Panics if the slices differ in length.
/// `dst &= src`: in-place elementwise GF(2) multiplication.
///
/// # Panics
/// Panics if the slices differ in length.
/// `dst &= !mask`: clears every bit of `dst` where `mask` has a 1.
///
/// The mask's padding is irrelevant; zero bits of `dst` stay zero, so the
/// padding invariant holds regardless.
///
/// # Panics
/// Panics if the slices differ in length.
/// Zeros the bit range `[from, to)` of `dst`.
///
/// # Panics
/// Panics if `from > to`, if `to > bits`, or if `dst` is shorter than
/// [`bytes_for`]`(bits)`.
/// Sets the bit range `[from, to)` of `dst` to one.
///
/// # Panics
/// Panics if `from > to`, if `to > bits`, or if `dst` is shorter than
/// [`bytes_for`]`(bits)`.
/// Hamming weight of the live bits `[0, bits)`.
///
/// Padding bits never count, even when the buffer holds more bytes than
/// [`bytes_for`]`(bits)`. The count is `usize`, which no buffer can
/// overflow; the underlying `u32` popcount kernel is exact only while a
/// segment holds at most `2^32 − 1` set bits, so the count is summed over
/// byte-aligned segments that each stay inside that range.
///
/// # Panics
/// Panics if `buf` is shorter than [`bytes_for`]`(bits)`.
/// The GF(2) inner product of two packed vectors: the parity of
/// `popcount(a & b)` over the live bits.
///
/// Returned as an [`Elem`] over GF(2) as [`Binary<1, Polynomial<3>>`](Binary); use
/// [`is_one`](crate::field::FieldElem::is_one) to test it. Padding bits
/// contribute nothing.
///
/// # Panics
/// Panics if the slices differ in length or are shorter than
/// [`bytes_for`]`(bits)`.
/// `dst ^= srcs[i]` for every set bit `i` of `selector`: the combination
/// step of subset-XOR over a dense row set.
///
/// An empty selector is a no-op. `dst` must not alias any source.
///
/// # Panics
/// Panics if any source differs in length from `dst`, if `selector` names a
/// source at or beyond `srcs.len()`, or if `dst` is shorter than
/// [`bytes_for`]`(bits)`.