use super::fpr::Fpr;
pub(crate) trait SamplerRng {
fn next_bytes(&mut self, buf: &mut [u8]);
}
const RCDT: [u128; 18] = [
3024686241123004913666,
1564742784480091954050,
636254429462080897535,
199560484645026482916,
47667343854657281903,
8595902006365044063,
1163297957344668388,
117656387352093658,
8867391802663976,
496969357462633,
20680885154299,
638331848991,
14602316184,
247426747,
3104126,
28824,
198,
1,
];
const C: [u64; 13] = [
0x0000_0004_7411_83A3,
0x0000_0036_548C_FC06,
0x0000_024F_DCBF_140A,
0x0000_171D_939D_E045,
0x0000_D00C_F58F_6F84,
0x0006_8068_1CF7_96E3,
0x002D_82D8_305B_0FEA,
0x0111_1111_0E06_6FD0,
0x0555_5555_5507_0F00,
0x1555_5555_5581_FF00,
0x4000_0000_0002_B400,
0x7FFF_FFFF_FFFF_4800,
0x8000_0000_0000_0000,
];
#[allow(clippy::approx_constant)]
const ILN2: Fpr = Fpr::from_f64(1.442_695_040_89);
#[allow(clippy::approx_constant)]
const LN2: Fpr = Fpr::from_f64(0.693_147_180_56);
const TWO63: Fpr = Fpr::from_f64(9_223_372_036_854_775_808.0);
const MAX_SIGMA: Fpr = Fpr::from_f64(1.8205);
#[inline]
fn inv_2sigma2() -> Fpr {
Fpr::from_f64(1.0).div(MAX_SIGMA.double().mul(MAX_SIGMA))
}
fn base_sampler<R: SamplerRng>(rng: &mut R) -> i64 {
let mut buf = [0u8; 9];
rng.next_bytes(&mut buf); let mut u: u128 = 0;
for (i, &byte) in buf.iter().enumerate() {
u |= (byte as u128) << (8 * i); }
let mut z0: i64 = 0;
for &elt in &RCDT {
z0 += (u < elt) as i64;
}
z0
}
fn approx_exp(x: Fpr, ccs: Fpr) -> u64 {
let z = x.mul(TWO63).trunc() as u64; let mut y = C[0];
for &elt in &C[1..] {
y = elt.wrapping_sub((((z as u128) * (y as u128)) >> 63) as u64);
}
let z2 = ((ccs.mul(TWO63).trunc() as u64) << 1) as u128; (((z2) * (y as u128)) >> 63) as u64
}
fn ber_exp<R: SamplerRng>(x: Fpr, ccs: Fpr, rng: &mut R) -> bool {
let s_full = x.mul(ILN2).trunc();
let r = x.sub(Fpr::of_i64(s_full).mul(LN2));
let s = s_full.clamp(0, 63) as u32;
let z = approx_exp(r, ccs).wrapping_sub(1) >> s;
let mut i: i32 = 56;
while i >= 0 {
let mut b = [0u8; 1];
rng.next_bytes(&mut b);
let w = (b[0] as i32) - (((z >> i) & 0xFF) as i32);
if w != 0 {
return w < 0;
}
i -= 8;
}
false
}
pub(crate) fn sampler_z<R: SamplerRng>(mu: Fpr, sigma: Fpr, sigmin: Fpr, rng: &mut R) -> i64 {
let s = mu.floor();
let r = mu.sub(Fpr::of_i64(s));
let dss = Fpr::from_f64(1.0).div(sigma.double().mul(sigma));
let ccs = sigmin.div(sigma);
let inv2s2 = inv_2sigma2();
loop {
let z0 = base_sampler(rng);
let mut sign = [0u8; 1];
rng.next_bytes(&mut sign);
let b = (sign[0] & 1) as i64;
let z = b + (2 * b - 1) * z0;
let zr = Fpr::of_i64(z).sub(r);
let x = zr.mul(zr).mul(dss).sub(Fpr::of_i64(z0 * z0).mul(inv2s2));
if ber_exp(x, ccs, rng) {
return z + s;
}
}
}
#[cfg(test)]
#[path = "sampler_tests.rs"]
mod sampler_tests;