use crate::{Gate, Measurement, QuantumChannel, QuantumState, errors::StateError};
pub struct BB84Result {
pub raw_length: usize,
pub total_sifted: usize,
pub check_errors: usize,
pub qber: f64,
pub eve_detected_count: usize,
pub established_key: Vec<bool>,
pub alice_bits: Vec<bool>,
pub alice_bases: Vec<bool>,
pub bob_bases: Vec<bool>,
pub bob_results: Vec<bool>,
}
pub fn run(
num_qubits: usize,
channel: &QuantumChannel,
eve_ratio: f64,
check_ratio: f64,
) -> Result<BB84Result, StateError> {
let mut alice_bits = Vec::with_capacity(num_qubits);
let mut alice_bases = Vec::with_capacity(num_qubits);
let mut bob_bases = Vec::with_capacity(num_qubits);
let mut bob_results = Vec::with_capacity(num_qubits);
let mut eve_intercepted_count = 0;
for _ in 0..num_qubits {
let a_bit = crate::rng::random_bool(0.5);
let a_basis = crate::rng::random_bool(0.5);
let mut state = QuantumState::new(1);
if a_bit {
state.apply(&Gate::x(), &[0])?;
}
if a_basis {
state.apply(&Gate::h(), &[0])?;
}
state.apply_channel(channel, &[0])?;
if eve_ratio > 1e-12 && crate::rng::random_bool(eve_ratio) {
eve_intercepted_count += 1;
let e_basis = crate::rng::random_bool(0.5);
let measurement = if e_basis {
Measurement::x_basis()
} else {
Measurement::z_basis()
};
let _ = state.measure(&measurement, &[0])?;
}
let b_basis = crate::rng::random_bool(0.5);
let measurement = if b_basis {
Measurement::x_basis()
} else {
Measurement::z_basis()
};
let res = state.measure(&measurement, &[0])?;
let b_val = res.index == 1;
alice_bits.push(a_bit);
alice_bases.push(a_basis);
bob_bases.push(b_basis);
bob_results.push(b_val);
}
let input_indices: Vec<usize> = (0..num_qubits).collect();
let mut match_indices: Vec<usize> = input_indices
.into_iter()
.filter(|&i| alice_bases[i] == bob_bases[i])
.collect();
let total_sifted = match_indices.len();
crate::rng::shuffle_slice(&mut match_indices);
let num_check = (total_sifted as f64 * check_ratio).round() as usize;
let (check_indices, key_indices) = match_indices.split_at(num_check);
let mut check_errors = 0;
for &i in check_indices {
if alice_bits[i] != bob_results[i] {
check_errors += 1;
}
}
let qber = if num_check > 0 {
check_errors as f64 / num_check as f64
} else {
0.0
};
let mut established_key = Vec::with_capacity(key_indices.len());
for &i in key_indices {
established_key.push(alice_bits[i]);
}
Ok(BB84Result {
raw_length: num_qubits,
total_sifted,
check_errors,
qber,
eve_detected_count: eve_intercepted_count,
established_key,
alice_bits,
alice_bases,
bob_bases,
bob_results,
})
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_bb84_noiseless() {
let channel = QuantumChannel::bit_flip(0.0);
let result = run(100, &channel, 0.0, 0.5).unwrap();
assert_eq!(result.raw_length, 100);
assert_eq!(result.check_errors, 0);
assert_eq!(result.qber, 0.0);
assert_eq!(result.eve_detected_count, 0);
}
#[test]
fn test_bb84_eve() {
let channel = QuantumChannel::bit_flip(0.0);
let result = run(100, &channel, 1.0, 0.5).unwrap();
assert!(result.eve_detected_count > 0);
assert!(result.qber > 0.0);
}
#[test]
fn test_bb84_zero_check() {
let channel = QuantumChannel::bit_flip(0.0);
let result = run(100, &channel, 0.0, 0.0).unwrap();
assert_eq!(result.qber, 0.0);
}
}