#[path = "../src/transform.rs"]
mod transform;
use std::f64::consts::{FRAC_1_SQRT_2, PI};
#[test]
fn every_fixed_point_transform_constant_regenerates_from_its_expression() {
let basis = core::array::from_fn(|frequency| {
core::array::from_fn(|position| {
let scale = if frequency == 0 { 0.5 } else { FRAC_1_SQRT_2 };
let angle = (2 * position + 1) as f64 * frequency as f64 * PI / 8.0;
(65536.0 * scale * angle.cos()).round() as i32
})
});
let inverse_cosine = (65536.0 * (2.0f64.sqrt() * (PI / 8.0).cos() - 1.0)).round() as i32;
let inverse_sine = (65536.0 * 2.0f64.sqrt() * (PI / 8.0).sin()).round() as i32;
assert_eq!(basis, transform::DCT_BASIS);
assert_eq!(inverse_cosine, transform::COS_PI_8_SQRT_2_MINUS_1);
assert_eq!(inverse_sine, transform::SIN_PI_8_SQRT_2);
}
fn real_forward_dct(input: &[i32; 16]) -> [i32; 16] {
let mut output = [0; 16];
for vertical in 0..4 {
for horizontal in 0..4 {
let vertical_scale = if vertical == 0 { FRAC_1_SQRT_2 } else { 1.0 };
let horizontal_scale = if horizontal == 0 { FRAC_1_SQRT_2 } else { 1.0 };
let mut sum = 0.0;
for row in 0..4 {
for column in 0..4 {
let vertical_angle = (2 * row + 1) as f64 * vertical as f64 * PI / 8.0;
let horizontal_angle = (2 * column + 1) as f64 * horizontal as f64 * PI / 8.0;
sum += f64::from(input[row * 4 + column])
* vertical_angle.cos()
* horizontal_angle.cos();
}
}
output[vertical * 4 + horizontal] =
(sum * vertical_scale * horizontal_scale).round() as i32;
}
}
output
}
#[test]
fn the_integer_forward_dct_stays_within_one_of_the_real_transform() {
let mut state = 0xd1b5_4a32u32;
let mut largest_difference = 0;
for _ in 0..4096 {
let mut block = [0; 16];
for value in &mut block {
state ^= state << 13;
state ^= state >> 17;
state ^= state << 5;
*value = (state % 511) as i32 - 255;
}
let integer = transform::forward_dct(&block);
let real = real_forward_dct(&block);
for index in 0..16 {
let difference = (integer[index] - real[index]).abs();
largest_difference = largest_difference.max(difference);
}
}
assert_eq!(largest_difference, 1);
}