use std::ops::{Add, Mul, Neg, Sub};
use crate::{
number_theory::modint::{M107, M998, MOD107, MOD998},
utils::num_traits::{One, Zero},
};
pub trait NumProbability<Rhs = Self, Output = Self>:
Clone
+ Copy
+ From<usize>
+ Neg<Output = Output>
+ Add<Rhs, Output = Output>
+ Sub<Rhs, Output = Output>
+ Mul<Rhs, Output = Output>
+ Mul<usize, Output = Output>
+ Zero
+ One
+ PartialEq
{
const MOD: usize;
}
impl NumProbability for M107 {
const MOD: usize = MOD107 as usize;
}
impl NumProbability for M998 {
const MOD: usize = MOD998 as usize;
}
pub struct Comb<N: NumProbability> {
fac: Vec<N>,
finv: Vec<N>,
}
impl<N: NumProbability> Comb<N> {
pub fn new(max_size: usize) -> Self {
let mut fac = vec![N::one(); max_size];
let mut finv = vec![N::one(); max_size];
let mut inv = vec![N::one(); max_size];
for i in 2..max_size {
fac[i] = fac[i - 1] * i;
inv[i] = -N::from(N::MOD / i) * inv[N::MOD % i];
finv[i] = finv[i - 1] * inv[i];
}
Comb { fac, finv }
}
pub fn comb(&self, n: usize, r: usize) -> N {
if n < r {
return 0.into();
}
self.fac[n] * self.finv[r] * self.finv[n - r]
}
pub fn perm(&self, n: usize, r: usize) -> N {
if n < r {
return 0.into();
}
self.fac[n] * self.finv[n - r]
}
pub fn comb_with_rep(&self, n: usize, r: usize) -> N {
self.comb(n + r - 1, n)
}
}