use crate::cdf::{
ADST_ADST, ADST_DCT, ADST_FLIPADST, DCT_ADST, DCT_DCT, DCT_FLIPADST, FLIPADST_ADST,
FLIPADST_DCT, FLIPADST_FLIPADST, H_ADST, H_DCT, H_FLIPADST, IDTX, TX_HEIGHT, TX_SIZES_ALL,
TX_WIDTH, TX_WIDTH_LOG2, V_ADST, V_DCT, V_FLIPADST,
};
use super::forward_adst::{forward_adst_16, forward_adst_4, forward_adst_8};
use super::forward_identity::{forward_idtx_16, forward_idtx_32, forward_idtx_4, forward_idtx_8};
use super::forward_transform::{
forward_dct_16, forward_dct_32, forward_dct_4, forward_dct_64, forward_dct_8,
};
use super::forward_wht::forward_wht_4x4;
fn forward_row_kernel(t: &mut [i64], tx_type: usize, log2_w: u32) {
let r = 32; if matches!(tx_type, x if x == DCT_DCT || x == ADST_DCT || x == FLIPADST_DCT || x == H_DCT) {
forward_dct_dispatch(t, log2_w, r);
} else if matches!(
tx_type,
x if x == DCT_ADST
|| x == ADST_ADST
|| x == DCT_FLIPADST
|| x == FLIPADST_FLIPADST
|| x == ADST_FLIPADST
|| x == FLIPADST_ADST
|| x == H_ADST
|| x == H_FLIPADST
) {
forward_adst_dispatch(t, log2_w, r);
} else {
debug_assert!(
matches!(tx_type, x if x == IDTX || x == V_DCT || x == V_ADST || x == V_FLIPADST)
);
forward_idtx_dispatch(t, log2_w);
}
}
fn forward_col_kernel(t: &mut [i64], tx_type: usize, log2_h: u32) {
let r = 32;
if matches!(tx_type, x if x == DCT_DCT || x == DCT_ADST || x == DCT_FLIPADST || x == V_DCT) {
forward_dct_dispatch(t, log2_h, r);
} else if matches!(
tx_type,
x if x == ADST_DCT
|| x == ADST_ADST
|| x == FLIPADST_DCT
|| x == FLIPADST_FLIPADST
|| x == ADST_FLIPADST
|| x == FLIPADST_ADST
|| x == V_ADST
|| x == V_FLIPADST
) {
forward_adst_dispatch(t, log2_h, r);
} else {
debug_assert!(
matches!(tx_type, x if x == IDTX || x == H_DCT || x == H_ADST || x == H_FLIPADST)
);
forward_idtx_dispatch(t, log2_h);
}
}
fn forward_dct_dispatch(t: &mut [i64], n: u32, r: u32) {
match n {
2 => forward_dct_4(t, r),
3 => forward_dct_8(t, r),
4 => forward_dct_16(t, r),
5 => forward_dct_32(t, r),
6 => forward_dct_64(t, r),
_ => panic!("oxideav-av1 forward_dct_dispatch: n must be 2..=6, got {n}"),
}
}
fn forward_adst_dispatch(t: &mut [i64], n: u32, r: u32) {
match n {
2 => forward_adst_4(t, r),
3 => forward_adst_8(t, r),
4 => forward_adst_16(t, r),
_ => panic!(
"oxideav-av1 forward_adst_dispatch: ADST is only defined for n in 2..=4 \
(the §7.13.2.9 inverse-ADST dispatcher's range), got {n} — \
§6.10.19 tx_type derivation forces DCT_DCT outside this range",
),
}
}
fn forward_idtx_dispatch(t: &mut [i64], n: u32) {
match n {
2 => forward_idtx_4(t),
3 => forward_idtx_8(t),
4 => forward_idtx_16(t),
5 => forward_idtx_32(t),
_ => panic!(
"oxideav-av1 forward_idtx_dispatch: IDTX is only defined for n in 2..=5 \
(the §7.13.2.15 inverse-identity dispatcher's range), got {n} — \
§6.10.19 tx_type derivation forces DCT_DCT outside this range",
),
}
}
fn flip_axes(tx_type: usize) -> (bool, bool) {
let flip_rows = matches!(
tx_type,
x if x == FLIPADST_DCT
|| x == FLIPADST_FLIPADST
|| x == FLIPADST_ADST
|| x == V_FLIPADST
);
let flip_cols = matches!(
tx_type,
x if x == DCT_FLIPADST
|| x == FLIPADST_FLIPADST
|| x == ADST_FLIPADST
|| x == H_FLIPADST
);
(flip_rows, flip_cols)
}
fn apply_flip(input: &[i64], w: usize, h: usize, flip_rows: bool, flip_cols: bool) -> Vec<i64> {
if !flip_rows && !flip_cols {
return input.to_vec();
}
let mut out = vec![0i64; w * h];
for i in 0..h {
let src_i = if flip_rows { h - 1 - i } else { i };
for j in 0..w {
let src_j = if flip_cols { w - 1 - j } else { j };
out[i * w + j] = input[src_i * w + src_j];
}
}
out
}
pub fn forward_transform_2d(
input: &[i64],
tx_size: usize,
plane_tx_type: usize,
lossless: bool,
) -> Vec<i64> {
assert!(
tx_size < TX_SIZES_ALL,
"oxideav-av1 forward_transform_2d: tx_size {tx_size} out of range (TX_SIZES_ALL = {TX_SIZES_ALL})"
);
let log2_w = TX_WIDTH_LOG2[tx_size] as u32;
let log2_h = (TX_HEIGHT[tx_size] as u32).trailing_zeros();
let w = TX_WIDTH[tx_size];
let h = TX_HEIGHT[tx_size];
let is_square = log2_w == log2_h;
let min_log2 = core::cmp::min(log2_w, log2_h);
let max_log2 = core::cmp::max(log2_w, log2_h);
let diff = log2_w.abs_diff(log2_h);
let is_supported_rect_diff1 = diff == 1
&& (min_log2 == 2 || min_log2 == 3 || min_log2 == 4 || min_log2 == 5)
&& max_log2 == min_log2 + 1;
let is_supported_rect_diff2 = diff == 2
&& ((min_log2 == 2 && max_log2 == 4)
|| (min_log2 == 3 && max_log2 == 5)
|| (min_log2 == 4 && max_log2 == 6));
let is_supported_rect = is_supported_rect_diff1 || is_supported_rect_diff2;
assert!(
is_square || is_supported_rect,
"oxideav-av1 forward_transform_2d: rectangular tx_size {tx_size} (w={w}, h={h}) \
not supported in this arc — supported shapes are the five square sizes \
(TX_4X4 / TX_8X8 / TX_16X16 / TX_32X32 / TX_64X64), the eight \
|log2W - log2H| == 1 pairs (TX_4X8 / TX_8X4 / TX_8X16 / TX_16X8 / \
TX_16X32 / TX_32X16 / TX_32X64 / TX_64X32), and three \
|log2W - log2H| == 2 pairs (TX_16X64 / TX_64X16, \
TX_4X16 / TX_16X4, and TX_8X32 / TX_32X8)"
);
assert_eq!(
input.len(),
w * h,
"oxideav-av1 forward_transform_2d: input length {} != w*h = {}",
input.len(),
w * h
);
if lossless {
assert_eq!(
w, 4,
"oxideav-av1 forward_transform_2d: lossless arm requires tx_size = TX_4X4"
);
return forward_wht_4x4(input).to_vec();
}
let (flip_rows, flip_cols) = flip_axes(plane_tx_type);
let flipped = apply_flip(input, w, h, flip_rows, flip_cols);
let mut work = flipped;
let mut col_buf = vec![0i64; h];
for j in 0..w {
for i in 0..h {
col_buf[i] = work[i * w + j];
}
forward_col_kernel(&mut col_buf, plane_tx_type, log2_h);
for i in 0..h {
work[i * w + j] = col_buf[i];
}
}
let mut row_buf = vec![0i64; w];
for i in 0..h {
row_buf.copy_from_slice(&work[i * w..(i + 1) * w]);
forward_row_kernel(&mut row_buf, plane_tx_type, log2_w);
if log2_w.abs_diff(log2_h) == 1 {
for slot in row_buf.iter_mut() {
*slot = round2_12(*slot * 2896);
}
}
work[i * w..(i + 1) * w].copy_from_slice(&row_buf);
}
let sum = log2_w + log2_h;
let shift: i32 = if sum <= 8 {
4 - (sum / 2) as i32
} else {
8 - sum as i32
};
if shift > 0 {
for v in work.iter_mut() {
*v <<= shift;
}
} else if shift < 0 {
let s = (-shift) as u32;
let half = 1i64 << (s - 1);
for v in work.iter_mut() {
*v = (*v + half) >> s;
}
}
work
}
#[inline]
fn round2_12(x: i64) -> i64 {
(x + (1i64 << 11)) >> 12
}
#[cfg(test)]
mod tests {
use super::*;
use crate::cdf::{
TX_16X16, TX_16X32, TX_16X4, TX_16X64, TX_16X8, TX_32X16, TX_32X32, TX_32X64, TX_32X8,
TX_4X16, TX_4X4, TX_4X8, TX_64X16, TX_64X32, TX_64X64, TX_8X16, TX_8X32, TX_8X4, TX_8X8,
};
use crate::transform::inverse_transform_2d;
#[test]
fn lossless_tx_4x4_zero_input_yields_zero() {
let input = vec![0i64; 16];
let coeffs = forward_transform_2d(&input, TX_4X4, DCT_DCT, true);
assert_eq!(coeffs.len(), 16);
for &v in coeffs.iter() {
assert_eq!(v, 0);
}
}
#[test]
fn lossless_tx_4x4_bit_exact_roundtrip() {
let mut input = vec![0i64; 16];
let mut s: u64 = 0xCAFE_F00D_DEAD_BEEF;
for v in input.iter_mut() {
s = s
.wrapping_mul(6364136223846793005)
.wrapping_add(1442695040888963407);
*v = ((s >> 32) & 0xFF) as i64 - 128;
}
let coeffs = forward_transform_2d(&input, TX_4X4, DCT_DCT, true);
let recovered = inverse_transform_2d(&coeffs, TX_4X4, DCT_DCT, 8, true);
assert_eq!(
recovered, input,
"lossless WHT round-trip diverged from bit-exact"
);
}
#[test]
fn lossless_tx_4x4_bit_exact_roundtrip_extreme_values() {
for &v in [-256i64, -255, -128, -1, 0, 1, 127, 128, 255].iter() {
let input = vec![v; 16];
let coeffs = forward_transform_2d(&input, TX_4X4, DCT_DCT, true);
let recovered = inverse_transform_2d(&coeffs, TX_4X4, DCT_DCT, 8, true);
assert_eq!(recovered, input, "lossless extreme-value {v} diverged");
}
}
fn lcg_residual(seed: u64, n: usize) -> Vec<i64> {
lcg_residual_bound(seed, n, 128)
}
fn lcg_residual_bound(seed: u64, n: usize, bound: i64) -> Vec<i64> {
let mut out = vec![0i64; n];
let mut s = seed;
for v in out.iter_mut() {
s = s
.wrapping_mul(6364136223846793005)
.wrapping_add(1442695040888963407);
let lim = 2 * bound + 1;
let raw = ((s >> 32) & 0xFFFF) as i64;
*v = (raw % lim) - bound;
}
out
}
fn check_roundtrip_frac(
input: &[i64],
tx_size: usize,
tx_type: usize,
scale_num: i64,
denom: i64,
max_err: i64,
) {
let coeffs = forward_transform_2d(input, tx_size, tx_type, false);
let recovered = inverse_transform_2d(&coeffs, tx_size, tx_type, 8, false);
assert_eq!(recovered.len(), input.len());
for (i, (&got, &orig)) in recovered.iter().zip(input.iter()).enumerate() {
let lhs = got * denom;
let rhs = scale_num * orig;
let err = (lhs - rhs).abs();
let bound = max_err * denom;
assert!(
err <= bound,
"tx_size={tx_size} tx_type={tx_type} cell {i}: orig={orig}, \
got={got}, expected≈{scale_num}/{denom}*orig={}, |err*{denom}|={err} > bound {bound}",
rhs as f64 / denom as f64,
);
}
}
fn check_roundtrip_flip(
input: &[i64],
tx_size: usize,
tx_type: usize,
scale_num: i64,
denom: i64,
max_err: i64,
) {
let coeffs = forward_transform_2d(input, tx_size, tx_type, false);
let recovered = inverse_transform_2d(&coeffs, tx_size, tx_type, 8, false);
let w = TX_WIDTH[tx_size];
let h = TX_HEIGHT[tx_size];
let (flip_rows, flip_cols) = flip_axes(tx_type);
let flipped = apply_flip(input, w, h, flip_rows, flip_cols);
assert_eq!(recovered.len(), flipped.len());
for (i, (&got, &orig)) in recovered.iter().zip(flipped.iter()).enumerate() {
let lhs = got * denom;
let rhs = scale_num * orig;
let err = (lhs - rhs).abs();
let bound = max_err * denom;
assert!(
err <= bound,
"tx_size={tx_size} tx_type={tx_type} cell {i}: \
flipped_orig={orig}, got={got}, \
expected≈{scale_num}/{denom}*flipped_orig={}, |err*{denom}|={err} > bound {bound}",
rhs as f64 / denom as f64,
);
}
}
#[test]
fn dct_dct_tx_4x4_roundtrip() {
let input = lcg_residual(0x1111_2222_3333_4444, 16);
check_roundtrip_frac(&input, TX_4X4, DCT_DCT, 1, 1, 16);
}
#[test]
fn dct_dct_tx_8x8_roundtrip() {
let input = lcg_residual(0x5555_6666_7777_8888, 64);
check_roundtrip_frac(&input, TX_8X8, DCT_DCT, 1, 1, 16);
}
#[test]
fn dct_dct_tx_16x16_roundtrip() {
let input = lcg_residual_bound(0x9999_AAAA_BBBB_CCCC, 256, 32);
check_roundtrip_frac(&input, TX_16X16, DCT_DCT, 1, 1, 16);
}
#[test]
fn dct_dct_tx_32x32_roundtrip() {
let input = lcg_residual_bound(0xDEAD_BEEF_F00D_CAFE, 1024, 8);
check_roundtrip_frac(&input, TX_32X32, DCT_DCT, 1, 1, 16);
}
#[test]
fn dct_dct_tx_64x64_roundtrip() {
let input = lcg_residual_bound(0x0123_4567_89AB_CDEF, 4096, 2);
check_roundtrip_frac(&input, TX_64X64, DCT_DCT, 1, 1, 64);
}
#[test]
fn adst_adst_tx_4x4_roundtrip() {
let input = lcg_residual(0xABCD_EF01_2345_6789, 16);
check_roundtrip_frac(&input, TX_4X4, ADST_ADST, 1, 1, 16);
}
#[test]
fn adst_dct_tx_8x8_roundtrip() {
let input = lcg_residual(0xFACE_BEEF_CAFE_BABE, 64);
check_roundtrip_frac(&input, TX_8X8, ADST_DCT, 1, 1, 16);
}
#[test]
fn dct_adst_tx_8x8_roundtrip() {
let input = lcg_residual(0xBADD_CAFE_F00D_0BAD, 64);
check_roundtrip_frac(&input, TX_8X8, DCT_ADST, 1, 1, 16);
}
#[test]
fn adst_adst_tx_16x16_roundtrip() {
let input = lcg_residual_bound(0x1357_9BDF_2468_ACE0, 256, 32);
check_roundtrip_frac(&input, TX_16X16, ADST_ADST, 1, 1, 16);
}
#[test]
fn flipadst_flipadst_tx_4x4_roundtrip() {
let input = lcg_residual(0x2233_4455_6677_8899, 16);
check_roundtrip_flip(&input, TX_4X4, FLIPADST_FLIPADST, 1, 1, 16);
}
#[test]
fn flipadst_dct_tx_8x8_roundtrip() {
let input = lcg_residual(0xAA55_AA55_AA55_AA55, 64);
check_roundtrip_flip(&input, TX_8X8, FLIPADST_DCT, 1, 1, 16);
}
#[test]
fn dct_flipadst_tx_8x8_roundtrip() {
let input = lcg_residual(0x55AA_55AA_55AA_55AA, 64);
check_roundtrip_flip(&input, TX_8X8, DCT_FLIPADST, 1, 1, 16);
}
#[test]
fn adst_flipadst_tx_16x16_roundtrip() {
let input = lcg_residual_bound(0xC0DE_F00D_BABE_FACE, 256, 32);
check_roundtrip_flip(&input, TX_16X16, ADST_FLIPADST, 1, 1, 16);
}
#[test]
fn flipadst_adst_tx_16x16_roundtrip() {
let input = lcg_residual_bound(0xFACE_BABE_F00D_C0DE, 256, 32);
check_roundtrip_flip(&input, TX_16X16, FLIPADST_ADST, 1, 1, 16);
}
#[test]
fn idtx_tx_4x4_roundtrip() {
let input = lcg_residual(0x1234_5678_9ABC_DEF0, 16);
check_roundtrip_frac(&input, TX_4X4, IDTX, 1, 1, 16);
}
#[test]
fn idtx_tx_8x8_roundtrip() {
let input = lcg_residual(0xDEAD_FACE_BEEF_CAFE, 64);
check_roundtrip_frac(&input, TX_8X8, IDTX, 1, 1, 16);
}
#[test]
fn idtx_tx_16x16_roundtrip() {
let input = lcg_residual_bound(0xBAD0_F00D_DEAD_BEEF, 256, 32);
check_roundtrip_frac(&input, TX_16X16, IDTX, 1, 1, 16);
}
#[test]
fn idtx_tx_32x32_roundtrip() {
let input = lcg_residual_bound(0xC001_D00D_FACE_FEED, 1024, 8);
check_roundtrip_frac(&input, TX_32X32, IDTX, 1, 1, 16);
}
#[test]
fn v_dct_tx_4x4_roundtrip() {
let input = lcg_residual(0x1010_2020_3030_4040, 16);
check_roundtrip_frac(&input, TX_4X4, V_DCT, 1, 1, 16);
}
#[test]
fn h_dct_tx_8x8_roundtrip() {
let input = lcg_residual(0x5050_6060_7070_8080, 64);
check_roundtrip_frac(&input, TX_8X8, H_DCT, 1, 1, 16);
}
#[test]
fn rect_tx_4x8_zero_input_yields_zero() {
let input = vec![0i64; 32];
let coeffs = forward_transform_2d(&input, TX_4X8, DCT_DCT, false);
assert_eq!(coeffs.len(), 32);
for (i, &v) in coeffs.iter().enumerate() {
assert_eq!(v, 0, "TX_4X8 cell {i}: zero in ⇒ zero out, got {v}");
}
}
#[test]
fn rect_tx_8x4_zero_input_yields_zero() {
let input = vec![0i64; 32];
let coeffs = forward_transform_2d(&input, TX_8X4, DCT_DCT, false);
assert_eq!(coeffs.len(), 32);
for (i, &v) in coeffs.iter().enumerate() {
assert_eq!(v, 0, "TX_8X4 cell {i}: zero in ⇒ zero out, got {v}");
}
}
#[test]
fn rect_tx_4x8_dct_dct_roundtrip() {
let input = lcg_residual(0x1357_2468_ACE0_BDF1, 32);
check_roundtrip_frac(&input, TX_4X8, DCT_DCT, 1, 1, 16);
}
#[test]
fn rect_tx_8x4_dct_dct_roundtrip() {
let input = lcg_residual(0x2468_ACE0_BDF1_1357, 32);
check_roundtrip_frac(&input, TX_8X4, DCT_DCT, 1, 1, 16);
}
#[test]
fn rect_tx_4x8_dc_input_dc_only_coefficient() {
let input = vec![64i64; 32];
let coeffs = forward_transform_2d(&input, TX_4X8, DCT_DCT, false);
assert_eq!(coeffs.len(), 32);
let dc = coeffs[0];
let max_off = coeffs[1..].iter().map(|c| c.abs()).max().unwrap_or(0);
assert!(
dc.abs() > 10 * max_off.max(1),
"TX_4X8 DC = {dc}, max off-DC = {max_off} — DC should dominate"
);
let recovered = inverse_transform_2d(&coeffs, TX_4X8, DCT_DCT, 8, false);
for (i, &v) in recovered.iter().enumerate() {
assert!(
(v - 64).abs() <= 2,
"TX_4X8 constant-DC round-trip cell {i}: got {v}, expected ≈ 64"
);
}
}
#[test]
fn rect_tx_8x4_dc_input_dc_only_coefficient() {
let input = vec![64i64; 32];
let coeffs = forward_transform_2d(&input, TX_8X4, DCT_DCT, false);
assert_eq!(coeffs.len(), 32);
let dc = coeffs[0];
let max_off = coeffs[1..].iter().map(|c| c.abs()).max().unwrap_or(0);
assert!(
dc.abs() > 10 * max_off.max(1),
"TX_8X4 DC = {dc}, max off-DC = {max_off} — DC should dominate"
);
let recovered = inverse_transform_2d(&coeffs, TX_8X4, DCT_DCT, 8, false);
for (i, &v) in recovered.iter().enumerate() {
assert!(
(v - 64).abs() <= 2,
"TX_8X4 constant-DC round-trip cell {i}: got {v}, expected ≈ 64"
);
}
}
#[test]
fn rect_tx_4x8_adst_dct_roundtrip() {
let input = lcg_residual(0xFACE_BABE_F00D_CAFE, 32);
check_roundtrip_frac(&input, TX_4X8, ADST_DCT, 1, 1, 16);
}
#[test]
fn rect_tx_8x4_dct_adst_roundtrip() {
let input = lcg_residual(0xCAFE_F00D_BABE_FACE, 32);
check_roundtrip_frac(&input, TX_8X4, DCT_ADST, 1, 1, 16);
}
#[test]
fn rect_tx_4x8_idtx_roundtrip() {
let input = lcg_residual(0x1010_2020_3030_4040, 32);
check_roundtrip_frac(&input, TX_4X8, IDTX, 1, 1, 16);
}
#[test]
fn rect_tx_8x4_flipadst_flipadst_roundtrip() {
let input = lcg_residual(0xDEAD_BEEF_CAFE_BABE, 32);
check_roundtrip_flip(&input, TX_8X4, FLIPADST_FLIPADST, 1, 1, 16);
}
#[test]
fn rect_tx_8x16_zero_input_yields_zero() {
let input = vec![0i64; 8 * 16];
let coeffs = forward_transform_2d(&input, TX_8X16, DCT_DCT, false);
assert_eq!(coeffs.len(), 8 * 16);
for (i, &v) in coeffs.iter().enumerate() {
assert_eq!(v, 0, "TX_8X16 cell {i}: zero in ⇒ zero out, got {v}");
}
}
#[test]
fn rect_tx_16x8_zero_input_yields_zero() {
let input = vec![0i64; 16 * 8];
let coeffs = forward_transform_2d(&input, TX_16X8, DCT_DCT, false);
assert_eq!(coeffs.len(), 16 * 8);
for (i, &v) in coeffs.iter().enumerate() {
assert_eq!(v, 0, "TX_16X8 cell {i}: zero in ⇒ zero out, got {v}");
}
}
#[test]
fn rect_tx_8x16_dct_dct_roundtrip() {
let input = lcg_residual_bound(0xCAFE_8B16_DEAD_BEEF, 8 * 16, 32);
check_roundtrip_frac(&input, TX_8X16, DCT_DCT, 1, 1, 16);
}
#[test]
fn rect_tx_16x8_dct_dct_roundtrip() {
let input = lcg_residual_bound(0xBEEF_16B8_F00D_CAFE, 16 * 8, 32);
check_roundtrip_frac(&input, TX_16X8, DCT_DCT, 1, 1, 16);
}
#[test]
fn rect_tx_8x16_dc_input_dc_only_coefficient() {
let input = vec![32i64; 8 * 16];
let coeffs = forward_transform_2d(&input, TX_8X16, DCT_DCT, false);
assert_eq!(coeffs.len(), 8 * 16);
let dc = coeffs[0];
let max_off = coeffs[1..].iter().map(|c| c.abs()).max().unwrap_or(0);
assert!(
dc.abs() > 10 * max_off.max(1),
"TX_8X16 DC = {dc}, max off-DC = {max_off} — DC should dominate"
);
let recovered = inverse_transform_2d(&coeffs, TX_8X16, DCT_DCT, 8, false);
for (i, &v) in recovered.iter().enumerate() {
assert!(
(v - 32).abs() <= 2,
"TX_8X16 constant-DC round-trip cell {i}: got {v}, expected ≈ 32"
);
}
}
#[test]
fn rect_tx_16x8_dc_input_dc_only_coefficient() {
let input = vec![32i64; 16 * 8];
let coeffs = forward_transform_2d(&input, TX_16X8, DCT_DCT, false);
assert_eq!(coeffs.len(), 16 * 8);
let dc = coeffs[0];
let max_off = coeffs[1..].iter().map(|c| c.abs()).max().unwrap_or(0);
assert!(
dc.abs() > 10 * max_off.max(1),
"TX_16X8 DC = {dc}, max off-DC = {max_off} — DC should dominate"
);
let recovered = inverse_transform_2d(&coeffs, TX_16X8, DCT_DCT, 8, false);
for (i, &v) in recovered.iter().enumerate() {
assert!(
(v - 32).abs() <= 2,
"TX_16X8 constant-DC round-trip cell {i}: got {v}, expected ≈ 32"
);
}
}
#[test]
fn rect_tx_8x16_adst_dct_roundtrip() {
let input = lcg_residual_bound(0x8B16_AD0C_DEAD_BEEF, 8 * 16, 32);
check_roundtrip_frac(&input, TX_8X16, ADST_DCT, 1, 1, 16);
}
#[test]
fn rect_tx_16x8_dct_adst_roundtrip() {
let input = lcg_residual_bound(0x16B8_DCAD_F00D_CAFE, 16 * 8, 32);
check_roundtrip_frac(&input, TX_16X8, DCT_ADST, 1, 1, 16);
}
#[test]
fn rect_tx_8x16_idtx_roundtrip() {
let input = lcg_residual_bound(0x8B16_1D70_BEEF_CAFE, 8 * 16, 32);
check_roundtrip_frac(&input, TX_8X16, IDTX, 1, 1, 16);
}
#[test]
fn rect_tx_16x8_flipadst_flipadst_roundtrip() {
let input = lcg_residual_bound(0x16B8_F1F1_DEAD_BEEF, 16 * 8, 32);
check_roundtrip_flip(&input, TX_16X8, FLIPADST_FLIPADST, 1, 1, 16);
}
#[test]
fn rect_tx_8x16_adst_adst_roundtrip() {
let input = lcg_residual_bound(0x8B16_AAAA_BABE_FACE, 8 * 16, 32);
check_roundtrip_frac(&input, TX_8X16, ADST_ADST, 1, 1, 16);
}
#[test]
fn rect_tx_16x32_zero_input_yields_zero() {
let input = vec![0i64; 16 * 32];
let coeffs = forward_transform_2d(&input, TX_16X32, DCT_DCT, false);
assert_eq!(coeffs.len(), 16 * 32);
for (i, &v) in coeffs.iter().enumerate() {
assert_eq!(v, 0, "TX_16X32 cell {i}: zero in ⇒ zero out, got {v}");
}
}
#[test]
fn rect_tx_32x16_zero_input_yields_zero() {
let input = vec![0i64; 32 * 16];
let coeffs = forward_transform_2d(&input, TX_32X16, DCT_DCT, false);
assert_eq!(coeffs.len(), 32 * 16);
for (i, &v) in coeffs.iter().enumerate() {
assert_eq!(v, 0, "TX_32X16 cell {i}: zero in ⇒ zero out, got {v}");
}
}
#[test]
fn rect_tx_16x32_dct_dct_roundtrip() {
let input = lcg_residual_bound(0xCAFE_1632_DEAD_BEEF, 16 * 32, 8);
check_roundtrip_frac(&input, TX_16X32, DCT_DCT, 1, 1, 4);
}
#[test]
fn rect_tx_32x16_dct_dct_roundtrip() {
let input = lcg_residual_bound(0xBEEF_3216_F00D_CAFE, 32 * 16, 8);
check_roundtrip_frac(&input, TX_32X16, DCT_DCT, 1, 1, 4);
}
#[test]
fn rect_tx_16x32_dc_input_dc_only_coefficient() {
let input = vec![8i64; 16 * 32];
let coeffs = forward_transform_2d(&input, TX_16X32, DCT_DCT, false);
assert_eq!(coeffs.len(), 16 * 32);
let dc = coeffs[0];
let max_off = coeffs[1..].iter().map(|c| c.abs()).max().unwrap_or(0);
assert!(
dc.abs() > 10 * max_off.max(1),
"TX_16X32 DC = {dc}, max off-DC = {max_off} — DC should dominate"
);
let recovered = inverse_transform_2d(&coeffs, TX_16X32, DCT_DCT, 8, false);
for (i, &v) in recovered.iter().enumerate() {
assert!(
(v - 8).abs() <= 2,
"TX_16X32 constant-DC round-trip cell {i}: got {v}, expected ≈ 8"
);
}
}
#[test]
fn rect_tx_32x16_dc_input_dc_only_coefficient() {
let input = vec![8i64; 32 * 16];
let coeffs = forward_transform_2d(&input, TX_32X16, DCT_DCT, false);
assert_eq!(coeffs.len(), 32 * 16);
let dc = coeffs[0];
let max_off = coeffs[1..].iter().map(|c| c.abs()).max().unwrap_or(0);
assert!(
dc.abs() > 10 * max_off.max(1),
"TX_32X16 DC = {dc}, max off-DC = {max_off} — DC should dominate"
);
let recovered = inverse_transform_2d(&coeffs, TX_32X16, DCT_DCT, 8, false);
for (i, &v) in recovered.iter().enumerate() {
assert!(
(v - 8).abs() <= 2,
"TX_32X16 constant-DC round-trip cell {i}: got {v}, expected ≈ 8"
);
}
}
#[test]
fn rect_tx_16x32_dct_adst_roundtrip() {
let input = lcg_residual_bound(0x1632_AD0C_DEAD_BEEF, 16 * 32, 8);
check_roundtrip_frac(&input, TX_16X32, DCT_ADST, 1, 1, 4);
}
#[test]
fn rect_tx_32x16_adst_dct_roundtrip() {
let input = lcg_residual_bound(0x3216_DCAD_F00D_CAFE, 32 * 16, 8);
check_roundtrip_frac(&input, TX_32X16, ADST_DCT, 1, 1, 4);
}
#[test]
fn rect_tx_16x32_idtx_roundtrip() {
let input = lcg_residual_bound(0x1632_1D70_BEEF_CAFE, 16 * 32, 4);
let coeffs = forward_transform_2d(&input, TX_16X32, IDTX, false);
let recovered = inverse_transform_2d(&coeffs, TX_16X32, IDTX, 8, false);
assert_eq!(recovered.len(), input.len());
let nonzero = recovered.iter().any(|&v| v != 0);
assert!(nonzero, "TX_16X32 IDTX round-trip produced all zeros");
}
#[test]
fn rect_tx_32x16_idtx_roundtrip() {
let input = lcg_residual_bound(0x3216_1D70_CAFE_BEEF, 32 * 16, 4);
let coeffs = forward_transform_2d(&input, TX_32X16, IDTX, false);
let recovered = inverse_transform_2d(&coeffs, TX_32X16, IDTX, 8, false);
assert_eq!(recovered.len(), input.len());
let nonzero = recovered.iter().any(|&v| v != 0);
assert!(nonzero, "TX_32X16 IDTX round-trip produced all zeros");
}
#[test]
fn rect_tx_16x32_v_dct_roundtrip() {
let input = lcg_residual_bound(0x1632_5DC7_BABE_F00D, 16 * 32, 8);
check_roundtrip_frac(&input, TX_16X32, V_DCT, 1, 1, 4);
}
#[test]
fn rect_tx_32x16_h_dct_roundtrip() {
let input = lcg_residual_bound(0x3216_4DC7_FACE_BABE, 32 * 16, 8);
check_roundtrip_frac(&input, TX_32X16, H_DCT, 1, 1, 4);
}
#[test]
fn rect_tx_32x64_zero_input_yields_zero() {
let input = vec![0i64; 32 * 64];
let coeffs = forward_transform_2d(&input, TX_32X64, DCT_DCT, false);
assert_eq!(coeffs.len(), 32 * 64);
for (i, &v) in coeffs.iter().enumerate() {
assert_eq!(v, 0, "TX_32X64 cell {i}: zero in ⇒ zero out, got {v}");
}
}
#[test]
fn rect_tx_64x32_zero_input_yields_zero() {
let input = vec![0i64; 64 * 32];
let coeffs = forward_transform_2d(&input, TX_64X32, DCT_DCT, false);
assert_eq!(coeffs.len(), 64 * 32);
for (i, &v) in coeffs.iter().enumerate() {
assert_eq!(v, 0, "TX_64X32 cell {i}: zero in ⇒ zero out, got {v}");
}
}
#[test]
fn rect_tx_32x64_dct_dct_roundtrip() {
let input = lcg_residual_bound(0xCAFE_3264_DEAD_BEEF, 32 * 64, 2);
check_roundtrip_frac(&input, TX_32X64, DCT_DCT, 1, 1, 64);
}
#[test]
fn rect_tx_64x32_dct_dct_roundtrip() {
let input = lcg_residual_bound(0xBEEF_6432_F00D_CAFE, 64 * 32, 2);
check_roundtrip_frac(&input, TX_64X32, DCT_DCT, 1, 1, 64);
}
#[test]
fn rect_tx_32x64_dc_input_dc_only_coefficient() {
let input = vec![2i64; 32 * 64];
let coeffs = forward_transform_2d(&input, TX_32X64, DCT_DCT, false);
assert_eq!(coeffs.len(), 32 * 64);
let dc = coeffs[0];
let max_off = coeffs[1..].iter().map(|c| c.abs()).max().unwrap_or(0);
assert!(
dc.abs() > 10 * max_off.max(1),
"TX_32X64 DC = {dc}, max off-DC = {max_off} — DC should dominate"
);
let recovered = inverse_transform_2d(&coeffs, TX_32X64, DCT_DCT, 8, false);
for (i, &v) in recovered.iter().enumerate() {
assert!(
(v - 2).abs() <= 2,
"TX_32X64 constant-DC round-trip cell {i}: got {v}, expected ≈ 2"
);
}
}
#[test]
fn rect_tx_64x32_dc_input_dc_only_coefficient() {
let input = vec![2i64; 64 * 32];
let coeffs = forward_transform_2d(&input, TX_64X32, DCT_DCT, false);
assert_eq!(coeffs.len(), 64 * 32);
let dc = coeffs[0];
let max_off = coeffs[1..].iter().map(|c| c.abs()).max().unwrap_or(0);
assert!(
dc.abs() > 10 * max_off.max(1),
"TX_64X32 DC = {dc}, max off-DC = {max_off} — DC should dominate"
);
let recovered = inverse_transform_2d(&coeffs, TX_64X32, DCT_DCT, 8, false);
for (i, &v) in recovered.iter().enumerate() {
assert!(
(v - 2).abs() <= 2,
"TX_64X32 constant-DC round-trip cell {i}: got {v}, expected ≈ 2"
);
}
}
#[test]
fn rect_tx_32x64_v_dct_roundtrip() {
let input = lcg_residual_bound(0x3264_5DC7_BABE_F00D, 32 * 64, 2);
check_roundtrip_frac(&input, TX_32X64, V_DCT, 1, 1, 64);
}
#[test]
fn rect_tx_64x32_h_dct_roundtrip() {
let input = lcg_residual_bound(0x6432_4DC7_FACE_BABE, 64 * 32, 2);
check_roundtrip_frac(&input, TX_64X32, H_DCT, 1, 1, 64);
}
#[test]
fn rect_tx_16x64_zero_input_yields_zero() {
let input = vec![0i64; 16 * 64];
let coeffs = forward_transform_2d(&input, TX_16X64, DCT_DCT, false);
assert_eq!(coeffs.len(), 16 * 64);
for (i, &v) in coeffs.iter().enumerate() {
assert_eq!(v, 0, "TX_16X64 cell {i}: zero in ⇒ zero out, got {v}");
}
}
#[test]
fn rect_tx_64x16_zero_input_yields_zero() {
let input = vec![0i64; 64 * 16];
let coeffs = forward_transform_2d(&input, TX_64X16, DCT_DCT, false);
assert_eq!(coeffs.len(), 64 * 16);
for (i, &v) in coeffs.iter().enumerate() {
assert_eq!(v, 0, "TX_64X16 cell {i}: zero in ⇒ zero out, got {v}");
}
}
#[test]
fn rect_tx_16x64_dct_dct_roundtrip() {
let input = lcg_residual_bound(0xCAFE_1664_DEAD_BEEF, 16 * 64, 2);
check_roundtrip_frac(&input, TX_16X64, DCT_DCT, 1, 1, 64);
}
#[test]
fn rect_tx_64x16_dct_dct_roundtrip() {
let input = lcg_residual_bound(0xBEEF_6416_F00D_CAFE, 64 * 16, 2);
check_roundtrip_frac(&input, TX_64X16, DCT_DCT, 1, 1, 64);
}
#[test]
fn rect_tx_16x64_dc_input_dc_only_coefficient() {
let input = vec![4i64; 16 * 64];
let coeffs = forward_transform_2d(&input, TX_16X64, DCT_DCT, false);
assert_eq!(coeffs.len(), 16 * 64);
let dc = coeffs[0];
let max_off = coeffs[1..].iter().map(|c| c.abs()).max().unwrap_or(0);
assert!(
dc.abs() > 10 * max_off.max(1),
"TX_16X64 DC = {dc}, max off-DC = {max_off} — DC should dominate"
);
let recovered = inverse_transform_2d(&coeffs, TX_16X64, DCT_DCT, 8, false);
for (i, &v) in recovered.iter().enumerate() {
assert!(
(v - 4).abs() <= 2,
"TX_16X64 constant-DC round-trip cell {i}: got {v}, expected ≈ 4"
);
}
}
#[test]
fn rect_tx_64x16_dc_input_dc_only_coefficient() {
let input = vec![4i64; 64 * 16];
let coeffs = forward_transform_2d(&input, TX_64X16, DCT_DCT, false);
assert_eq!(coeffs.len(), 64 * 16);
let dc = coeffs[0];
let max_off = coeffs[1..].iter().map(|c| c.abs()).max().unwrap_or(0);
assert!(
dc.abs() > 10 * max_off.max(1),
"TX_64X16 DC = {dc}, max off-DC = {max_off} — DC should dominate"
);
let recovered = inverse_transform_2d(&coeffs, TX_64X16, DCT_DCT, 8, false);
for (i, &v) in recovered.iter().enumerate() {
assert!(
(v - 4).abs() <= 2,
"TX_64X16 constant-DC round-trip cell {i}: got {v}, expected ≈ 4"
);
}
}
#[test]
fn rect_tx_4x16_zero_input_yields_zero() {
let input = vec![0i64; 4 * 16];
let coeffs = forward_transform_2d(&input, TX_4X16, DCT_DCT, false);
assert_eq!(coeffs.len(), 4 * 16);
for (i, &v) in coeffs.iter().enumerate() {
assert_eq!(v, 0, "TX_4X16 cell {i}: zero in ⇒ zero out, got {v}");
}
}
#[test]
fn rect_tx_16x4_zero_input_yields_zero() {
let input = vec![0i64; 16 * 4];
let coeffs = forward_transform_2d(&input, TX_16X4, DCT_DCT, false);
assert_eq!(coeffs.len(), 16 * 4);
for (i, &v) in coeffs.iter().enumerate() {
assert_eq!(v, 0, "TX_16X4 cell {i}: zero in ⇒ zero out, got {v}");
}
}
#[test]
fn rect_tx_4x16_dct_dct_roundtrip() {
let input = lcg_residual_bound(0x416B_EEF1_DEAD_F00D, 4 * 16, 128);
check_roundtrip_frac(&input, TX_4X16, DCT_DCT, 1, 1, 6);
}
#[test]
fn rect_tx_16x4_dct_dct_roundtrip() {
let input = lcg_residual_bound(0x164B_EEF2_F00D_CAFE, 16 * 4, 128);
check_roundtrip_frac(&input, TX_16X4, DCT_DCT, 1, 1, 6);
}
#[test]
fn rect_tx_4x16_adst_adst_roundtrip() {
let input = lcg_residual_bound(0x4A6B_EEF3_C0DE_BABE, 4 * 16, 128);
check_roundtrip_frac(&input, TX_4X16, ADST_ADST, 1, 1, 6);
}
#[test]
fn rect_tx_16x4_adst_adst_roundtrip() {
let input = lcg_residual_bound(0x16AB_EEF4_DECA_F00D, 16 * 4, 128);
check_roundtrip_frac(&input, TX_16X4, ADST_ADST, 1, 1, 6);
}
#[test]
fn rect_tx_4x16_idtx_roundtrip() {
let input = lcg_residual_bound(0x40C0_FFEE_BEEF_1664, 4 * 16, 128);
check_roundtrip_frac(&input, TX_4X16, IDTX, 1, 1, 6);
}
#[test]
fn rect_tx_16x4_idtx_roundtrip() {
let input = lcg_residual_bound(0x16C0_FFEE_BEEF_BABE, 16 * 4, 128);
check_roundtrip_frac(&input, TX_16X4, IDTX, 1, 1, 6);
}
#[test]
fn rect_tx_4x16_flipadst_flipadst_roundtrip() {
let input = lcg_residual_bound(0x4F1A_DEAD_C0DE_4F00, 4 * 16, 128);
check_roundtrip_flip(&input, TX_4X16, FLIPADST_FLIPADST, 1, 1, 6);
}
#[test]
fn rect_tx_16x4_v_dct_roundtrip() {
let input = lcg_residual_bound(0x164D_C742_BABE_C0DE, 16 * 4, 128);
check_roundtrip_frac(&input, TX_16X4, V_DCT, 1, 1, 6);
}
#[test]
fn rect_tx_4x16_h_adst_roundtrip() {
let input = lcg_residual_bound(0x4A57_DEAD_BEEF_416B, 4 * 16, 128);
check_roundtrip_frac(&input, TX_4X16, H_ADST, 1, 1, 6);
}
#[test]
fn rect_tx_4x16_dc_input_dc_only_coefficient() {
let input = vec![4i64; 4 * 16];
let coeffs = forward_transform_2d(&input, TX_4X16, DCT_DCT, false);
assert_eq!(coeffs.len(), 4 * 16);
let dc = coeffs[0];
let max_off = coeffs[1..].iter().map(|c| c.abs()).max().unwrap_or(0);
assert!(
dc.abs() > 10 * max_off.max(1),
"TX_4X16 DC = {dc}, max off-DC = {max_off} — DC should dominate"
);
let recovered = inverse_transform_2d(&coeffs, TX_4X16, DCT_DCT, 8, false);
for (i, &v) in recovered.iter().enumerate() {
assert!(
(v - 4).abs() <= 1,
"TX_4X16 constant-DC round-trip cell {i}: got {v}, expected ≈ 4"
);
}
}
#[test]
fn rect_tx_16x4_dc_input_dc_only_coefficient() {
let input = vec![4i64; 16 * 4];
let coeffs = forward_transform_2d(&input, TX_16X4, DCT_DCT, false);
assert_eq!(coeffs.len(), 16 * 4);
let dc = coeffs[0];
let max_off = coeffs[1..].iter().map(|c| c.abs()).max().unwrap_or(0);
assert!(
dc.abs() > 10 * max_off.max(1),
"TX_16X4 DC = {dc}, max off-DC = {max_off} — DC should dominate"
);
let recovered = inverse_transform_2d(&coeffs, TX_16X4, DCT_DCT, 8, false);
for (i, &v) in recovered.iter().enumerate() {
assert!(
(v - 4).abs() <= 1,
"TX_16X4 constant-DC round-trip cell {i}: got {v}, expected ≈ 4"
);
}
}
#[test]
fn rect_tx_8x32_zero_input_yields_zero() {
let input = vec![0i64; 8 * 32];
let coeffs = forward_transform_2d(&input, TX_8X32, DCT_DCT, false);
assert_eq!(coeffs.len(), 8 * 32);
for (i, &v) in coeffs.iter().enumerate() {
assert_eq!(v, 0, "TX_8X32 cell {i}: zero in ⇒ zero out, got {v}");
}
}
#[test]
fn rect_tx_32x8_zero_input_yields_zero() {
let input = vec![0i64; 32 * 8];
let coeffs = forward_transform_2d(&input, TX_32X8, DCT_DCT, false);
assert_eq!(coeffs.len(), 32 * 8);
for (i, &v) in coeffs.iter().enumerate() {
assert_eq!(v, 0, "TX_32X8 cell {i}: zero in ⇒ zero out, got {v}");
}
}
#[test]
fn rect_tx_8x32_dct_dct_roundtrip() {
let input = lcg_residual_bound(0x8B32_DEAD_C0DE_BEEF, 8 * 32, 32);
check_roundtrip_frac(&input, TX_8X32, DCT_DCT, 1, 1, 64);
}
#[test]
fn rect_tx_32x8_dct_dct_roundtrip() {
let input = lcg_residual_bound(0x32B8_BEEF_F00D_CAFE, 32 * 8, 32);
check_roundtrip_frac(&input, TX_32X8, DCT_DCT, 1, 1, 64);
}
#[test]
fn rect_tx_8x32_idtx_roundtrip() {
let input = lcg_residual_bound(0x8C0F_FEEB_EEF8_3200, 8 * 32, 32);
check_roundtrip_frac(&input, TX_8X32, IDTX, 1, 1, 64);
}
#[test]
fn rect_tx_32x8_idtx_roundtrip() {
let input = lcg_residual_bound(0x32C0_FFEE_BEEF_8800, 32 * 8, 32);
check_roundtrip_frac(&input, TX_32X8, IDTX, 1, 1, 64);
}
#[test]
fn rect_tx_8x32_dc_input_dc_only_coefficient() {
let input = vec![4i64; 8 * 32];
let coeffs = forward_transform_2d(&input, TX_8X32, DCT_DCT, false);
assert_eq!(coeffs.len(), 8 * 32);
let dc = coeffs[0];
let max_off = coeffs[1..].iter().map(|c| c.abs()).max().unwrap_or(0);
assert!(
dc.abs() > 10 * max_off.max(1),
"TX_8X32 DC = {dc}, max off-DC = {max_off} — DC should dominate"
);
let recovered = inverse_transform_2d(&coeffs, TX_8X32, DCT_DCT, 8, false);
for (i, &v) in recovered.iter().enumerate() {
assert!(
(v - 4).abs() <= 1,
"TX_8X32 constant-DC round-trip cell {i}: got {v}, expected ≈ 4"
);
}
}
#[test]
fn rect_tx_32x8_dc_input_dc_only_coefficient() {
let input = vec![4i64; 32 * 8];
let coeffs = forward_transform_2d(&input, TX_32X8, DCT_DCT, false);
assert_eq!(coeffs.len(), 32 * 8);
let dc = coeffs[0];
let max_off = coeffs[1..].iter().map(|c| c.abs()).max().unwrap_or(0);
assert!(
dc.abs() > 10 * max_off.max(1),
"TX_32X8 DC = {dc}, max off-DC = {max_off} — DC should dominate"
);
let recovered = inverse_transform_2d(&coeffs, TX_32X8, DCT_DCT, 8, false);
for (i, &v) in recovered.iter().enumerate() {
assert!(
(v - 4).abs() <= 1,
"TX_32X8 constant-DC round-trip cell {i}: got {v}, expected ≈ 4"
);
}
}
#[test]
fn zero_input_across_all_supported_square_combinations() {
let cases: &[(usize, usize, usize)] = &[
(TX_4X4, 16, DCT_DCT),
(TX_8X8, 64, DCT_DCT),
(TX_16X16, 256, DCT_DCT),
(TX_32X32, 1024, DCT_DCT),
(TX_64X64, 4096, DCT_DCT),
(TX_4X8, 32, DCT_DCT),
(TX_8X4, 32, DCT_DCT),
(TX_4X8, 32, ADST_DCT),
(TX_8X4, 32, DCT_ADST),
(TX_4X8, 32, IDTX),
(TX_8X4, 32, FLIPADST_FLIPADST),
(TX_8X16, 128, DCT_DCT),
(TX_16X8, 128, DCT_DCT),
(TX_8X16, 128, ADST_ADST),
(TX_16X8, 128, DCT_ADST),
(TX_8X16, 128, IDTX),
(TX_16X8, 128, FLIPADST_FLIPADST),
(TX_16X32, 512, DCT_DCT),
(TX_32X16, 512, DCT_DCT),
(TX_16X32, 512, DCT_ADST),
(TX_32X16, 512, ADST_DCT),
(TX_16X32, 512, IDTX),
(TX_32X16, 512, IDTX),
(TX_16X32, 512, V_DCT),
(TX_32X16, 512, H_DCT),
(TX_32X64, 2048, DCT_DCT),
(TX_64X32, 2048, DCT_DCT),
(TX_32X64, 2048, V_DCT),
(TX_64X32, 2048, H_DCT),
(TX_16X64, 1024, DCT_DCT),
(TX_64X16, 1024, DCT_DCT),
(TX_4X16, 64, DCT_DCT),
(TX_16X4, 64, DCT_DCT),
(TX_4X16, 64, ADST_ADST),
(TX_16X4, 64, ADST_ADST),
(TX_4X16, 64, ADST_DCT),
(TX_16X4, 64, DCT_ADST),
(TX_4X16, 64, FLIPADST_FLIPADST),
(TX_16X4, 64, FLIPADST_DCT),
(TX_4X16, 64, DCT_FLIPADST),
(TX_16X4, 64, ADST_FLIPADST),
(TX_4X16, 64, FLIPADST_ADST),
(TX_16X4, 64, IDTX),
(TX_4X16, 64, IDTX),
(TX_4X16, 64, V_DCT),
(TX_16X4, 64, V_DCT),
(TX_4X16, 64, H_DCT),
(TX_16X4, 64, H_DCT),
(TX_4X16, 64, V_ADST),
(TX_16X4, 64, H_ADST),
(TX_4X16, 64, V_FLIPADST),
(TX_16X4, 64, H_FLIPADST),
(TX_8X32, 256, DCT_DCT),
(TX_32X8, 256, DCT_DCT),
(TX_8X32, 256, IDTX),
(TX_32X8, 256, IDTX),
(TX_4X4, 16, ADST_ADST),
(TX_8X8, 64, ADST_DCT),
(TX_8X8, 64, DCT_ADST),
(TX_16X16, 256, ADST_ADST),
(TX_4X4, 16, FLIPADST_FLIPADST),
(TX_8X8, 64, FLIPADST_DCT),
(TX_16X16, 256, ADST_FLIPADST),
(TX_4X4, 16, IDTX),
(TX_8X8, 64, IDTX),
(TX_16X16, 256, IDTX),
(TX_32X32, 1024, IDTX),
(TX_4X4, 16, V_DCT),
(TX_4X4, 16, H_DCT),
(TX_8X8, 64, V_ADST),
(TX_16X16, 256, H_FLIPADST),
];
for &(tx_size, n, tx_type) in cases {
let input = vec![0i64; n];
let coeffs = forward_transform_2d(&input, tx_size, tx_type, false);
assert_eq!(coeffs.len(), n);
for (i, &v) in coeffs.iter().enumerate() {
assert_eq!(
v, 0,
"tx_size={tx_size} tx_type={tx_type} cell {i}: zero in ⇒ zero out, \
got {v}"
);
}
}
}
#[test]
#[should_panic(expected = "out of range")]
fn tx_size_above_tx_sizes_all_panics() {
let input = vec![0i64; 16];
let _ = forward_transform_2d(&input, crate::cdf::TX_SIZES_ALL, DCT_DCT, false);
}
#[test]
#[should_panic(expected = "lossless arm requires tx_size = TX_4X4")]
fn lossless_non_4x4_panics() {
let input = vec![0i64; 64];
let _ = forward_transform_2d(&input, TX_8X8, DCT_DCT, true);
}
}