#![forbid(unsafe_code)]
use crate::entropy::rank::{comb, factorial, multinomial};
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum Coordinate {
Combination {
n: u64,
k: u64,
},
Multinomial {
n: u64,
m: usize,
},
Factoradic {
m: u32,
},
}
impl Coordinate {
pub fn space(&self) -> Option<u128> {
match self {
Coordinate::Combination { n, k } => comb(*n as u128, *k as u128),
Coordinate::Multinomial { n, m } => multinomial(*n, &vec![1; *m]),
Coordinate::Factoradic { m } => factorial(*m as u128),
}
}
pub fn bits(&self) -> Option<u64> {
let space = self.space()?;
let bits = 128 - space.leading_zeros() as u64;
Some(bits)
}
pub fn bytes(&self) -> Option<u64> {
self.bits().map(|b| b.div_ceil(8).max(1))
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn combination_coordinate() {
let c = Coordinate::Combination { n: 65536, k: 2 };
assert_eq!(c.space(), comb(65536, 2));
assert_eq!(c.bits(), Some(31));
assert_eq!(c.bytes(), Some(4));
}
#[test]
fn multinomial_coordinate() {
let c = Coordinate::Multinomial { n: 10, m: 10 };
assert_eq!(c.space(), factorial(10));
}
#[test]
fn factoradic_coordinate() {
let c = Coordinate::Factoradic { m: 34 };
assert_eq!(c.space(), factorial(34));
assert!(c.space().is_some());
let too_big = Coordinate::Factoradic { m: 35 };
assert!(too_big.space().is_none());
}
}