use crate::quantum::{fxmul, inv_sqrt2, Amp, Circuit, StateVector, FRAC, ONE};
fn splitmix64(s: &mut u64) -> u64 {
*s = s.wrapping_add(0x9E37_79B9_7F4A_7C15);
let mut z = *s;
z = (z ^ (z >> 30)).wrapping_mul(0xBF58_476D_1CE4_E5B9);
z = (z ^ (z >> 27)).wrapping_mul(0x94D0_49BB_1331_11EB);
z ^ (z >> 31)
}
fn coin(s: &mut u64) -> bool {
splitmix64(s) >> 63 == 1
}
fn prepare(bit: bool, x_basis: bool) -> StateVector {
let mut c = Circuit::new(1);
if bit {
c.x(0);
}
if x_basis {
c.h(0);
}
c.simulate().expect("one qubit")
}
fn measure(sv: &StateVector, x_basis: bool, rng: &mut u64) -> bool {
let (a0, a1) = (sv.amps[0], sv.amps[1]);
let p0 = if x_basis {
let inv = inv_sqrt2();
let h0 = Amp { re: fxmul(inv, a0.re + a1.re), im: fxmul(inv, a0.im + a1.im) };
(h0.norm2() >> FRAC) as i64
} else {
(a0.norm2() >> FRAC) as i64
};
let draw = (splitmix64(rng) >> 11) as i64 % ONE;
draw >= p0
}
#[derive(Clone, Copy, Debug)]
pub struct Bb84 {
pub rounds: u32,
pub sifted: u32,
pub errors: u32,
pub qber_ppm: u32,
pub eavesdropper: bool,
}
pub fn bb84(rounds: u32, seed: u64, eavesdropper: bool) -> Bb84 {
let mut rng = seed ^ 0xB1B8_4A55_1234_9E37;
let (mut sifted, mut errors) = (0u32, 0u32);
for _ in 0..rounds {
let a_bit = coin(&mut rng);
let a_basis = coin(&mut rng);
let mut state = prepare(a_bit, a_basis);
if eavesdropper {
let e_basis = coin(&mut rng);
let e_bit = measure(&state, e_basis, &mut rng);
state = prepare(e_bit, e_basis);
}
let b_basis = coin(&mut rng);
let b_bit = measure(&state, b_basis, &mut rng);
if a_basis == b_basis {
sifted += 1;
if a_bit != b_bit {
errors += 1;
}
}
}
let qber_ppm = if sifted == 0 { 0 } else { ((errors as u64 * 1_000_000) / sifted as u64) as u32 };
Bb84 { rounds, sifted, errors, qber_ppm, eavesdropper }
}
pub fn clone_fidelity(x_basis_input: bool) -> f64 {
let mut attempt = Circuit::new(2);
if x_basis_input {
attempt.h(0);
}
attempt.cx(0, 1);
let got = attempt.simulate().expect("two qubits");
let mut ideal = Circuit::new(2);
if x_basis_input {
ideal.h(0);
ideal.h(1);
}
let want = ideal.simulate().expect("two qubits");
got.fidelity_fx(&want) as f64 / ONE as f64
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
#[ignore]
fn probe_bb84() {
println!("\n BB84, 20000 rounds");
for eve in [false, true] {
let r = bb84(20000, 7, eve);
println!(" eavesdropper={:<5} sifted {:>6} errors {:>6} QBER {:>7.4}",
eve, r.sifted, r.errors, r.qber_ppm as f64 / 1e6);
}
println!("\n cloning fidelity");
println!(" |0> (basis state) {:.6}", clone_fidelity(false));
println!(" |+> (superposition) {:.6}", clone_fidelity(true));
}
#[test]
fn a_quiet_channel_yields_a_perfect_key() {
let r = bb84(4000, 7, false);
assert_eq!(r.errors, 0, "no eavesdropper must mean no errors, got {}", r.errors);
assert_eq!(r.qber_ppm, 0);
let frac = r.sifted as f64 / r.rounds as f64;
assert!((frac - 0.5).abs() < 0.05, "sifted fraction {frac}, want ~1/2");
}
#[test]
fn intercept_resend_shows_up_as_a_quarter_of_the_key() {
let r = bb84(6000, 11, true);
let qber = r.qber_ppm as f64 / 1.0e6;
assert!(
(qber - 0.25).abs() < 0.03,
"intercept-resend QBER should sit at 1/4, got {qber} ({} of {})",
r.errors,
r.sifted
);
}
#[test]
fn runs_are_reproducible() {
let a = bb84(500, 99, true);
let b = bb84(500, 99, true);
assert_eq!((a.sifted, a.errors), (b.sifted, b.errors));
let c = bb84(500, 100, true);
assert!((a.sifted, a.errors) != (c.sifted, c.errors) || a.rounds != c.rounds);
}
#[test]
fn cloning_works_on_basis_states_and_fails_on_superpositions() {
let basis = clone_fidelity(false);
let superposed = clone_fidelity(true);
assert!(basis > 0.999, "|0> should clone perfectly, got {basis}");
assert!(
superposed < 0.6,
"|+> must NOT clone; a CNOT entangles instead, got fidelity {superposed}"
);
}
}