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,
};
pub const COS128_LOOKUP: [i32; 65] = [
4096, 4095, 4091, 4085, 4076, 4065, 4052, 4036, 4017, 3996, 3973, 3948, 3920, 3889, 3857, 3822,
3784, 3745, 3703, 3659, 3612, 3564, 3513, 3461, 3406, 3349, 3290, 3229, 3166, 3102, 3035, 2967,
2896, 2824, 2751, 2675, 2598, 2520, 2440, 2359, 2276, 2191, 2106, 2019, 1931, 1842, 1751, 1660,
1567, 1474, 1380, 1285, 1189, 1092, 995, 897, 799, 700, 601, 501, 401, 301, 201, 101, 0,
];
pub const SINPI_1_9: i32 = 1321;
pub const SINPI_2_9: i32 = 2482;
pub const SINPI_3_9: i32 = 3344;
pub const SINPI_4_9: i32 = 3803;
pub const TRANSFORM_ROW_SHIFT: [u32; TX_SIZES_ALL] = [
0, 1, 2, 2, 2, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, ];
#[inline]
pub fn round2(x: i64, n: u32) -> i64 {
if n == 0 {
return x;
}
let half: i64 = 1i64 << (n - 1);
(x + half) >> n
}
#[inline]
pub fn clip3(low: i64, high: i64, x: i64) -> i64 {
if x < low {
low
} else if x > high {
high
} else {
x
}
}
#[inline]
pub fn brev(num_bits: u32, x: u32) -> u32 {
let mut t = 0u32;
for i in 0..num_bits {
let bit = (x >> i) & 1;
t += bit << (num_bits - 1 - i);
}
t
}
#[inline]
pub fn cos128(angle: i32) -> i32 {
let angle2 = (angle as i64 & 255) as i32;
if (0..=64).contains(&angle2) {
COS128_LOOKUP[angle2 as usize]
} else if (65..=128).contains(&angle2) {
-COS128_LOOKUP[(128 - angle2) as usize]
} else if (129..=192).contains(&angle2) {
-COS128_LOOKUP[(angle2 - 128) as usize]
} else {
COS128_LOOKUP[(256 - angle2) as usize]
}
}
#[inline]
pub fn sin128(angle: i32) -> i32 {
cos128(angle - 64)
}
#[inline]
pub fn butterfly_b(t: &mut [i64], a: usize, b: usize, angle: i32, flip: u8, _r: u32) {
let cos_a = cos128(angle) as i64;
let sin_a = sin128(angle) as i64;
let ta = t[a];
let tb = t[b];
let x = ta * cos_a - tb * sin_a;
let y = ta * sin_a + tb * cos_a;
t[a] = round2(x, 12);
t[b] = round2(y, 12);
if flip == 1 {
t.swap(a, b);
}
}
#[inline]
pub fn butterfly_h(t: &mut [i64], a: usize, b: usize, flip: u8, r: u32) {
let (ai, bi) = if flip == 1 { (b, a) } else { (a, b) };
let x = t[ai];
let y = t[bi];
let lo: i64 = -(1i64 << (r - 1));
let hi: i64 = (1i64 << (r - 1)) - 1;
t[ai] = clip3(lo, hi, x + y);
t[bi] = clip3(lo, hi, x - y);
}
pub fn inverse_dct_permute(t: &mut [i64], n: u32) {
let len = 1usize << n;
let mut copy = [0i64; 64];
copy[..len].copy_from_slice(&t[..len]);
let table = &BREV_TABLE[n.min(6) as usize];
for (i, slot) in t.iter_mut().take(len).enumerate() {
*slot = copy[table[i] as usize];
}
}
const BREV_TABLE: [[u8; 64]; 7] = {
let mut table = [[0u8; 64]; 7];
let mut n = 0;
while n < 7 {
let len = 1usize << n;
let mut i = 0;
while i < len {
let mut t = 0u32;
let mut b = 0;
while b < n {
t += ((i as u32 >> b) & 1) << (n - 1 - b);
b += 1;
}
table[n][i] = t as u8;
i += 1;
}
n += 1;
}
table
};
#[allow(clippy::too_many_arguments)]
pub fn inverse_dct(t: &mut [i64], n: u32, r: u32) {
debug_assert!((2..=6).contains(&n));
inverse_dct_permute(t, n);
if n == 6 {
for i in 0..16 {
let a = 32 + i;
let b = 63 - i;
let angle = 63 - 4 * (brev(4, i as u32) as i32);
butterfly_b(t, a, b, angle, 0, r);
}
}
if n >= 5 {
for i in 0..8 {
let a = 16 + i;
let b = 31 - i;
let angle = 6 + ((brev(3, (7 - i) as u32) as i32) << 3);
butterfly_b(t, a, b, angle, 0, r);
}
}
if n == 6 {
for i in 0..16 {
butterfly_h(t, 32 + i * 2, 33 + i * 2, (i & 1) as u8, r);
}
}
if n >= 4 {
for i in 0..4 {
let a = 8 + i;
let b = 15 - i;
let angle = 12 + ((brev(2, (3 - i) as u32) as i32) << 4);
butterfly_b(t, a, b, angle, 0, r);
}
}
if n >= 5 {
for i in 0..8 {
butterfly_h(t, 16 + 2 * i, 17 + 2 * i, (i & 1) as u8, r);
}
}
if n == 6 {
for i in 0..4 {
for j in 0..2 {
let a = 62 - i * 4 - j;
let b = 33 + i * 4 + j;
let angle = 60 - 16 * (brev(2, i as u32) as i32) + 64 * (j as i32);
butterfly_b(t, a, b, angle, 1, r);
}
}
}
if n >= 3 {
for i in 0..2 {
butterfly_b(t, 4 + i, 7 - i, 56 - 32 * (i as i32), 0, r);
}
}
if n >= 4 {
for i in 0..4 {
butterfly_h(t, 8 + 2 * i, 9 + 2 * i, (i & 1) as u8, r);
}
}
if n >= 5 {
for i in 0..2 {
for j in 0..2 {
let a = 30 - 4 * i - j;
let b = 17 + 4 * i + j;
let angle = 24 + ((j as i32) << 6) + (((1 - i) as i32) << 5);
butterfly_b(t, a, b, angle, 1, r);
}
}
}
if n == 6 {
for i in 0..8 {
for j in 0..2 {
let a = 32 + i * 4 + j;
let b = 35 + i * 4 - j;
butterfly_h(t, a, b, (i & 1) as u8, r);
}
}
}
for i in 0..2 {
butterfly_b(t, 2 * i, 2 * i + 1, 32 + 16 * (i as i32), (1 - i) as u8, r);
}
if n >= 3 {
for i in 0..2 {
butterfly_h(t, 4 + 2 * i, 5 + 2 * i, i as u8, r);
}
}
if n >= 4 {
for i in 0..2 {
butterfly_b(t, 14 - i, 9 + i, 48 + 64 * (i as i32), 1, r);
}
}
if n >= 5 {
for i in 0..4 {
for j in 0..2 {
let a = 16 + 4 * i + j;
let b = 19 + 4 * i - j;
butterfly_h(t, a, b, (i & 1) as u8, r);
}
}
}
if n == 6 {
for i in 0..2usize {
for j in 0..4usize {
let a = 61 - i * 8 - j;
let b = 34 + i * 8 + j;
let angle: i32 = 56 - (i as i32) * 32 + ((j >> 1) as i32) * 64;
butterfly_b(t, a, b, angle, 1, r);
}
}
}
for i in 0..2 {
butterfly_h(t, i, 3 - i, 0, r);
}
if n >= 3 {
butterfly_b(t, 6, 5, 32, 1, r);
}
if n >= 4 {
for i in 0..2 {
for j in 0..2 {
butterfly_h(t, 8 + 4 * i + j, 11 + 4 * i - j, i as u8, r);
}
}
}
if n >= 5 {
for i in 0..4 {
butterfly_b(t, 29 - i, 18 + i, 48 + ((i >> 1) as i32) * 64, 1, r);
}
}
if n == 6 {
for i in 0..4 {
for j in 0..4 {
let a = 32 + 8 * i + j;
let b = 39 + 8 * i - j;
butterfly_h(t, a, b, (i & 1) as u8, r);
}
}
}
if n >= 3 {
for i in 0..4 {
butterfly_h(t, i, 7 - i, 0, r);
}
}
if n >= 4 {
for i in 0..2 {
butterfly_b(t, 13 - i, 10 + i, 32, 1, r);
}
}
if n >= 5 {
for i in 0..2 {
for j in 0..4 {
let a = 16 + i * 8 + j;
let b = 23 + i * 8 - j;
butterfly_h(t, a, b, i as u8, r);
}
}
}
if n == 6 {
for i in 0..8 {
let angle = if i < 4 { 48 } else { 112 };
butterfly_b(t, 59 - i, 36 + i, angle, 1, r);
}
}
if n >= 4 {
for i in 0..8 {
butterfly_h(t, i, 15 - i, 0, r);
}
}
if n >= 5 {
for i in 0..4 {
butterfly_b(t, 27 - i, 20 + i, 32, 1, r);
}
}
if n == 6 {
for i in 0..8 {
butterfly_h(t, 32 + i, 47 - i, 0, r);
butterfly_h(t, 48 + i, 63 - i, 1, r);
}
}
if n >= 5 {
for i in 0..16 {
butterfly_h(t, i, 31 - i, 0, r);
}
}
if n == 6 {
for i in 0..8 {
butterfly_b(t, 55 - i, 40 + i, 32, 1, r);
}
}
if n == 6 {
for i in 0..32 {
butterfly_h(t, i, 63 - i, 0, r);
}
}
}
pub fn inverse_adst_input_permute(t: &mut [i64], n: u32) {
let n0 = 1usize << n;
let copy: Vec<i64> = t[..n0].to_vec();
for (i, slot) in t[..n0].iter_mut().enumerate() {
let idx = if i & 1 == 1 { i - 1 } else { n0 - i - 1 };
*slot = copy[idx];
}
}
pub fn inverse_adst_output_permute(t: &mut [i64], n: u32) {
let n0 = 1usize << n;
let copy: Vec<i64> = t[..n0].to_vec();
for (i, slot) in t[..n0].iter_mut().enumerate() {
let a = ((i >> 3) & 1) as u32;
let b = (((i >> 2) & 1) ^ ((i >> 3) & 1)) as u32;
let c = (((i >> 1) & 1) ^ ((i >> 2) & 1)) as u32;
let d = ((i & 1) ^ ((i >> 1) & 1)) as u32;
let idx = ((d << 3) | (c << 2) | (b << 1) | a) >> (4 - n);
let v = copy[idx as usize];
*slot = if i & 1 == 1 { -v } else { v };
}
}
pub fn inverse_adst4(t: &mut [i64], _r: u32) {
let t0 = t[0];
let t1 = t[1];
let t2 = t[2];
let t3 = t[3];
let mut s = [0i64; 7];
s[0] = SINPI_1_9 as i64 * t0;
s[1] = SINPI_2_9 as i64 * t0;
s[2] = SINPI_3_9 as i64 * t1;
s[3] = SINPI_4_9 as i64 * t2;
s[4] = SINPI_1_9 as i64 * t2;
s[5] = SINPI_2_9 as i64 * t3;
s[6] = SINPI_4_9 as i64 * t3;
let a7 = t0 - t2;
let b7 = a7 + t3;
s[0] += s[3];
s[1] -= s[4];
s[3] = s[2];
s[2] = SINPI_3_9 as i64 * b7;
s[0] += s[5];
s[1] -= s[6];
let x0 = s[0] + s[3];
let x1 = s[1] + s[3];
let x2 = s[2];
let x3 = s[0] + s[1] - s[3];
t[0] = round2(x0, 12);
t[1] = round2(x1, 12);
t[2] = round2(x2, 12);
t[3] = round2(x3, 12);
}
pub fn inverse_adst8(t: &mut [i64], r: u32) {
inverse_adst_input_permute(t, 3);
for i in 0..4 {
butterfly_b(t, 2 * i, 2 * i + 1, 60 - 16 * (i as i32), 1, r);
}
for i in 0..4 {
butterfly_h(t, i, 4 + i, 0, r);
}
for i in 0..2 {
butterfly_b(t, 4 + 3 * i, 5 + i, 48 - 32 * (i as i32), 1, r);
}
for j in 0..2 {
for i in 0..2 {
butterfly_h(t, 4 * j + i, 2 + 4 * j + i, 0, r);
}
}
for i in 0..2 {
butterfly_b(t, 2 + 4 * i, 3 + 4 * i, 32, 1, r);
}
inverse_adst_output_permute(t, 3);
}
pub fn inverse_adst16(t: &mut [i64], r: u32) {
inverse_adst_input_permute(t, 4);
for i in 0..8 {
butterfly_b(t, 2 * i, 2 * i + 1, 62 - 8 * (i as i32), 1, r);
}
for i in 0..8 {
butterfly_h(t, i, 8 + i, 0, r);
}
for i in 0..2 {
butterfly_b(t, 8 + 2 * i, 9 + 2 * i, 56 - 32 * (i as i32), 1, r);
butterfly_b(t, 13 + 2 * i, 12 + 2 * i, 8 + 32 * (i as i32), 1, r);
}
for j in 0..2 {
for i in 0..4 {
butterfly_h(t, 8 * j + i, 4 + 8 * j + i, 0, r);
}
}
for j in 0..2 {
for i in 0..2 {
butterfly_b(
t,
4 + 8 * j + 3 * i,
5 + 8 * j + i,
48 - 32 * (i as i32),
1,
r,
);
}
}
for j in 0..4 {
for i in 0..2 {
butterfly_h(t, 4 * j + i, 2 + 4 * j + i, 0, r);
}
}
for i in 0..4 {
butterfly_b(t, 2 + 4 * i, 3 + 4 * i, 32, 1, r);
}
inverse_adst_output_permute(t, 4);
}
pub fn inverse_adst(t: &mut [i64], n: u32, r: u32) {
match n {
2 => inverse_adst4(t, r),
3 => inverse_adst8(t, r),
4 => inverse_adst16(t, r),
_ => panic!("oxideav-av1 §7.13.2.9: inverse_adst expects n in 2..=4, got {n}"),
}
}
pub fn inverse_wht4(t: &mut [i64], shift: u32) {
let a0 = t[0] >> shift;
let c0 = t[1] >> shift;
let d0 = t[2] >> shift;
let b0 = t[3] >> shift;
let mut a = a0;
let c = c0;
let mut d = d0;
let mut b = b0;
a += c;
d -= b;
let e = (a - d) >> 1;
b = e - b;
let c2 = e - c;
a -= b;
d += c2;
t[0] = a;
t[1] = b;
t[2] = c2;
t[3] = d;
}
pub fn inverse_identity4(t: &mut [i64]) {
for slot in t.iter_mut().take(4) {
*slot = round2(*slot * 5793, 12);
}
}
pub fn inverse_identity8(t: &mut [i64]) {
for slot in t.iter_mut().take(8) {
*slot *= 2;
}
}
pub fn inverse_identity16(t: &mut [i64]) {
for slot in t.iter_mut().take(16) {
*slot = round2(*slot * 11586, 12);
}
}
pub fn inverse_identity32(t: &mut [i64]) {
for slot in t.iter_mut().take(32) {
*slot *= 4;
}
}
pub fn inverse_identity(t: &mut [i64], n: u32) {
match n {
2 => inverse_identity4(t),
3 => inverse_identity8(t),
4 => inverse_identity16(t),
5 => inverse_identity32(t),
_ => panic!("oxideav-av1 §7.13.2.15: inverse_identity expects n in 2..=5, got {n}"),
}
}
pub fn inverse_transform_2d(
dequant: &[i64],
tx_sz: usize,
tx_type: usize,
bit_depth: u32,
lossless: bool,
) -> Vec<i64> {
assert!(
tx_sz < TX_SIZES_ALL,
"oxideav-av1 §7.13.3: tx_sz {tx_sz} out of range (TX_SIZES_ALL = {TX_SIZES_ALL})"
);
let log2_w = TX_WIDTH_LOG2[tx_sz] as u32;
let log2_h = (TX_HEIGHT[tx_sz] as u32).trailing_zeros();
let w = TX_WIDTH[tx_sz];
let h = TX_HEIGHT[tx_sz];
assert_eq!(
dequant.len(),
w * h,
"oxideav-av1 §7.13.3: dequant length mismatch"
);
let row_shift = if lossless {
0
} else {
TRANSFORM_ROW_SHIFT[tx_sz]
};
let col_shift = if lossless { 0 } else { 4 };
let row_clamp_range = bit_depth + 8;
let col_clamp_range = core::cmp::max(bit_depth + 6, 16);
let mut residual = vec![0i64; w * h];
let mut tbuf = [0i64; 64];
let tbuf = &mut tbuf[..w];
for i in 0..h {
let mut any_nonzero = false;
for j in 0..w {
tbuf[j] = if i < 32 && j < 32 {
dequant[i * w + j]
} else {
0
};
any_nonzero |= tbuf[j] != 0;
}
if !any_nonzero {
continue;
}
if log2_w.abs_diff(log2_h) == 1 {
for slot in tbuf.iter_mut() {
*slot = round2(*slot * 2896, 12);
}
}
if lossless {
inverse_wht4(tbuf, 2);
} else {
apply_row_kernel(tbuf, tx_type, log2_w, row_clamp_range);
}
for j in 0..w {
residual[i * w + j] = round2(tbuf[j], row_shift);
}
}
let lo: i64 = -(1i64 << (col_clamp_range - 1));
let hi: i64 = (1i64 << (col_clamp_range - 1)) - 1;
for cell in residual.iter_mut() {
*cell = clip3(lo, hi, *cell);
}
let mut tbuf = [0i64; 64];
let tbuf = &mut tbuf[..h];
for j in 0..w {
for i in 0..h {
tbuf[i] = residual[i * w + j];
}
if lossless {
inverse_wht4(tbuf, 0);
} else {
apply_col_kernel(tbuf, tx_type, log2_h, col_clamp_range);
}
for i in 0..h {
residual[i * w + j] = round2(tbuf[i], col_shift);
}
}
residual
}
fn apply_row_kernel(t: &mut [i64], tx_type: usize, log2_w: u32, row_clamp_range: u32) {
if matches!(tx_type, x if x == DCT_DCT || x == ADST_DCT || x == FLIPADST_DCT || x == H_DCT) {
inverse_dct(t, log2_w, row_clamp_range);
} 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
) {
inverse_adst(t, log2_w, row_clamp_range);
} else {
debug_assert!(
matches!(tx_type, x if x == IDTX || x == V_DCT || x == V_ADST || x == V_FLIPADST)
);
inverse_identity(t, log2_w);
}
}
fn apply_col_kernel(t: &mut [i64], tx_type: usize, log2_h: u32, col_clamp_range: u32) {
if matches!(tx_type, x if x == DCT_DCT || x == DCT_ADST || x == DCT_FLIPADST || x == V_DCT) {
inverse_dct(t, log2_h, col_clamp_range);
} 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
) {
inverse_adst(t, log2_h, col_clamp_range);
} else {
debug_assert!(
matches!(tx_type, x if x == IDTX || x == H_DCT || x == H_ADST || x == H_FLIPADST)
);
inverse_identity(t, log2_h);
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::cdf::{TX_16X16, TX_32X32, TX_4X4, TX_4X8, TX_64X64, TX_8X8};
#[test]
fn cos128_spec_anchor_points() {
assert_eq!(cos128(0), 4096); assert_eq!(cos128(64), 0); assert_eq!(cos128(128), -4096); assert_eq!(cos128(192), 0); assert_eq!(cos128(32), 2896);
assert_eq!(cos128(96), -2896);
assert_eq!(cos128(160), -2896);
assert_eq!(cos128(224), 2896);
}
#[test]
fn sin128_is_cos128_shifted() {
for a in -64..256 {
assert_eq!(sin128(a), cos128(a - 64));
}
}
#[test]
fn brev_basic() {
assert_eq!(brev(2, 0b00), 0b00);
assert_eq!(brev(2, 0b01), 0b10);
assert_eq!(brev(2, 0b10), 0b01);
assert_eq!(brev(2, 0b11), 0b11);
assert_eq!(brev(3, 0b100), 0b001);
assert_eq!(brev(4, 0b0001), 0b1000);
}
#[test]
fn round2_signed_arithmetic() {
assert_eq!(round2(0, 12), 0);
assert_eq!(round2(4096, 12), 1);
assert_eq!(round2(2048, 12), 1); assert_eq!(round2(2047, 12), 0); assert_eq!(round2(-2048, 12), 0);
assert_eq!(round2(-4096, 12), -1);
assert_eq!(round2(-6144, 12), -1);
assert_eq!(round2(-6145, 12), -2);
assert_eq!(round2(12345, 0), 12345);
assert_eq!(round2(-1, 0), -1);
}
#[test]
fn wht4_round_trip_zero() {
let mut t = [0i64; 4];
inverse_wht4(&mut t, 2);
assert_eq!(t, [0; 4]);
}
#[test]
fn identity8_doubles_values() {
let mut t: Vec<i64> = (1..=8).collect();
inverse_identity8(&mut t);
assert_eq!(t, vec![2, 4, 6, 8, 10, 12, 14, 16]);
}
#[test]
fn identity32_quadruples_values() {
let mut t: Vec<i64> = (0..32).collect();
inverse_identity32(&mut t);
for (i, v) in t.iter().enumerate() {
assert_eq!(*v, 4 * i as i64);
}
}
#[test]
fn identity4_scales_by_5793_over_4096() {
let mut t = [4096i64, 0, 0, 0];
inverse_identity4(&mut t);
assert_eq!(t, [5793, 0, 0, 0]);
}
#[test]
fn identity16_scales_by_11586_over_4096() {
let mut t = [0i64; 16];
t[0] = 4096;
inverse_identity16(&mut t);
assert_eq!(t[0], 11586);
}
#[test]
fn dct_permute_2_swaps_middle_pair() {
let mut t = [10i64, 20, 30, 40];
inverse_dct_permute(&mut t, 2);
assert_eq!(t, [10, 30, 20, 40]);
}
#[test]
fn adst_input_permute_n3() {
let mut t: Vec<i64> = (0..8).collect();
inverse_adst_input_permute(&mut t, 3);
assert_eq!(t, vec![7, 0, 5, 2, 3, 4, 1, 6]);
}
#[test]
fn idct4_dc_only_flat_output() {
let mut t = [4096i64, 0, 0, 0];
inverse_dct(&mut t, 2, 16);
assert_eq!(t, [2896, 2896, 2896, 2896]);
}
#[test]
fn idct8_zero_input_yields_zero() {
let mut t = [0i64; 8];
inverse_dct(&mut t, 3, 16);
assert_eq!(t, [0; 8]);
}
#[test]
fn idct16_zero_input_yields_zero() {
let mut t = [0i64; 16];
inverse_dct(&mut t, 4, 16);
assert_eq!(t, [0; 16]);
}
#[test]
fn idct32_zero_input_yields_zero() {
let mut t = [0i64; 32];
inverse_dct(&mut t, 5, 16);
assert_eq!(t, [0; 32]);
}
#[test]
fn idct64_zero_input_yields_zero() {
let mut t = [0i64; 64];
inverse_dct(&mut t, 6, 16);
assert_eq!(t, [0; 64]);
}
#[test]
fn iadst4_zero_input_yields_zero() {
let mut t = [0i64; 4];
inverse_adst4(&mut t, 16);
assert_eq!(t, [0; 4]);
}
#[test]
fn iadst8_zero_input_yields_zero() {
let mut t = [0i64; 8];
inverse_adst8(&mut t, 16);
assert_eq!(t, [0; 8]);
}
#[test]
fn iadst16_zero_input_yields_zero() {
let mut t = [0i64; 16];
inverse_adst16(&mut t, 16);
assert_eq!(t, [0; 16]);
}
#[test]
fn idtx_dispatcher_4_8_16_32() {
let mut t = [4096i64, 0, 0, 0];
inverse_identity(&mut t, 2);
assert_eq!(t[0], 5793);
let mut t = [1i64; 8];
inverse_identity(&mut t, 3);
assert_eq!(t, [2; 8]);
let mut t = [0i64; 16];
t[0] = 4096;
inverse_identity(&mut t, 4);
assert_eq!(t[0], 11586);
let mut t = [1i64; 32];
inverse_identity(&mut t, 5);
for v in &t {
assert_eq!(*v, 4);
}
}
#[test]
fn transform_2d_dct_dct_4x4_dc_only() {
let mut dequant = vec![0i64; 16];
dequant[0] = 4096;
let residual = inverse_transform_2d(&dequant, TX_4X4, DCT_DCT, 8, false);
assert_eq!(residual, vec![128; 16]);
}
#[test]
fn transform_2d_dct_dct_zero_yields_zero() {
let dequant = vec![0i64; 16];
let residual = inverse_transform_2d(&dequant, TX_4X4, DCT_DCT, 8, false);
assert_eq!(residual, vec![0; 16]);
}
#[test]
fn transform_2d_idtx_4x4_with_dc_only() {
let mut dequant = vec![0i64; 16];
dequant[0] = 4096;
let residual = inverse_transform_2d(&dequant, TX_4X4, IDTX, 8, false);
let mut expected = vec![0i64; 16];
expected[0] = 512;
assert_eq!(residual, expected);
}
#[test]
fn transform_2d_lossless_zero_yields_zero() {
let dequant = vec![0i64; 16];
let residual = inverse_transform_2d(&dequant, TX_4X4, DCT_DCT, 8, true);
assert_eq!(residual, vec![0; 16]);
}
#[test]
fn transform_2d_rectangular_4x8_zero_yields_zero() {
let dequant = vec![0i64; 32];
let residual = inverse_transform_2d(&dequant, TX_4X8, DCT_DCT, 8, false);
assert_eq!(residual, vec![0; 32]);
}
#[test]
fn transform_2d_8x8_zero_yields_zero() {
let dequant = vec![0i64; 64];
let residual = inverse_transform_2d(&dequant, TX_8X8, DCT_DCT, 8, false);
assert_eq!(residual, vec![0; 64]);
}
#[test]
fn transform_2d_16x16_zero_yields_zero() {
let dequant = vec![0i64; 256];
let residual = inverse_transform_2d(&dequant, TX_16X16, ADST_ADST, 8, false);
assert_eq!(residual, vec![0; 256]);
}
#[test]
fn transform_2d_32x32_zero_yields_zero() {
let dequant = vec![0i64; 1024];
let residual = inverse_transform_2d(&dequant, TX_32X32, DCT_DCT, 8, false);
assert_eq!(residual, vec![0; 1024]);
}
#[test]
fn transform_2d_64x64_zero_yields_zero() {
let dequant = vec![0i64; 4096];
let residual = inverse_transform_2d(&dequant, TX_64X64, DCT_DCT, 8, false);
assert_eq!(residual, vec![0; 4096]);
}
#[test]
fn transform_2d_flipadst_zero_yields_zero() {
let dequant = vec![0i64; 64];
let residual = inverse_transform_2d(&dequant, TX_8X8, FLIPADST_FLIPADST, 8, false);
assert_eq!(residual, vec![0; 64]);
}
#[test]
fn transform_2d_h_v_variants_zero_yields_zero() {
let variants = [V_DCT, H_DCT, V_ADST, H_ADST, V_FLIPADST, H_FLIPADST];
for tx_type in variants {
let dequant = vec![0i64; 64];
let residual = inverse_transform_2d(&dequant, TX_8X8, tx_type, 8, false);
assert_eq!(
residual,
vec![0; 64],
"tx_type {tx_type} should zero-preserve"
);
}
}
#[test]
fn idct4_linearity() {
let mut a = [4096i64, 0, 0, 0];
let mut b = [8192i64, 0, 0, 0];
inverse_dct(&mut a, 2, 16);
inverse_dct(&mut b, 2, 16);
for i in 0..4 {
assert_eq!(b[i], 2 * a[i]);
}
}
}