use crate::util::{DctSample, try_vec};
use crate::{PxdctError, PxdctExecutor, SpectralExecutor};
use num_traits::AsPrimitive;
use std::sync::Arc;
#[derive(Copy, Clone, Debug, Default, PartialEq, Eq, Hash)]
pub enum Scaling {
#[default]
None,
Scale,
Ortho,
}
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
pub enum TransformKind {
Dct1,
Dst1,
Dct2,
Dct3,
Dct4,
Dst2,
Dst3,
Dst4,
Dct5,
Dst5,
Dct6,
Dst6,
Dct7,
Dst7,
Dct8,
Dst8,
}
#[derive(Copy, Clone, Debug)]
enum InputCorrection<T> {
None,
Constant { index: usize, factor: T },
Alternating { index: usize, factor: T },
Dct1Dual { factor: T },
}
#[derive(Copy, Clone, Debug)]
struct ScalingPlan<T> {
correction: InputCorrection<T>,
flat: T,
apply_flat: bool,
endpoint_head: Option<T>,
endpoint_tail: Option<T>,
}
impl<T: DctSample> ScalingPlan<T>
where
f64: AsPrimitive<T>,
{
fn flat_only(length: usize) -> Self {
let flat = (2.0_f64 / length as f64).sqrt().as_();
Self {
correction: InputCorrection::None,
flat,
apply_flat: true,
endpoint_head: None,
endpoint_tail: None,
}
}
fn ortho(kind: TransformKind, length: usize) -> Result<Self, PxdctError> {
if length == 0 {
return Err(PxdctError::ZeroSizedDct);
}
let n = length as f64;
let inv_sqrt2 = std::f64::consts::FRAC_1_SQRT_2;
let lift_half = inv_sqrt2 - 0.5_f64;
let plan = match kind {
TransformKind::Dct1 => {
if length < 2 {
return Err(PxdctError::MinimumPoints(2, "DCT-I".to_string()));
}
let flat = (1.0_f64 / (2.0 * (n - 1.0))).sqrt();
let dual_factor: T = (std::f64::consts::SQRT_2 - 1.0_f64).as_();
Self {
correction: InputCorrection::Dct1Dual {
factor: dual_factor,
},
flat: flat.as_(),
apply_flat: true,
endpoint_head: Some(inv_sqrt2.as_()),
endpoint_tail: Some(inv_sqrt2.as_()),
}
}
TransformKind::Dst1 => {
let flat = (2.0_f64 / (n + 1.0)).sqrt();
Self {
correction: InputCorrection::None,
flat: flat.as_(),
apply_flat: true,
endpoint_head: None,
endpoint_tail: None,
}
}
TransformKind::Dct2 => {
let flat = (2.0_f64 / n).sqrt();
Self {
correction: InputCorrection::None,
flat: flat.as_(),
apply_flat: true,
endpoint_head: Some(inv_sqrt2.as_()),
endpoint_tail: None,
}
}
TransformKind::Dct3 => {
let flat = (2.0_f64 / n).sqrt();
Self {
correction: InputCorrection::Constant {
index: 0,
factor: lift_half.as_(),
},
flat: flat.as_(),
apply_flat: true,
endpoint_head: None,
endpoint_tail: None,
}
}
TransformKind::Dct4 | TransformKind::Dst4 => {
let flat = (2.0_f64 / n).sqrt();
Self {
correction: InputCorrection::None,
flat: flat.as_(),
apply_flat: true,
endpoint_head: None,
endpoint_tail: None,
}
}
TransformKind::Dst2 => {
let flat = (2.0_f64 / n).sqrt();
Self {
correction: InputCorrection::None,
flat: flat.as_(),
apply_flat: true,
endpoint_head: None,
endpoint_tail: Some(inv_sqrt2.as_()),
}
}
TransformKind::Dst3 => {
let flat = (2.0_f64 / n).sqrt();
Self {
correction: InputCorrection::Alternating {
index: length - 1,
factor: lift_half.as_(),
},
flat: flat.as_(),
apply_flat: true,
endpoint_head: None,
endpoint_tail: None,
}
}
TransformKind::Dct5 => {
let denom = n - 0.5;
let flat = (2.0_f64 / denom).sqrt();
Self {
correction: InputCorrection::Constant {
index: 0,
factor: lift_half.as_(),
},
flat: flat.as_(),
apply_flat: true,
endpoint_head: Some(inv_sqrt2.as_()),
endpoint_tail: None,
}
}
TransformKind::Dst5 => {
let denom = n + 0.5;
let flat = (2.0_f64 / denom).sqrt();
Self {
correction: InputCorrection::None,
flat: flat.as_(),
apply_flat: true,
endpoint_head: None,
endpoint_tail: None,
}
}
TransformKind::Dct6 => {
let denom = n - 0.5;
let flat = (2.0_f64 / denom).sqrt();
Self {
correction: InputCorrection::Alternating {
index: length - 1,
factor: lift_half.as_(),
},
flat: flat.as_(),
apply_flat: true,
endpoint_head: Some(inv_sqrt2.as_()),
endpoint_tail: None,
}
}
TransformKind::Dst6 => {
let denom = n + 0.5;
let flat = (2.0_f64 / denom).sqrt();
Self {
correction: InputCorrection::None,
flat: flat.as_(),
apply_flat: true,
endpoint_head: None,
endpoint_tail: None,
}
}
TransformKind::Dct7 => {
let denom = n - 0.5;
let flat = (2.0_f64 / denom).sqrt();
Self {
correction: InputCorrection::Constant {
index: 0,
factor: lift_half.as_(),
},
flat: flat.as_(),
apply_flat: true,
endpoint_head: None,
endpoint_tail: Some(inv_sqrt2.as_()),
}
}
TransformKind::Dst7 => {
let denom = n + 0.5;
let flat = (2.0_f64 / denom).sqrt();
Self {
correction: InputCorrection::None,
flat: flat.as_(),
apply_flat: true,
endpoint_head: None,
endpoint_tail: None,
}
}
TransformKind::Dct8 => {
let denom = n + 0.5;
let flat = (2.0_f64 / denom).sqrt();
Self {
correction: InputCorrection::None,
flat: flat.as_(),
apply_flat: true,
endpoint_head: None,
endpoint_tail: None,
}
}
TransformKind::Dst8 => {
let denom = n - 0.5;
let flat = (2.0_f64 / denom).sqrt();
Self {
correction: InputCorrection::Alternating {
index: length - 1,
factor: lift_half.as_(),
},
flat: flat.as_(),
apply_flat: true,
endpoint_head: None,
endpoint_tail: Some(inv_sqrt2.as_()),
}
}
};
Ok(plan)
}
}
pub(crate) struct ScalingInterceptor<T> {
pub(crate) interceptor: SpectralExecutor<T>,
plan: ScalingPlan<T>,
}
impl<T: DctSample> ScalingInterceptor<T>
where
f64: AsPrimitive<T>,
{
#[inline]
fn post_process(&self, output: &mut [T], snapshot: PreSnapshot<T>) {
match self.plan.correction {
InputCorrection::None => {}
InputCorrection::Constant { factor, .. } => {
let add = factor * snapshot.correction_value;
for y in output.iter_mut() {
*y += add;
}
}
InputCorrection::Alternating { factor, .. } => {
let add = factor * snapshot.correction_value;
let minus_one: T = (-1.0_f64).as_();
let neg_add = minus_one * add;
for (k, y) in output.iter_mut().enumerate() {
if k & 1 == 0 {
*y += add;
} else {
*y += neg_add;
}
}
}
InputCorrection::Dct1Dual { factor } => {
let head_contrib = factor * snapshot.head_value;
let tail_contrib = factor * snapshot.tail_value;
let minus_one: T = (-1.0_f64).as_();
let neg_tail = minus_one * tail_contrib;
for (k, y) in output.iter_mut().enumerate() {
let alt_tail = if k & 1 == 0 { tail_contrib } else { neg_tail };
*y += head_contrib + alt_tail;
}
}
}
if self.plan.apply_flat {
let flat = self.plan.flat;
for y in output.iter_mut() {
*y *= flat;
}
}
if let Some(ep) = self.plan.endpoint_head
&& let Some(first) = output.first_mut()
{
*first *= ep;
}
if let Some(ep) = self.plan.endpoint_tail
&& let Some(last) = output.last_mut()
{
*last *= ep;
}
}
#[inline]
fn snapshot(&self, input: &[T]) -> PreSnapshot<T> {
let zero: T = T::default();
match self.plan.correction {
InputCorrection::None => PreSnapshot {
head_value: zero,
tail_value: zero,
correction_value: zero,
},
InputCorrection::Constant { index, .. }
| InputCorrection::Alternating { index, .. } => PreSnapshot {
head_value: zero,
tail_value: zero,
correction_value: input.get(index).copied().unwrap_or(zero),
},
InputCorrection::Dct1Dual { .. } => {
let n = input.len();
PreSnapshot {
head_value: input.first().copied().unwrap_or(zero),
tail_value: if n > 0 { input[n - 1] } else { zero },
correction_value: zero,
}
}
}
}
}
#[derive(Copy, Clone)]
struct PreSnapshot<T> {
head_value: T,
tail_value: T,
correction_value: T,
}
impl<T: DctSample> PxdctExecutor<T> for ScalingInterceptor<T>
where
f64: AsPrimitive<T>,
{
fn execute(&self, data: &mut [T]) -> Result<(), PxdctError> {
let mut scratch = try_vec![T::default(); self.scratch_size()];
self.execute_with_scratch(data, &mut scratch)
}
fn execute_with_scratch(&self, data: &mut [T], scratch: &mut [T]) -> Result<(), PxdctError> {
let snap = self.snapshot(data);
self.interceptor.execute_with_scratch(data, scratch)?;
self.post_process(data, snap);
Ok(())
}
fn execute_into(&self, input: &[T], output: &mut [T]) -> Result<(), PxdctError> {
let mut scratch = try_vec![T::default(); self.scratch_size()];
self.execute_into_with_scratch(input, output, &mut scratch)
}
fn execute_into_with_scratch(
&self,
input: &[T],
output: &mut [T],
scratch: &mut [T],
) -> Result<(), PxdctError> {
use crate::util::validate_oof_sizes;
validate_oof_sizes!(input, output, self.interceptor.length());
let snap = self.snapshot(input);
self.interceptor
.execute_into_with_scratch(input, output, scratch)?;
self.post_process(output, snap);
Ok(())
}
fn length(&self) -> usize {
self.interceptor.length()
}
fn scratch_size(&self) -> usize {
self.interceptor.scratch_size()
}
}
pub(crate) fn wrap_with_scaling<T: DctSample>(
inner: SpectralExecutor<T>,
kind: TransformKind,
scaling: Scaling,
) -> Result<SpectralExecutor<T>, PxdctError>
where
f64: AsPrimitive<T>,
{
match scaling {
Scaling::None => Ok(inner),
Scaling::Scale => {
let plan: ScalingPlan<T> = ScalingPlan::flat_only(inner.length());
Ok(Arc::new(ScalingInterceptor {
interceptor: inner,
plan,
}))
}
Scaling::Ortho => {
let length = inner.length();
let plan: ScalingPlan<T> = ScalingPlan::ortho(kind, length)?;
Ok(Arc::new(ScalingInterceptor {
interceptor: inner,
plan,
}))
}
}
}