Sample p uniformly with τ non-zero coefficients in {Q-1, 1} using SHAKE-256. This creates a "ball" polynomial with exactly tau non-zero ±1 coefficients. The polynomial will be normalized with coefficients in {0, 1, Q-1}. FIPS 204: SampleInBall (Algorithm 18)
fn sampleInBall(comptime tau: u16, seed: []const u8) Poly
fn sampleInBall(comptime tau: u16, seed: []const u8) Poly {
var p = Poly.zero;
var buf: [sha3.Shake256.block_length]u8 = undefined; // SHAKE-256 rate is 136 bytes
var h = sha3.Shake256.init(.{});
h.update(seed);
h.squeeze(&buf);
// Extract signs from first 8 bytes
var signs: u64 = 0;
for (0..8) |j| {
signs |= @as(u64, buf[j]) << @intCast(j * 8);
}
var buf_off: usize = 8;
// Generate tau non-zero coefficients using Fisher-Yates shuffle
// Start with N-tau zeros, then add tau ±1 values
var i: u16 = N - tau;
while (i < N) : (i += 1) {
var b: u16 = undefined;
// Find location using rejection sampling
while (true) {
if (buf_off >= buf.len) {
h.squeeze(&buf);
buf_off = 0;
}
b = buf[buf_off];
buf_off += 1;
if (b <= i) {
break;
}
}
// Shuffle: move existing value to position i
p.cs[i] = p.cs[b];
// Set position b to ±1 based on sign bit
p.cs[b] = 1;
const sign_bit: u1 = @truncate(signs);
const mask = bitMask(u32, sign_bit);
p.cs[b] ^= mask & (1 | (Q - 1));
signs >>= 1;
}
return p;
}