use ndarray::Array1;
use num_complex::Complex;
use super::FMVol;
use crate::fourier_malliavin::coefficients::convolution_coefficients;
use crate::traits::FloatExt;
const BIAS_CORRECTED_C_M: f64 = 0.05;
pub(super) fn default_bias_corrected_m<T: FloatExt>(mesh: T) -> usize {
let scaled = T::from_f64_fast(BIAS_CORRECTED_C_M) / mesh.sqrt();
scaled.floor().to_usize().unwrap_or(usize::MAX).max(1)
}
impl<T: FloatExt> FMVol<T> {
pub(super) fn resolve_m(&self, m: Option<usize>) -> usize {
m.unwrap_or((self.n_freq as f64).sqrt() as usize)
}
pub(super) fn resolve_m_volvol(&self, m: Option<usize>) -> usize {
m.unwrap_or((self.n_freq as f64).powf(0.4) as usize)
}
pub(super) fn resolve_m_volvol_bc(&self, m: Option<usize>) -> usize {
m.unwrap_or_else(|| default_bias_corrected_m(self.mesh))
}
pub(super) fn resolve_l_volvol_bc(&self, l: Option<usize>, big_m: usize) -> usize {
l.unwrap_or_else(|| {
let asymptotic = (self.n_freq as f64).powf(0.25).floor().max(1.0) as usize;
asymptotic.min(big_m.saturating_mul(2))
})
}
pub(super) fn compute_bias_correction_constant(&self, big_m: usize) -> T {
let two = T::from_f64_fast(2.0);
let three = T::from_f64_fast(3.0);
let a = two * T::from_usize_(self.n_freq) * self.mesh / self.period;
let r = a - a.floor();
let eta = if a.abs() > T::epsilon() {
r * (T::one() - r) / (two * a * a)
} else {
T::zero()
};
let m = T::from_usize_(big_m);
m * m * self.mesh / (three * self.period) * (T::one() + two * eta)
}
pub(super) fn bias_corrected_volvol_coefficients(
&self,
big_m: usize,
big_l: usize,
) -> Array1<Complex<T>> {
let mm = big_m + big_l;
let c_v = self.vol_coeffs(mm);
let k_const = self.compute_bias_correction_constant(big_m);
let scale = T::from_f64_fast(std::f64::consts::TAU.powi(2)) / self.period;
let m_plus_1 = T::from_usize_(big_m + 1);
let mut corrected = Array1::<Complex<T>>::zeros(2 * big_l + 1);
for (j, k) in (-(big_l as i64)..=(big_l as i64)).enumerate() {
let mut derivative_sum = Complex::<T>::new(T::zero(), T::zero());
let mut quarticity_sum = Complex::<T>::new(T::zero(), T::zero());
for h in -(big_m as i64)..=(big_m as i64) {
let product = c_v[(mm as i64 + h) as usize] * c_v[(mm as i64 + k - h) as usize];
let fejer = T::one() - T::from_usize_(h.unsigned_abs() as usize) / m_plus_1;
let derivative = T::from_f64_fast(h as f64 * (h - k) as f64);
derivative_sum = derivative_sum + product * fejer * derivative;
quarticity_sum = quarticity_sum + product;
}
corrected[j] = (derivative_sum / m_plus_1 - quarticity_sum * k_const) * scale;
}
corrected
}
pub(super) fn center(&self) -> usize {
self.max_freq
}
pub(super) fn const_(&self) -> T {
T::from_f64_fast(std::f64::consts::TAU) / self.period
}
pub(super) fn vol_coeffs(&self, m: usize) -> Array1<Complex<T>> {
assert!(
self.n_freq + m <= self.max_freq,
"need max_freq ≥ N + M = {} but have {}",
self.n_freq + m,
self.max_freq
);
convolution_coefficients(&self.dx, &self.dx, self.period, self.n_freq, m)
}
}
pub(super) fn fejer_inversion<T: FloatExt>(
coeffs: &Array1<Complex<T>>,
m_freq: usize,
period: T,
tau: &[T],
fejer_denom: T,
) -> Array1<T> {
let const_ = T::from_f64_fast(std::f64::consts::TAU) / period;
let mut result = Array1::<T>::zeros(tau.len());
for (i, &t) in tau.iter().enumerate() {
let mut sum = Complex::<T>::new(T::zero(), T::zero());
for (j, k) in (-(m_freq as i64)..=(m_freq as i64)).enumerate() {
let k_t = T::from_f64_fast(k as f64);
let fejer = T::one() - T::from_usize_(k.unsigned_abs() as usize) / fejer_denom;
let phase = const_ * k_t * t;
sum = sum + coeffs[j] * Complex::new(phase.cos(), phase.sin()) * fejer;
}
result[i] = sum.re;
}
result
}