use crate::descriptor::{SpaceDescriptor, from_nat, to_nat};
use crate::error::RankAdapterError;
use crate::metric;
use num_bigint::BigUint;
use sim_lib_discrete_comb::{bit_vector_rank, bit_vector_unrank, subset_rank, subset_unrank};
use sim_lib_rank::Nat;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct BitVectorSpace {
pub width: usize,
}
impl BitVectorSpace {
pub fn descriptor(&self) -> SpaceDescriptor {
SpaceDescriptor {
id: "rank/discrete/bit-vector",
version: 1,
params: vec![("width", self.width.to_string())],
order: "natural-binary",
metric: "hamming",
}
}
pub fn cardinality(&self) -> Nat {
to_nat(BigUint::from(1u32) << self.width)
}
pub fn rank(&self, bits: &[bool]) -> Result<Nat, RankAdapterError> {
if bits.len() != self.width {
return Err(RankAdapterError::Invalid(format!(
"bit vector length {} != width {}",
bits.len(),
self.width
)));
}
Ok(to_nat(bit_vector_rank(bits)))
}
pub fn unrank(&self, ordinal: &Nat) -> Result<Vec<bool>, RankAdapterError> {
Ok(bit_vector_unrank(&from_nat(ordinal), self.width))
}
pub fn distance(&self, a: &Nat, b: &Nat) -> Result<Nat, RankAdapterError> {
Ok(to_nat(metric::hamming_bits(
&self.unrank(a)?,
&self.unrank(b)?,
)))
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct SubsetSpace {
pub n: usize,
}
impl SubsetSpace {
pub fn descriptor(&self) -> SpaceDescriptor {
SpaceDescriptor {
id: "rank/discrete/subset",
version: 1,
params: vec![("n", self.n.to_string())],
order: "bitmask",
metric: "symmetric-difference",
}
}
pub fn cardinality(&self) -> Nat {
to_nat(BigUint::from(1u32) << self.n)
}
pub fn rank(&self, members: &[usize]) -> Result<Nat, RankAdapterError> {
Ok(to_nat(subset_rank(members, self.n)?))
}
pub fn unrank(&self, ordinal: &Nat) -> Result<Vec<usize>, RankAdapterError> {
Ok(subset_unrank(&from_nat(ordinal), self.n))
}
pub fn distance(&self, a: &Nat, b: &Nat) -> Result<Nat, RankAdapterError> {
Ok(to_nat(metric::symmetric_difference(
&self.unrank(a)?,
&self.unrank(b)?,
)))
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn bit_vector_round_trips_and_measures() {
let space = BitVectorSpace { width: 4 };
for i in 0..16u32 {
let ord = to_nat(BigUint::from(i));
let bits = space.unrank(&ord).unwrap();
assert_eq!(space.rank(&bits).unwrap(), ord);
}
let d = space
.distance(&to_nat(BigUint::from(3u32)), &to_nat(BigUint::from(5u32)))
.unwrap();
assert_eq!(d, to_nat(BigUint::from(2u32)));
assert_eq!(space.cardinality(), to_nat(BigUint::from(16u32)));
}
#[test]
fn subset_round_trips() {
let space = SubsetSpace { n: 5 };
for i in 0..32u32 {
let ord = to_nat(BigUint::from(i));
let members = space.unrank(&ord).unwrap();
assert_eq!(space.rank(&members).unwrap(), ord);
}
}
#[test]
fn subset_symmetric_difference() {
let space = SubsetSpace { n: 4 };
let a = space.rank(&[0, 1, 2]).unwrap();
let b = space.rank(&[1, 2, 3]).unwrap();
assert_eq!(space.distance(&a, &b).unwrap(), to_nat(BigUint::from(2u32)));
}
}