use num::{Integer, PrimInt};
pub fn binom<T: PrimInt + Integer>(n: T, k: T) -> T {
if k < T::zero() || k > n {
return T::zero();
}
let mut res = T::one();
let mut i = T::zero();
while i < k {
res = res * (n - i) / (i + T::one());
i = i + T::one();
}
res
}
pub fn multiset_encode<I: PrimInt, O: PrimInt>(items: &[I]) -> O {
let mut idx = 0usize;
for (i, a) in items.iter().map(|x| x.to_usize().unwrap()).enumerate() {
idx = idx + binom(a + i, i + 1);
}
O::from::<usize>(idx).unwrap()
}
pub fn multiset_decode<I: PrimInt, O: PrimInt>(idx: I, n: O, k: O, items: &mut [O]) {
let mut idx = idx.to_usize().unwrap();
let n = n.to_usize().unwrap();
let k = k.to_usize().unwrap();
let mut a = n + k - 1usize;
let mut pos = k - 1;
for i in (1..=k).rev() {
while binom(a, i) > idx {
a -= 1;
}
items[pos] = O::from::<usize>(a - pos).unwrap();
if i == 1 {
break;
}
idx -= binom(a, i);
a -= 1;
pos -= 1;
}
}
#[cfg(test)]
mod tests {
use itertools::Itertools;
use super::*;
#[test]
fn test_multiset_encdec() {
let n = 13;
let k = 5;
for seq in (0..n).combinations_with_replacement(k) {
let mut seq = seq.iter().map(|x| *x as usize).collect::<Vec<usize>>();
seq.sort();
let idx: usize = multiset_encode(&seq);
let mut decoded = vec![0; k];
multiset_decode(idx, n, k, &mut decoded);
println!("seq: {:?} => {:?} => {:?}", seq, idx, decoded);
assert_eq!(seq, decoded);
}
for idx in 0..binom(n+k-1, k) {
let mut seq = vec![0; k];
multiset_decode(idx, n, k, &mut seq);
let idx_redo: usize = multiset_encode(&seq);
println!("idx: {:?} => {:?} => {:?}", idx, seq, idx_redo);
assert_eq!(idx, idx_redo);
}
}
}