use std::f64::consts::PI;
use crate::xafsft::{Window, fft_padded, ftwindow, ifft_padded, xftf_fast, xftr_fast};
use num_complex::Complex64;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum FitSpace {
K,
R,
Q,
W,
}
#[derive(Debug, Clone)]
pub struct Transform {
pub kmin: f64,
pub kmax: f64,
pub kweight: Vec<i32>,
pub dk: f64,
pub dk2: Option<f64>,
pub window: Window,
pub nfft: usize,
pub kstep: f64,
pub rmin: f64,
pub rmax: f64,
pub dr: f64,
pub dr2: Option<f64>,
pub rwindow: Window,
pub rbkg: f64,
pub fitspace: FitSpace,
rstep: f64,
k_: Vec<f64>,
kwin: Vec<f64>,
rwin: Vec<f64>,
}
impl Transform {
#[allow(clippy::too_many_arguments)]
pub fn new(
kmin: f64,
kmax: f64,
kweight: Vec<i32>,
dk: f64,
dk2: Option<f64>,
window: Window,
nfft: usize,
kstep: f64,
rmin: f64,
rmax: f64,
dr: f64,
dr2: Option<f64>,
rwindow: Window,
rbkg: f64,
fitspace: FitSpace,
) -> Self {
assert!(
!kweight.is_empty(),
"Transform requires at least one k-weight"
);
let rstep = PI / (kstep * nfft as f64);
let k_: Vec<f64> = (0..nfft).map(|i| kstep * i as f64).collect();
let r_: Vec<f64> = (0..nfft).map(|i| rstep * i as f64).collect();
let kwin = ftwindow(&k_, Some(kmin), Some(kmax), dk, dk2, window);
let rwin_xmin = rbkg.max(rmin);
let rwin = ftwindow(&r_, Some(rwin_xmin), Some(rmax), dr, dr2, rwindow);
Transform {
kmin,
kmax,
kweight,
dk,
dk2,
window,
nfft,
kstep,
rmin,
rmax,
dr,
dr2,
rwindow,
rbkg,
fitspace,
rstep,
k_,
kwin,
rwin,
}
}
pub fn defaults() -> Self {
Transform::new(
0.0,
20.0,
vec![2],
4.0,
None,
Window::Kaiser,
2048,
0.05,
0.0,
10.0,
0.0,
None,
Window::Hanning,
0.0,
FitSpace::R,
)
}
pub fn rstep(&self) -> f64 {
self.rstep
}
pub fn enable_refine_bkg(&mut self) {
self.rbkg = self.rbkg.max(self.rmin);
self.rmin = self.rstep;
let r_: Vec<f64> = (0..self.nfft).map(|i| self.rstep * i as f64).collect();
let xmin = self.rbkg.max(self.rmin);
self.rwin = ftwindow(
&r_,
Some(xmin),
Some(self.rmax),
self.dr,
self.dr2,
self.rwindow,
);
}
pub fn k_grid(&self) -> &[f64] {
&self.k_
}
pub fn kwin(&self) -> &[f64] {
&self.kwin
}
pub fn rwin(&self) -> &[f64] {
&self.rwin
}
pub fn fftf(&self, chi: &[f64], kweight: i32) -> Vec<Complex64> {
let m = chi.len();
let cx: Vec<Complex64> = (0..m)
.map(|i| Complex64::new(chi[i] * self.kwin[i] * self.k_[i].powi(kweight), 0.0))
.collect();
xftf_fast(&cx, self.nfft, self.kstep)
}
pub fn fftr(&self, chir: &[Complex64]) -> Vec<Complex64> {
let m = chir.len();
let cx: Vec<Complex64> = (0..m).map(|i| chir[i] * self.rwin[i]).collect();
xftr_fast(&cx, self.nfft, self.kstep)
}
pub fn cwt(&self, chi: &[f64], kweight: i32) -> (Vec<Complex64>, usize) {
let nkpts = chi.len();
let nfft = self.nfft;
let kstep = self.kstep;
let rstep = self.rstep;
let weighted: Vec<f64> = if kweight != 0 {
(0..nkpts)
.map(|i| chi[i] * self.kwin[i] * self.k_[i].powi(kweight))
.collect()
} else {
chi.to_vec()
};
let half = nfft / 2;
let m = nkpts.min(half);
let chix: Vec<Complex64> = (0..half)
.map(|i| {
if i < m {
Complex64::new(weighted[i], 0.0)
} else {
Complex64::new(0.0, 0.0)
}
})
.collect();
let ffchi_full = fft_padded(&chix, 2 * nfft);
let ffchi = &ffchi_full[..nfft];
let nrpts = (self.rmax / rstep).round() as usize;
let omega: Vec<f64> = (0..nfft)
.map(|j| PI * j as f64 / (kstep * nfft as f64))
.collect();
let log_fact: f64 = (1..=nrpts).map(|j| (j as f64).ln()).sum();
let cauchy_sum = (2.0 * PI).ln() - log_fact;
let nfft_half = nfft as f64 / 2.0;
let ikmin = (0.0f64.max(0.01 + self.kmin / kstep)) as usize;
let ikmax = ((nfft_half.min(0.01 + self.kmax / kstep)) as usize).min(nkpts);
let irmin = (0.0f64.max(0.01 + self.rmin / rstep)) as usize;
let irmax = (nfft_half.min(0.01 + self.rmax / rstep)) as usize;
let ncols = ikmax.saturating_sub(ikmin);
let mut out = Vec::with_capacity(irmax.saturating_sub(irmin) * ncols);
for i in irmin..irmax {
let r_i = if i == 0 { 1.0e-19 } else { rstep * i as f64 };
let alpha = nrpts as f64 / (2.0 * r_i);
let prod: Vec<Complex64> = (0..nfft)
.map(|j| {
let aom = alpha * omega[j];
let filt = cauchy_sum + nrpts as f64 * aom.ln() - aom;
ffchi[j] * filt.exp()
})
.collect();
let row = ifft_padded(&prod, 2 * nfft);
out.extend_from_slice(&row[ikmin..ikmax]);
}
(out, ncols)
}
}