use crate::api::{Direction, Flags};
use crate::kernel::{Complex, Float};
use crate::prelude::*;
use super::types::{
Plan, Plan2D, PlanND, RealPlan2D, RealPlan3D, RealPlanND, SplitPlan, SplitPlan2D, SplitPlan3D,
SplitPlanND,
};
pub fn fft_nd<T: Float>(input: &[Complex<T>], dims: &[usize]) -> Vec<Complex<T>> {
let total: usize = dims.iter().product();
assert_eq!(
input.len(),
total,
"Input size must match product of dimensions"
);
let mut output = vec![Complex::zero(); total];
if let Some(plan) = PlanND::new(dims, Direction::Forward, Flags::ESTIMATE) {
plan.execute(input, &mut output);
}
output
}
pub fn ifft_nd<T: Float>(input: &[Complex<T>], dims: &[usize]) -> Vec<Complex<T>> {
let total: usize = dims.iter().product();
assert_eq!(
input.len(),
total,
"Input size must match product of dimensions"
);
let mut output = vec![Complex::zero(); total];
if let Some(plan) = PlanND::new(dims, Direction::Backward, Flags::ESTIMATE) {
plan.execute(input, &mut output);
let scale = T::from_usize(total);
for x in &mut output {
*x = *x / scale;
}
}
output
}
pub fn fft2d<T: Float>(input: &[Complex<T>], n0: usize, n1: usize) -> Vec<Complex<T>> {
assert_eq!(input.len(), n0 * n1, "Input size must match n0 × n1");
let mut output = vec![Complex::zero(); n0 * n1];
if let Some(plan) = Plan2D::new(n0, n1, Direction::Forward, Flags::ESTIMATE) {
plan.execute(input, &mut output);
}
output
}
pub fn ifft2d<T: Float>(input: &[Complex<T>], n0: usize, n1: usize) -> Vec<Complex<T>> {
assert_eq!(input.len(), n0 * n1, "Input size must match n0 × n1");
let mut output = vec![Complex::zero(); n0 * n1];
if let Some(plan) = Plan2D::new(n0, n1, Direction::Backward, Flags::ESTIMATE) {
plan.execute(input, &mut output);
let scale = T::from_usize(n0 * n1);
for x in &mut output {
*x = *x / scale;
}
}
output
}
pub fn fft<T: Float>(input: &[Complex<T>]) -> Vec<Complex<T>> {
let n = input.len();
let mut output = vec![Complex::zero(); n];
if let Some(plan) = Plan::dft_1d(n, Direction::Forward, Flags::ESTIMATE) {
plan.execute(input, &mut output);
}
output
}
pub fn ifft<T: Float>(input: &[Complex<T>]) -> Vec<Complex<T>> {
let n = input.len();
let mut output = vec![Complex::zero(); n];
if let Some(plan) = Plan::dft_1d(n, Direction::Backward, Flags::ESTIMATE) {
plan.execute(input, &mut output);
let scale = T::from_usize(n);
for x in &mut output {
*x = *x / scale;
}
}
output
}
pub fn rfft<T: Float>(input: &[T]) -> Vec<Complex<T>> {
use crate::rdft::solvers::R2cSolver;
let n = input.len();
let mut output = vec![Complex::zero(); n / 2 + 1];
R2cSolver::new(n).execute(input, &mut output);
output
}
pub fn irfft<T: Float>(input: &[Complex<T>], n: usize) -> Vec<T> {
use crate::rdft::solvers::C2rSolver;
let mut output = vec![T::ZERO; n];
C2rSolver::new(n).execute_normalized(input, &mut output);
output
}
pub fn fft_split<T: Float>(in_real: &[T], in_imag: &[T]) -> (Vec<T>, Vec<T>) {
let n = in_real.len();
assert_eq!(
in_imag.len(),
n,
"Real and imaginary arrays must have same length"
);
if n == 0 {
return (Vec::new(), Vec::new());
}
let plan = SplitPlan::dft_1d(n, Direction::Forward, Flags::ESTIMATE)
.expect("Failed to create split-complex FFT plan");
let mut out_real = vec![T::zero(); n];
let mut out_imag = vec![T::zero(); n];
plan.execute(in_real, in_imag, &mut out_real, &mut out_imag);
(out_real, out_imag)
}
pub fn ifft_split<T: Float>(in_real: &[T], in_imag: &[T]) -> (Vec<T>, Vec<T>) {
let n = in_real.len();
assert_eq!(
in_imag.len(),
n,
"Real and imaginary arrays must have same length"
);
if n == 0 {
return (Vec::new(), Vec::new());
}
let plan = SplitPlan::dft_1d(n, Direction::Backward, Flags::ESTIMATE)
.expect("Failed to create split-complex IFFT plan");
let mut out_real = vec![T::zero(); n];
let mut out_imag = vec![T::zero(); n];
plan.execute(in_real, in_imag, &mut out_real, &mut out_imag);
let scale = T::one() / T::from_usize(n);
for r in &mut out_real {
*r = *r * scale;
}
for i in &mut out_imag {
*i = *i * scale;
}
(out_real, out_imag)
}
pub fn fft_batch<T: Float>(input: &[Complex<T>], n: usize, howmany: usize) -> Vec<Complex<T>> {
use crate::dft::problem::Sign;
use crate::dft::solvers::VrankGeq1Solver;
assert_eq!(
input.len(),
n * howmany,
"Input size must match n × howmany"
);
let mut output = vec![Complex::zero(); n * howmany];
let solver = VrankGeq1Solver::new_contiguous(n, howmany);
solver.execute(input, &mut output, Sign::Forward);
output
}
pub fn ifft_batch<T: Float>(input: &[Complex<T>], n: usize, howmany: usize) -> Vec<Complex<T>> {
use crate::dft::problem::Sign;
use crate::dft::solvers::VrankGeq1Solver;
assert_eq!(
input.len(),
n * howmany,
"Input size must match n × howmany"
);
let mut output = vec![Complex::zero(); n * howmany];
let solver = VrankGeq1Solver::new_contiguous(n, howmany);
solver.execute(input, &mut output, Sign::Backward);
let scale = T::from_usize(n);
for x in &mut output {
*x = *x / scale;
}
output
}
pub fn rfft_batch<T: Float>(input: &[T], n: usize, howmany: usize) -> Vec<Complex<T>> {
use crate::rdft::solvers::RdftVrankGeq1Solver;
assert_eq!(
input.len(),
n * howmany,
"Input size must match n × howmany"
);
let out_len = n / 2 + 1;
let mut output = vec![Complex::zero(); out_len * howmany];
let solver = RdftVrankGeq1Solver::new(n, howmany, 1, 1, n as isize, out_len as isize);
solver.execute_r2c(input, &mut output);
output
}
pub fn irfft_batch<T: Float>(input: &[Complex<T>], n: usize, howmany: usize) -> Vec<T> {
use crate::rdft::solvers::RdftVrankGeq1Solver;
let in_len = n / 2 + 1;
assert_eq!(
input.len(),
in_len * howmany,
"Input size must match (n/2+1) × howmany"
);
let mut output = vec![T::ZERO; n * howmany];
let solver = RdftVrankGeq1Solver::new(n, howmany, 1, 1, in_len as isize, n as isize);
solver.execute_c2r(input, &mut output);
output
}
pub fn rfft2d<T: Float>(input: &[T], n0: usize, n1: usize) -> Vec<Complex<T>> {
let expected_in = n0 * n1;
assert_eq!(input.len(), expected_in, "Input size must match n0 × n1");
let out_len = n0 * (n1 / 2 + 1);
let mut output = vec![Complex::zero(); out_len];
let plan = RealPlan2D::r2c(n0, n1, Flags::ESTIMATE).expect("Failed to create plan");
plan.execute_r2c(input, &mut output);
output
}
pub fn irfft2d<T: Float>(input: &[Complex<T>], n0: usize, n1: usize) -> Vec<T> {
let expected_in = n0 * (n1 / 2 + 1);
assert_eq!(
input.len(),
expected_in,
"Input size must match n0 × (n1/2+1)"
);
let out_len = n0 * n1;
let mut output = vec![T::ZERO; out_len];
let plan = RealPlan2D::c2r(n0, n1, Flags::ESTIMATE).expect("Failed to create plan");
plan.execute_c2r(input, &mut output);
let scale = T::one() / T::from_usize(out_len);
for x in &mut output {
*x = *x * scale;
}
output
}
pub fn rfft3d<T: Float>(input: &[T], n0: usize, n1: usize, n2: usize) -> Vec<Complex<T>> {
let expected_in = n0 * n1 * n2;
assert_eq!(
input.len(),
expected_in,
"Input size must match n0 × n1 × n2"
);
let out_len = n0 * n1 * (n2 / 2 + 1);
let mut output = vec![Complex::zero(); out_len];
let plan = RealPlan3D::r2c(n0, n1, n2, Flags::ESTIMATE).expect("Failed to create plan");
plan.execute_r2c(input, &mut output);
output
}
pub fn irfft3d<T: Float>(input: &[Complex<T>], n0: usize, n1: usize, n2: usize) -> Vec<T> {
let expected_in = n0 * n1 * (n2 / 2 + 1);
assert_eq!(
input.len(),
expected_in,
"Input size must match n0 × n1 × (n2/2+1)"
);
let out_len = n0 * n1 * n2;
let mut output = vec![T::ZERO; out_len];
let plan = RealPlan3D::c2r(n0, n1, n2, Flags::ESTIMATE).expect("Failed to create plan");
plan.execute_c2r(input, &mut output);
let scale = T::one() / T::from_usize(out_len);
for x in &mut output {
*x = *x * scale;
}
output
}
pub fn rfft_nd<T: Float>(input: &[T], dims: &[usize]) -> Vec<Complex<T>> {
assert!(!dims.is_empty(), "Dimensions cannot be empty");
let expected_in: usize = dims.iter().product();
assert_eq!(
input.len(),
expected_in,
"Input size must match product of dims"
);
let last = *dims
.last()
.expect("Dimensions cannot be empty (checked above)");
let prefix: usize = dims[..dims.len() - 1].iter().product();
let out_len = prefix.max(1) * (last / 2 + 1);
let mut output = vec![Complex::zero(); out_len];
let plan = RealPlanND::r2c(dims, Flags::ESTIMATE).expect("Failed to create plan");
plan.execute_r2c(input, &mut output);
output
}
pub fn irfft_nd<T: Float>(input: &[Complex<T>], dims: &[usize]) -> Vec<T> {
assert!(!dims.is_empty(), "Dimensions cannot be empty");
let last = *dims
.last()
.expect("Dimensions cannot be empty (checked above)");
let prefix: usize = dims[..dims.len() - 1].iter().product();
let expected_in = prefix.max(1) * (last / 2 + 1);
assert_eq!(
input.len(),
expected_in,
"Input size must match prefix × (last/2+1)"
);
let out_len: usize = dims.iter().product();
let mut output = vec![T::ZERO; out_len];
let plan = RealPlanND::c2r(dims, Flags::ESTIMATE).expect("Failed to create plan");
plan.execute_c2r(input, &mut output);
let scale = T::one() / T::from_usize(out_len);
for x in &mut output {
*x = *x * scale;
}
output
}
pub fn fft2d_split<T: Float>(
in_real: &[T],
in_imag: &[T],
n0: usize,
n1: usize,
) -> (Vec<T>, Vec<T>) {
let total = n0 * n1;
assert_eq!(in_real.len(), total);
assert_eq!(in_imag.len(), total);
let mut out_real = vec![T::ZERO; total];
let mut out_imag = vec![T::ZERO; total];
let plan = SplitPlan2D::new(n0, n1, Direction::Forward, Flags::ESTIMATE)
.expect("Failed to create plan");
plan.execute(in_real, in_imag, &mut out_real, &mut out_imag);
(out_real, out_imag)
}
pub fn ifft2d_split<T: Float>(
in_real: &[T],
in_imag: &[T],
n0: usize,
n1: usize,
) -> (Vec<T>, Vec<T>) {
let total = n0 * n1;
assert_eq!(in_real.len(), total);
assert_eq!(in_imag.len(), total);
let mut out_real = vec![T::ZERO; total];
let mut out_imag = vec![T::ZERO; total];
let plan = SplitPlan2D::new(n0, n1, Direction::Backward, Flags::ESTIMATE)
.expect("Failed to create plan");
plan.execute(in_real, in_imag, &mut out_real, &mut out_imag);
(out_real, out_imag)
}
pub fn fft3d_split<T: Float>(
in_real: &[T],
in_imag: &[T],
n0: usize,
n1: usize,
n2: usize,
) -> (Vec<T>, Vec<T>) {
let total = n0 * n1 * n2;
assert_eq!(in_real.len(), total);
assert_eq!(in_imag.len(), total);
let mut out_real = vec![T::ZERO; total];
let mut out_imag = vec![T::ZERO; total];
let plan = SplitPlan3D::new(n0, n1, n2, Direction::Forward, Flags::ESTIMATE)
.expect("Failed to create plan");
plan.execute(in_real, in_imag, &mut out_real, &mut out_imag);
(out_real, out_imag)
}
pub fn ifft3d_split<T: Float>(
in_real: &[T],
in_imag: &[T],
n0: usize,
n1: usize,
n2: usize,
) -> (Vec<T>, Vec<T>) {
let total = n0 * n1 * n2;
assert_eq!(in_real.len(), total);
assert_eq!(in_imag.len(), total);
let mut out_real = vec![T::ZERO; total];
let mut out_imag = vec![T::ZERO; total];
let plan = SplitPlan3D::new(n0, n1, n2, Direction::Backward, Flags::ESTIMATE)
.expect("Failed to create plan");
plan.execute(in_real, in_imag, &mut out_real, &mut out_imag);
(out_real, out_imag)
}
pub fn fft_nd_split<T: Float>(in_real: &[T], in_imag: &[T], dims: &[usize]) -> (Vec<T>, Vec<T>) {
let total: usize = dims.iter().product();
assert_eq!(in_real.len(), total);
assert_eq!(in_imag.len(), total);
let mut out_real = vec![T::ZERO; total];
let mut out_imag = vec![T::ZERO; total];
let plan =
SplitPlanND::new(dims, Direction::Forward, Flags::ESTIMATE).expect("Failed to create plan");
plan.execute(in_real, in_imag, &mut out_real, &mut out_imag);
(out_real, out_imag)
}
pub fn ifft_nd_split<T: Float>(in_real: &[T], in_imag: &[T], dims: &[usize]) -> (Vec<T>, Vec<T>) {
let total: usize = dims.iter().product();
assert_eq!(in_real.len(), total);
assert_eq!(in_imag.len(), total);
let mut out_real = vec![T::ZERO; total];
let mut out_imag = vec![T::ZERO; total];
let plan = SplitPlanND::new(dims, Direction::Backward, Flags::ESTIMATE)
.expect("Failed to create plan");
plan.execute(in_real, in_imag, &mut out_real, &mut out_imag);
(out_real, out_imag)
}