1use std::f64::consts::{PI, SQRT_2, TAU};
7
8use crate::{
9 DctType, Direction, DstType, Normalization, SignConvention, SignalError, fft::Complex,
10};
11
12pub fn reference_dft(
17 input: &[(f64, f64)],
18 direction: Direction,
19 sign: SignConvention,
20) -> Result<Vec<(f64, f64)>, SignalError> {
21 if input.is_empty() {
22 return Err(SignalError::InvalidLength {
23 len: 0,
24 reason: "DFT requires at least one value",
25 });
26 }
27 validate_complex(input)?;
28 let sign = sign.angle_sign(direction);
29 let len = input.len() as f64;
30 let output = (0..input.len())
31 .map(|frequency| {
32 input
33 .iter()
34 .copied()
35 .enumerate()
36 .fold(Complex::ZERO, |sum, (sample, value)| {
37 let angle = sign * TAU * frequency as f64 * sample as f64 / len;
38 sum + Complex::from(value) * Complex::cis(angle)
39 })
40 .into()
41 })
42 .collect::<Vec<_>>();
43 validate_complex(&output)?;
44 Ok(output)
45}
46
47pub fn reference_dct(
49 input: &[f64],
50 kind: DctType,
51 direction: Direction,
52 normalization: Normalization,
53) -> Result<Vec<f64>, SignalError> {
54 validate_real(input)?;
55 if kind == DctType::I && input.len() < 2 {
56 return Err(SignalError::InvalidLength {
57 len: input.len(),
58 reason: "DCT-I requires at least two values",
59 });
60 }
61 if normalization == Normalization::Orthonormal {
62 let output = orthonormal_dct(input, kind, direction);
63 validate_real(&output)?;
64 return Ok(output);
65 }
66 let (kernel, factor) = match (kind, direction) {
67 (DctType::I, _) => (DctType::I, 2.0 * (input.len() - 1) as f64),
68 (DctType::II, Direction::Forward) | (DctType::III, Direction::Inverse) => {
69 (DctType::II, 2.0 * input.len() as f64)
70 }
71 (DctType::III, Direction::Forward) | (DctType::II, Direction::Inverse) => {
72 (DctType::III, 2.0 * input.len() as f64)
73 }
74 (DctType::IV, _) => (DctType::IV, 2.0 * input.len() as f64),
75 };
76 let mut output = raw_dct(input, kernel);
77 apply_pair_normalization(&mut output, factor, direction, normalization);
78 validate_real(&output)?;
79 Ok(output)
80}
81
82pub fn reference_dst(
84 input: &[f64],
85 kind: DstType,
86 direction: Direction,
87 normalization: Normalization,
88) -> Result<Vec<f64>, SignalError> {
89 validate_real(input)?;
90 if normalization == Normalization::Orthonormal {
91 let output = orthonormal_dst(input, kind, direction);
92 validate_real(&output)?;
93 return Ok(output);
94 }
95 let (kernel, factor) = match (kind, direction) {
96 (DstType::I, _) => (DstType::I, 2.0 * (input.len() + 1) as f64),
97 (DstType::II, Direction::Forward) | (DstType::III, Direction::Inverse) => {
98 (DstType::II, 2.0 * input.len() as f64)
99 }
100 (DstType::III, Direction::Forward) | (DstType::II, Direction::Inverse) => {
101 (DstType::III, 2.0 * input.len() as f64)
102 }
103 (DstType::IV, _) => (DstType::IV, 2.0 * input.len() as f64),
104 };
105 let mut output = raw_dst(input, kernel);
106 apply_pair_normalization(&mut output, factor, direction, normalization);
107 validate_real(&output)?;
108 Ok(output)
109}
110
111fn raw_dct(input: &[f64], kind: DctType) -> Vec<f64> {
112 let len = input.len();
113 match kind {
114 DctType::I => (0..len)
115 .map(|frequency| {
116 input[0]
117 + if frequency % 2 == 0 {
118 input[len - 1]
119 } else {
120 -input[len - 1]
121 }
122 + 2.0
123 * input[1..len - 1]
124 .iter()
125 .enumerate()
126 .map(|(offset, value)| {
127 let sample = offset + 1;
128 value
129 * (PI * frequency as f64 * sample as f64 / (len - 1) as f64)
130 .cos()
131 })
132 .sum::<f64>()
133 })
134 .collect(),
135 DctType::II => (0..len)
136 .map(|frequency| {
137 2.0 * input
138 .iter()
139 .enumerate()
140 .map(|(sample, value)| {
141 value
142 * (PI * frequency as f64 * (2 * sample + 1) as f64 / (2 * len) as f64)
143 .cos()
144 })
145 .sum::<f64>()
146 })
147 .collect(),
148 DctType::III => (0..len)
149 .map(|frequency| {
150 input[0]
151 + 2.0
152 * input[1..]
153 .iter()
154 .enumerate()
155 .map(|(offset, value)| {
156 let sample = offset + 1;
157 value
158 * (PI * sample as f64 * (2 * frequency + 1) as f64
159 / (2 * len) as f64)
160 .cos()
161 })
162 .sum::<f64>()
163 })
164 .collect(),
165 DctType::IV => (0..len)
166 .map(|frequency| {
167 2.0 * input
168 .iter()
169 .enumerate()
170 .map(|(sample, value)| {
171 value
172 * (PI * (2 * frequency + 1) as f64 * (2 * sample + 1) as f64
173 / (4 * len) as f64)
174 .cos()
175 })
176 .sum::<f64>()
177 })
178 .collect(),
179 }
180}
181
182fn raw_dst(input: &[f64], kind: DstType) -> Vec<f64> {
183 let len = input.len();
184 match kind {
185 DstType::I => (0..len)
186 .map(|frequency| {
187 2.0 * input
188 .iter()
189 .enumerate()
190 .map(|(sample, value)| {
191 value
192 * (PI * (frequency + 1) as f64 * (sample + 1) as f64 / (len + 1) as f64)
193 .sin()
194 })
195 .sum::<f64>()
196 })
197 .collect(),
198 DstType::II => (0..len)
199 .map(|frequency| {
200 2.0 * input
201 .iter()
202 .enumerate()
203 .map(|(sample, value)| {
204 value
205 * (PI * (frequency + 1) as f64 * (2 * sample + 1) as f64
206 / (2 * len) as f64)
207 .sin()
208 })
209 .sum::<f64>()
210 })
211 .collect(),
212 DstType::III => (0..len)
213 .map(|frequency| {
214 let endpoint = if frequency % 2 == 0 {
215 input[len - 1]
216 } else {
217 -input[len - 1]
218 };
219 endpoint
220 + 2.0
221 * input[..len - 1]
222 .iter()
223 .enumerate()
224 .map(|(sample, value)| {
225 value
226 * (PI * (sample + 1) as f64 * (2 * frequency + 1) as f64
227 / (2 * len) as f64)
228 .sin()
229 })
230 .sum::<f64>()
231 })
232 .collect(),
233 DstType::IV => (0..len)
234 .map(|frequency| {
235 2.0 * input
236 .iter()
237 .enumerate()
238 .map(|(sample, value)| {
239 value
240 * (PI * (2 * frequency + 1) as f64 * (2 * sample + 1) as f64
241 / (4 * len) as f64)
242 .sin()
243 })
244 .sum::<f64>()
245 })
246 .collect(),
247 }
248}
249
250fn orthonormal_dct(input: &[f64], kind: DctType, direction: Direction) -> Vec<f64> {
251 let len = input.len();
252 match kind {
253 DctType::I => matrix_apply(input, |row, column| {
254 let row_weight = endpoint_weight(row, len);
255 let column_weight = endpoint_weight(column, len);
256 (2.0 / (len - 1) as f64).sqrt()
257 * row_weight
258 * column_weight
259 * (PI * row as f64 * column as f64 / (len - 1) as f64).cos()
260 }),
261 DctType::II | DctType::III => {
262 let transpose = (kind == DctType::II) == (direction == Direction::Inverse);
263 matrix_apply(input, |row, column| {
264 let (frequency, sample) = if transpose {
265 (column, row)
266 } else {
267 (row, column)
268 };
269 (2.0 / len as f64).sqrt()
270 * if frequency == 0 { 1.0 / SQRT_2 } else { 1.0 }
271 * (PI * frequency as f64 * (2 * sample + 1) as f64 / (2 * len) as f64).cos()
272 })
273 }
274 DctType::IV => matrix_apply(input, |row, column| {
275 (2.0 / len as f64).sqrt()
276 * (PI * (2 * row + 1) as f64 * (2 * column + 1) as f64 / (4 * len) as f64).cos()
277 }),
278 }
279}
280
281fn orthonormal_dst(input: &[f64], kind: DstType, direction: Direction) -> Vec<f64> {
282 let len = input.len();
283 match kind {
284 DstType::I => matrix_apply(input, |row, column| {
285 (2.0 / (len + 1) as f64).sqrt()
286 * (PI * (row + 1) as f64 * (column + 1) as f64 / (len + 1) as f64).sin()
287 }),
288 DstType::II | DstType::III => {
289 let transpose = (kind == DstType::II) == (direction == Direction::Inverse);
290 matrix_apply(input, |row, column| {
291 let (frequency, sample) = if transpose {
292 (column, row)
293 } else {
294 (row, column)
295 };
296 (2.0 / len as f64).sqrt()
297 * if frequency + 1 == len {
298 1.0 / SQRT_2
299 } else {
300 1.0
301 }
302 * (PI * (frequency + 1) as f64 * (2 * sample + 1) as f64 / (2 * len) as f64)
303 .sin()
304 })
305 }
306 DstType::IV => matrix_apply(input, |row, column| {
307 (2.0 / len as f64).sqrt()
308 * (PI * (2 * row + 1) as f64 * (2 * column + 1) as f64 / (4 * len) as f64).sin()
309 }),
310 }
311}
312
313fn endpoint_weight(index: usize, len: usize) -> f64 {
314 if index == 0 || index + 1 == len {
315 1.0 / SQRT_2
316 } else {
317 1.0
318 }
319}
320
321fn matrix_apply(input: &[f64], coefficient: impl Fn(usize, usize) -> f64) -> Vec<f64> {
322 (0..input.len())
323 .map(|row| {
324 input
325 .iter()
326 .enumerate()
327 .map(|(column, value)| coefficient(row, column) * value)
328 .sum()
329 })
330 .collect()
331}
332
333fn apply_pair_normalization(
334 output: &mut [f64],
335 factor: f64,
336 direction: Direction,
337 normalization: Normalization,
338) {
339 let scale = match (normalization, direction) {
340 (Normalization::None, _) => 1.0,
341 (Normalization::Forward, Direction::Forward)
342 | (Normalization::Inverse, Direction::Inverse) => 1.0 / factor,
343 (Normalization::Forward | Normalization::Inverse, _) => 1.0,
344 (Normalization::Orthonormal, _) => unreachable!("handled by orthonormal definitions"),
345 };
346 output.iter_mut().for_each(|value| *value *= scale);
347}
348
349fn validate_real(input: &[f64]) -> Result<(), SignalError> {
350 if input.is_empty() {
351 return Err(SignalError::InvalidLength {
352 len: 0,
353 reason: "real transforms require at least one value",
354 });
355 }
356 for (index, value) in input.iter().enumerate() {
357 if !value.is_finite() {
358 return Err(SignalError::NonFinite {
359 index,
360 component: "value",
361 });
362 }
363 }
364 Ok(())
365}
366
367fn validate_complex(input: &[(f64, f64)]) -> Result<(), SignalError> {
368 for (index, (real, imag)) in input.iter().copied().enumerate() {
369 if !real.is_finite() {
370 return Err(SignalError::NonFinite {
371 index,
372 component: "real",
373 });
374 }
375 if !imag.is_finite() {
376 return Err(SignalError::NonFinite {
377 index,
378 component: "imag",
379 });
380 }
381 }
382 Ok(())
383}