use crossbeam_skiplist::SkipMap;
use dashu::integer::IBig;
use num_traits::{One, Zero};
pub struct SymmetricGroupCache {
c000598: SkipMap<usize, IBig>,
c000642: SkipMap<usize, IBig>,
}
impl Default for SymmetricGroupCache {
fn default() -> Self {
let c000598 = SkipMap::new();
c000598.insert(0, IBig::one());
Self {
c000598,
c000642: Default::default(),
}
}
}
fn cycle_index<F>(n: usize, f: &F, k: usize) -> IBig
where F: Fn(usize) -> IBig
{
if n == 0 {
return if k == 0 { IBig::one() } else { IBig::zero() };
}
if n == 1 {
return f(k);
}
let mut r = IBig::zero();
for i in 1..=n {
let mut sum = IBig::zero();
for j in (0..=k).step_by(i) {
sum += f(j / i) * cycle_index(n - i, f, k - j);
}
r += sum;
}
r / n
}
impl SymmetricGroupCache {
pub fn a000598(&self, k: usize) -> IBig {
match self.c000598.get(&k) {
Some(s) => {
s.value().clone()
}
None => {
let new = cycle_index(3, &|k| self.a000598(k), k - 1);
self.c000598.insert(k, new.clone());
new
}
}
}
pub fn a000642(&self, k: usize) -> IBig {
match self.c000642.get(&k) {
Some(s) => {
s.value().clone()
}
None => {
let new = cycle_index(2, &|k| self.a000598(k), k);
self.c000642.insert(k, new.clone());
new
}
}
}
pub fn a000631(&self, k: usize) -> IBig {
cycle_index(2, &|k| self.a000642(k), k)
}
pub fn a000602(&self, k: usize) -> IBig {
self.a000642(k) + if k % 2 == 1 { self.a000642((k - 1) / 2) } else { IBig::zero() } - self.a000631(k - 1)
}
}