use std::hash::{Hash, Hasher};
use std::collections::hash_map::DefaultHasher;
use std::ops::Range;
pub fn log2_floor(n: u64) -> u32 {
63 - n.leading_zeros()
}
pub fn binomial_coefficient(n: u64, mut k: u64) -> u64 {
if k > n - k {
k = n - k;
}
let mut result: u64 = 1;
for value in 1..(k + 1) {
result *= n + 1 - value;
result /= value;
}
result
}
pub fn hash_single<T>(value: T) -> u64 where T: Hash {
let mut hasher = DefaultHasher::new();
value.hash(&mut hasher);
hasher.finish()
}
pub const E_COCHAIN: [usize; 1] = [0b0];
pub const O_COCHAIN: [usize; 1] = [0b1];
pub const A_COCHAIN: [usize; 2] = [0b00, 0b11];
pub const B_COCHAIN: [usize; 2] = [0b01, 0b10];
pub fn generate_cochain(range: Range<usize>, prefixes: &[usize]) -> Vec<usize> {
let mask = (1 << prefixes.len()) - 1;
let mut output = Vec::new();
for idx in range {
for &prefix in prefixes {
if (idx & mask) == prefix {
output.push(idx);
}
}
}
output
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn correct_binomial_low_edge_values() {
assert_eq!(binomial_coefficient(0, 0), 1);
assert_eq!(binomial_coefficient(1, 0), 1);
assert_eq!(binomial_coefficient(1, 1), 1);
assert_eq!(binomial_coefficient(2, 0), 1);
assert_eq!(binomial_coefficient(2, 1), 2);
assert_eq!(binomial_coefficient(2, 2), 1);
assert_eq!(binomial_coefficient(3, 0), 1);
assert_eq!(binomial_coefficient(3, 1), 3);
assert_eq!(binomial_coefficient(3, 2), 3);
assert_eq!(binomial_coefficient(3, 3), 1);
assert_eq!(binomial_coefficient(4, 0), 1);
assert_eq!(binomial_coefficient(4, 1), 4);
assert_eq!(binomial_coefficient(4, 2), 6);
assert_eq!(binomial_coefficient(4, 3), 4);
assert_eq!(binomial_coefficient(4, 4), 1);
assert_eq!(binomial_coefficient(5, 0), 1);
assert_eq!(binomial_coefficient(5, 1), 5);
assert_eq!(binomial_coefficient(5, 2), 10);
assert_eq!(binomial_coefficient(5, 3), 10);
assert_eq!(binomial_coefficient(5, 4), 5);
assert_eq!(binomial_coefficient(5, 5), 1);
assert_eq!(binomial_coefficient(6, 0), 1);
assert_eq!(binomial_coefficient(6, 1), 6);
assert_eq!(binomial_coefficient(6, 2), 15);
assert_eq!(binomial_coefficient(6, 3), 20);
assert_eq!(binomial_coefficient(6, 4), 15);
assert_eq!(binomial_coefficient(6, 5), 6);
assert_eq!(binomial_coefficient(6, 6), 1);
assert_eq!(binomial_coefficient(7, 0), 1);
assert_eq!(binomial_coefficient(7, 1), 7);
assert_eq!(binomial_coefficient(7, 2), 21);
assert_eq!(binomial_coefficient(7, 3), 35);
assert_eq!(binomial_coefficient(7, 4), 35);
assert_eq!(binomial_coefficient(7, 5), 21);
assert_eq!(binomial_coefficient(7, 6), 7);
assert_eq!(binomial_coefficient(7, 7), 1);
}
#[test]
fn log2_floor_correct_powers_of_2() {
assert_eq!(log2_floor(1), 0);
assert_eq!(log2_floor(2), 1);
assert_eq!(log2_floor(4), 2);
assert_eq!(log2_floor(8), 3);
assert_eq!(log2_floor(16), 4);
assert_eq!(log2_floor(32), 5);
assert_eq!(log2_floor(64), 6);
assert_eq!(log2_floor(128), 7);
assert_eq!(log2_floor(256), 8);
assert_eq!(log2_floor(512), 9);
assert_eq!(log2_floor(1024), 10);
assert_eq!(log2_floor(2048), 11);
assert_eq!(log2_floor(4096), 12);
assert_eq!(log2_floor(8192), 13);
assert_eq!(log2_floor(16384), 14);
assert_eq!(log2_floor(32768), 15);
assert_eq!(log2_floor(65536), 16);
}
#[test]
fn check_even_cochains() {
let chain_1 = generate_cochain(0..20, &E_COCHAIN);
assert!(chain_1.iter().all(|&x| x % 2 == 0));
assert_eq!(chain_1.len(), 10);
let chain_2 = generate_cochain(0..1, &E_COCHAIN);
assert!(chain_2.iter().all(|&x| x % 2 == 0));
assert_eq!(chain_2.len(), 1);
let chain_3 = generate_cochain(0..13, &E_COCHAIN);
assert!(chain_3.iter().all(|&x| x % 2 == 0));
assert_eq!(chain_3.len(), 7);
}
#[test]
fn check_odd_cochain() {
let chain_1 = generate_cochain(0..20, &O_COCHAIN);
assert!(chain_1.iter().all(|&x| x % 2 != 0));
assert_eq!(chain_1.len(), 10);
let chain_2 = generate_cochain(0..1, &O_COCHAIN);
assert!(chain_2.iter().all(|&x| x % 2 != 0));
assert_eq!(chain_2.len(), 0);
let chain_3 = generate_cochain(0..13, &O_COCHAIN);
assert!(chain_3.iter().all(|&x| x % 2 != 0));
assert_eq!(chain_3.len(), 6);
}
#[test]
#[allow(non_snake_case)]
fn check_A_cochain() {
let chain_1 = generate_cochain(0..20, &A_COCHAIN);
assert_eq!(chain_1, &[0, 3, 4,7, 8, 11, 12, 15, 16, 19]);
let chain_1 = generate_cochain(0..1, &A_COCHAIN);
assert_eq!(chain_1, &[0]);
let chain_1 = generate_cochain(0..14, &A_COCHAIN);
assert_eq!(chain_1, &[0, 3, 4, 7, 8, 11, 12]);
}
#[test]
#[allow(non_snake_case)]
fn check_B_cochain() {
let chain_1 = generate_cochain(0..20, &B_COCHAIN);
assert_eq!(chain_1, &[1, 2, 5, 6, 9, 10, 13, 14, 17, 18]);
let chain_1 = generate_cochain(0..1, &B_COCHAIN);
assert_eq!(chain_1, &[]);
let chain_1 = generate_cochain(0..14, &B_COCHAIN);
assert_eq!(chain_1, &[1, 2, 5, 6, 9, 10, 13]);
}
}