use std::f32::consts::PI;
use std::sync::OnceLock;
fn cos_table() -> &'static [[f32; 8]; 8] {
static T: OnceLock<[[f32; 8]; 8]> = OnceLock::new();
T.get_or_init(|| {
let mut t = [[0.0f32; 8]; 8];
for k in 0..8 {
let c_k = if k == 0 {
(1.0_f32 / 2.0_f32).sqrt()
} else {
1.0
};
for n in 0..8 {
t[k][n] = 0.5 * c_k * ((2 * n + 1) as f32 * k as f32 * PI / 16.0).cos();
}
}
t
})
}
pub fn fdct8x8(block: &mut [f32; 64]) {
let t = cos_table();
let mut tmp = [0.0f32; 64];
for y in 0..8 {
for k in 0..8 {
let mut s = 0.0f32;
for n in 0..8 {
s += t[k][n] * block[y * 8 + n];
}
tmp[y * 8 + k] = s;
}
}
for x in 0..8 {
for k in 0..8 {
let mut s = 0.0f32;
for n in 0..8 {
s += t[k][n] * tmp[n * 8 + x];
}
block[k * 8 + x] = s;
}
}
}
pub fn idct8x8(block: &mut [f32; 64]) {
let t = cos_table();
let mut tmp = [0.0f32; 64];
for y in 0..8 {
for n in 0..8 {
let mut s = 0.0f32;
for k in 0..8 {
s += t[k][n] * block[y * 8 + k];
}
tmp[y * 8 + n] = s;
}
}
for x in 0..8 {
for m in 0..8 {
let mut s = 0.0f32;
for k in 0..8 {
s += t[k][m] * tmp[k * 8 + x];
}
block[m * 8 + x] = s;
}
}
}
pub fn fdct8x8_constant(block: &mut [f32; 64]) {
let v = block[0];
let dc = v * 8.0;
for s in block.iter_mut() {
*s = 0.0;
}
block[0] = dc;
}
#[inline]
pub fn is_constant_block(block: &[f32; 64]) -> bool {
let v0 = block[0];
for &v in &block[1..] {
if v != v0 {
return false;
}
}
true
}
pub fn idct8x8_dc_only(block: &mut [f32; 64]) {
let dc = block[0] * 0.125;
for s in block.iter_mut() {
*s = dc;
}
}
#[inline]
pub fn is_dc_only(block: &[f32; 64]) -> bool {
for &v in &block[1..] {
if v != 0.0 {
return false;
}
}
true
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn idct_of_dct_is_identity() {
let mut block = [0.0f32; 64];
for (i, b) in block.iter_mut().enumerate() {
*b = ((i * 7) % 255) as f32 - 128.0;
}
let original = block;
fdct8x8(&mut block);
idct8x8(&mut block);
for i in 0..64 {
assert!((block[i] - original[i]).abs() < 1e-2);
}
}
#[test]
fn is_dc_only_detects_zero_ac() {
let mut block = [0.0f32; 64];
assert!(is_dc_only(&block), "all-zero block is DC-only");
block[0] = 1024.0;
assert!(is_dc_only(&block), "DC-only block (AC == 0) is DC-only");
block[5] = 1e-30;
assert!(
!is_dc_only(&block),
"any non-zero AC coefficient breaks the predicate"
);
block[5] = -0.0;
assert!(
is_dc_only(&block),
"negative zero AC is still mathematically zero"
);
}
#[test]
fn idct_dc_only_matches_general_idct() {
for &dc in &[-2048.0f32, -512.0, -1.0, 0.0, 1.0, 512.0, 2047.0, 4096.0] {
let mut a = [0.0f32; 64];
let mut b = [0.0f32; 64];
a[0] = dc;
b[0] = dc;
idct8x8(&mut a);
idct8x8_dc_only(&mut b);
for i in 0..64 {
assert!(
(a[i] - b[i]).abs() < 1e-4,
"DC={dc}: idct_dc_only[{i}] = {} vs idct[{i}] = {}",
b[i],
a[i]
);
}
}
}
#[test]
fn idct_dc_only_output_is_constant_dc_over_eight() {
let mut block = [0.0f32; 64];
block[0] = 800.0;
idct8x8_dc_only(&mut block);
for &s in block.iter() {
assert!((s - 100.0).abs() < 1e-4);
}
}
#[test]
fn is_constant_block_detects_uniform_input() {
let block = [42.0f32; 64];
assert!(is_constant_block(&block));
let mut diff = [42.0f32; 64];
diff[37] = 41.999;
assert!(!is_constant_block(&diff), "any differing entry breaks it");
let mut signed_zero = [0.0f32; 64];
signed_zero[5] = -0.0;
assert!(is_constant_block(&signed_zero));
}
#[test]
fn fdct_constant_matches_general_fdct() {
for &v in &[-256.0f32, -200.0, -1.0, 0.0, 1.0, 64.0, 128.0, 255.0] {
let mut a = [v; 64];
let mut b = [v; 64];
fdct8x8(&mut a);
fdct8x8_constant(&mut b);
for i in 0..64 {
assert!(
(a[i] - b[i]).abs() < 1e-3,
"v={v}: fdct_constant[{i}] = {} vs fdct[{i}] = {}",
b[i],
a[i]
);
}
}
}
#[test]
fn fdct_constant_emits_dc_equal_to_eight_v() {
let mut block = [12.5f32; 64];
fdct8x8_constant(&mut block);
assert!(
(block[0] - 100.0).abs() < 1e-4,
"DC = 8 * 12.5 = 100, got {}",
block[0]
);
for &s in &block[1..] {
assert_eq!(s, 0.0, "AC must be exactly 0 after constant fdct");
}
}
#[test]
fn fdct_constant_idct_dc_only_round_trip_is_identity() {
for &v in &[-256.0f32, -1.0, 0.0, 1.0, 100.0, 255.0] {
let mut block = [v; 64];
fdct8x8_constant(&mut block);
idct8x8_dc_only(&mut block);
for &s in block.iter() {
assert!(
(s - v).abs() < 1e-3,
"round-trip lost the constant: v={v}, got {s}"
);
}
}
}
}