use num_bigint::BigInt;
use num_rational::Ratio;
use num_traits::{One, Zero};
use parking_lot::Mutex;
use crate::base::numeric::Q;
static CACHE: Mutex<Vec<Q>> = Mutex::new(Vec::new());
fn tangent_numbers(n: usize) -> Vec<BigInt> {
let mut t: Vec<BigInt> = vec![BigInt::zero(); n + 1];
if n == 0 {
return t;
}
t[1] = BigInt::one();
for k in 2..=n {
t[k] = &t[k - 1] * BigInt::from(k - 1);
}
for k in 2..=n {
for j in k..=n {
let v = &t[j - 1] * BigInt::from(j - k) + &t[j] * BigInt::from(j - k + 2);
t[j] = v;
}
}
t
}
pub(crate) fn even(k: usize) -> Q {
if k == 0 {
return Q::one();
}
let mut cache = CACHE.lock();
if cache.len() < k {
let n = k.max(2 * cache.len()).max(32);
let t = tangent_numbers(n);
let mut out = Vec::with_capacity(n);
for (i, tk) in t.iter().enumerate().skip(1) {
let two_k = 2 * i;
let p = BigInt::one() << two_k;
let den = &p * (&p - BigInt::one());
let mut num = tk * BigInt::from(two_k);
if i % 2 == 0 {
num = -num;
}
out.push(Ratio::new(num, den));
}
*cache = out;
}
cache[k - 1].clone()
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn matches_the_rational_recurrence() {
let q = |n: i64, d: i64| Ratio::new(BigInt::from(n), BigInt::from(d));
assert_eq!(even(1), q(1, 6));
assert_eq!(even(2), q(-1, 30));
assert_eq!(even(3), q(1, 42));
assert_eq!(even(6), q(691, -2730));
for k in 1..=60 {
assert_eq!(
even(k),
crate::base::bernoulli::bernoulli(2 * k),
"B_{}",
2 * k
);
}
}
}