use super::adjacency::Adjacency;
use rand::distributions::WeightedIndex;
use rand::rngs::ThreadRng;
use rand::{thread_rng, Rng};
pub struct Generator<R: Rng = ThreadRng> {
maximal_bit_degree: usize,
bit_degrees: Vec<usize>,
adjacency: Adjacency,
active_bits: Vec<usize>,
distribution: Vec<f64>,
target_check_degree: usize,
check_degree_condition: CheckDegreeCondition,
random_number_generator: R,
}
impl Generator<ThreadRng> {
pub fn new() -> Self {
Self {
maximal_bit_degree: 1,
bit_degrees: Vec::new(),
adjacency: Adjacency::new(),
active_bits: Vec::new(),
distribution: Vec::new(),
target_check_degree: 2,
check_degree_condition: CheckDegreeCondition::MustBeFullDegree,
random_number_generator: thread_rng(),
}
}
pub fn with_n_bits(n_bits: usize) -> Self {
let mut generator = Self::new()
.initialize_bit_degrees(n_bits)
.initialize_adjacency(n_bits);
generator.set_over_all_bits().set_uniform_distribution();
generator
}
fn initialize_bit_degrees(mut self, n_bits: usize) -> Self {
self.bit_degrees = vec![0; n_bits];
self
}
fn initialize_adjacency(mut self, n_bits: usize) -> Self {
self.adjacency = Adjacency::with_n_bits(n_bits);
self
}
}
impl<R: Rng> Generator<R> {
pub fn with_random_number_generator<S: Rng>(self, rng: S) -> Generator<S> {
Generator {
maximal_bit_degree: 0,
bit_degrees: self.bit_degrees,
adjacency: self.adjacency,
active_bits: self.active_bits,
distribution: self.distribution,
target_check_degree: self.target_check_degree,
check_degree_condition: self.check_degree_condition,
random_number_generator: rng,
}
}
pub fn set_minimal_girth(&mut self, minimal_girth: usize) -> &mut Self {
let depth = if minimal_girth >= 2 {
(minimal_girth - 2) / 2
} else {
0
};
self.adjacency.set_recursion_depth(depth);
self
}
pub fn set_maximal_bit_degree(&mut self, degree: usize) -> &mut Self {
self.maximal_bit_degree = degree;
self
}
pub fn set_over_all_bits(&mut self) -> &mut Self {
self.active_bits = (0..self.get_n_bits()).collect();
self
}
pub fn set_over_bits(&mut self, mut bits: Vec<usize>) -> &mut Self {
bits.sort();
bits.dedup();
self.active_bits = bits;
self
}
pub fn set_without_bits(&mut self, bits: Vec<usize>) -> &mut Self {
bits.into_iter().for_each(|bit| {
if let Some(index) = self.active_bits.iter().position(|b| *b == bit) {
self.active_bits.swap_remove(index);
}
});
self
}
pub fn set_distribution(&mut self, distribution: Vec<f64>) -> &mut Self {
if distribution.len() != self.get_n_bits() {
panic!("wrong number of probabilities");
}
if distribution.iter().any(|prob| *prob < 0.0) {
panic!("there are some negative probabilities");
}
self.distribution = distribution;
self
}
pub fn set_uniform_distribution(&mut self) -> &mut Self {
self.distribution = vec![1.0 / self.get_n_bits() as f64; self.get_n_bits()];
self
}
pub fn set_target_check_degree(&mut self, degree: usize) -> &mut Self {
self.target_check_degree = degree;
self
}
pub fn allow_checks_of_degree_at_least(&mut self, degree: usize) -> &mut Self {
self.check_degree_condition = CheckDegreeCondition::MustBeAtLeastOfDegree(degree);
self
}
pub fn allow_only_check_of_target_degree(&mut self) -> &mut Self {
self.check_degree_condition = CheckDegreeCondition::MustBeFullDegree;
self
}
pub fn get_bits_adjacent_to(&self, bit: usize) -> Vec<usize> {
self.adjacency.get_bits_adjacent_to(bit)
}
pub fn get_n_bits(&self) -> usize {
self.bit_degrees.len()
}
pub fn get_random_check(&mut self) -> Option<Vec<usize>> {
let mut candidate_check = self.get_candidate_check();
if self.is_valid_check(&candidate_check) {
candidate_check.sort();
self.update_from_check(&candidate_check);
Some(candidate_check)
} else {
None
}
}
fn get_candidate_check(&mut self) -> Vec<usize> {
let mut check = Vec::with_capacity(self.target_check_degree);
for _ in 0..self.target_check_degree {
self.add_random_bit_to_check(&mut check);
}
check
}
fn add_random_bit_to_check(&mut self, check: &mut Vec<usize>) {
self.get_random_bit_generator_for_check(check)
.add_random_bit_to_check(check);
}
fn get_random_bit_generator_for_check(&mut self, check: &[usize]) -> RandomBitGenerator<R> {
let availables = self.get_available_bits_for_check(check);
let distribution = self.get_distribution_over(&availables);
RandomBitGenerator {
availables,
distribution,
random_number_generator: &mut self.random_number_generator,
}
}
fn get_available_bits_for_check(&self, check: &[usize]) -> Vec<usize> {
self.active_bits
.iter()
.filter(|bit| self.is_available(**bit))
.filter(|bit| self.is_not_adjacent_to_check(bit, check))
.cloned()
.collect()
}
fn is_available(&self, bit: usize) -> bool {
self.bit_degrees[bit] < self.maximal_bit_degree
}
fn is_not_adjacent_to_check(&self, bit: &usize, check: &[usize]) -> bool {
check.iter().all(|b| self.are_not_adjacent(*b, bit))
}
fn are_not_adjacent(&self, bit_0: usize, bit_1: &usize) -> bool {
!self.get_bits_adjacent_to(bit_0).contains(bit_1)
}
fn get_distribution_over(&self, bits: &[usize]) -> Vec<f64> {
bits.iter().map(|bit| self.distribution[*bit]).collect()
}
fn is_valid_check(&self, check: &[usize]) -> bool {
match self.check_degree_condition {
CheckDegreeCondition::MustBeAtLeastOfDegree(min_degree) => check.len() >= min_degree,
CheckDegreeCondition::MustBeFullDegree => check.len() == self.target_check_degree,
}
}
fn update_from_check(&mut self, check: &[usize]) {
self.update_degrees_from_check(check);
self.update_adjacency_from_check(check);
}
fn update_degrees_from_check(&mut self, check: &[usize]) {
check.iter().for_each(|bit| self.bit_degrees[*bit] += 1);
}
fn update_adjacency_from_check(&mut self, check: &[usize]) {
self.adjacency.update_from_check(check)
}
}
enum CheckDegreeCondition {
MustBeFullDegree,
MustBeAtLeastOfDegree(usize),
}
struct RandomBitGenerator<'a, R: Rng> {
availables: Vec<usize>,
distribution: Vec<f64>,
random_number_generator: &'a mut R,
}
impl<'a, R: Rng> RandomBitGenerator<'a, R> {
fn add_random_bit_to_check(self, check: &mut Vec<usize>) -> Self {
if let Ok(distribution) = WeightedIndex::new(&self.distribution) {
let sample = self.random_number_generator.sample(distribution);
check.push(self.availables[sample]);
}
self
}
}
#[cfg(test)]
mod test {
use super::*;
use rand::SeedableRng;
use rand_chacha::ChaCha8Rng;
#[test]
fn an_empty_generator_does_not_generate_checks() {
let mut generator = Generator::new();
assert!(generator
.set_target_check_degree(2)
.get_random_check()
.is_none());
}
#[test]
fn a_generator_does_not_generate_checks_of_degree_less_than_minimal_check_degree() {
let mut generator = Generator::with_n_bits(5);
generator.allow_checks_of_degree_at_least(2);
assert!(generator
.set_target_check_degree(0)
.get_random_check()
.is_none());
assert!(generator
.set_target_check_degree(1)
.get_random_check()
.is_none());
assert!(generator
.set_target_check_degree(2)
.get_random_check()
.is_some());
}
#[test]
fn a_generator_does_not_include_the_same_bit_twice_in_a_check() {
let mut generator = Generator::with_n_bits(3);
generator.set_maximal_bit_degree(2).set_minimal_girth(4);
let first_check = generator
.set_over_bits(vec![0, 1])
.set_target_check_degree(2)
.get_random_check();
assert_eq!(first_check, Some(vec![0, 1]));
let second_check = generator.set_target_check_degree(3).get_random_check();
assert_eq!(second_check, None);
}
#[test]
fn non_full_check_can_be_set_to_be_allowed() {
let mut generator = Generator::with_n_bits(3);
generator
.allow_checks_of_degree_at_least(2)
.set_maximal_bit_degree(2)
.set_minimal_girth(4)
.set_target_check_degree(2);
let first_check = generator.set_over_bits(vec![0, 1]).get_random_check();
let second_check = generator.set_over_bits(vec![1, 2]).get_random_check();
assert_eq!(first_check, Some(vec![0, 1]));
assert_eq!(second_check, Some(vec![1, 2]));
let third_check = generator
.set_over_all_bits()
.set_target_check_degree(3)
.get_random_check();
assert_eq!(third_check, Some(vec![0, 2]));
}
#[test]
fn a_generator_does_not_exceed_bit_maximal_degree() {
let mut generator = Generator::with_n_bits(3);
generator
.set_maximal_bit_degree(2)
.set_target_check_degree(2);
let first_check = generator.set_without_bits(vec![2]).get_random_check();
assert_eq!(first_check, Some(vec![0, 1]));
let second_check = generator
.set_over_all_bits()
.set_without_bits(vec![0])
.get_random_check();
assert_eq!(second_check, Some(vec![1, 2]));
let third_check = generator
.set_over_all_bits()
.set_target_check_degree(3)
.get_random_check();
assert_eq!(third_check, None);
let fourth_check = generator.set_target_check_degree(2).get_random_check();
assert_eq!(fourth_check, Some(vec![0, 2]));
assert_eq!(generator.get_random_check(), None);
}
#[test]
fn a_generator_does_not_create_cycle_of_length_two_and_four_if_minimal_girth_is_six() {
let mut generator =
Generator::with_n_bits(3).with_random_number_generator(ChaCha8Rng::seed_from_u64(10));
generator.set_maximal_bit_degree(2).set_minimal_girth(6);
let first_check = generator.set_target_check_degree(3).get_random_check();
assert_eq!(first_check, Some(vec![0, 1, 2]));
let second_check = generator.set_target_check_degree(2).get_random_check();
assert_eq!(second_check, None);
let third_check = generator.set_target_check_degree(1).get_random_check();
assert_eq!(third_check.is_some(), true);
}
#[test]
fn a_generator_does_not_create_cycle_of_length_two_four_and_six_if_minimal_girth_is_eight() {
let mut generator =
Generator::with_n_bits(5).with_random_number_generator(ChaCha8Rng::seed_from_u64(10));
generator.set_maximal_bit_degree(2).set_minimal_girth(8);
let first_check = generator
.set_over_bits(vec![0, 1, 2])
.set_target_check_degree(3)
.get_random_check();
assert_eq!(first_check, Some(vec![0, 1, 2]));
generator.set_target_check_degree(2);
let second_check = generator.set_over_bits(vec![2, 3]).get_random_check();
assert_eq!(second_check, Some(vec![2, 3]));
let third_check = generator.set_over_bits(vec![0, 3]).get_random_check();
assert_eq!(third_check, None);
let fourth_check = generator.set_over_bits(vec![0, 3, 4]).get_random_check();
assert_eq!(fourth_check.clone().unwrap().contains(&4), true);
assert_eq!(fourth_check.unwrap().len(), 2);
}
#[test]
fn a_generator_can_generate_check_according_to_a_bit_distribution() {
let mut generator =
Generator::with_n_bits(5).with_random_number_generator(ChaCha8Rng::seed_from_u64(10));
generator
.set_maximal_bit_degree(2)
.set_distribution(vec![0.25, 0.25, 0.0, 0.25, 0.25])
.set_over_bits(vec![0, 1, 2])
.set_target_check_degree(3);
assert_eq!(generator.get_random_check(), None);
generator.set_target_check_degree(2);
assert_eq!(generator.get_random_check(), Some(vec![0, 1]));
assert_eq!(generator.get_random_check(), Some(vec![0, 1]));
assert_eq!(generator.get_random_check(), None);
generator.set_over_all_bits();
assert_eq!(generator.get_random_check(), Some(vec![3, 4]));
generator.set_target_check_degree(3);
assert_eq!(generator.get_random_check(), None);
generator.set_uniform_distribution();
assert_eq!(generator.get_random_check(), Some(vec![2, 3, 4]));
}
}