use crate::transform::{inverse_identity, round2};
use std::sync::OnceLock;
fn forward_identity_scalar(n: u32) -> i64 {
debug_assert!((2..=5).contains(&n));
static C2: OnceLock<i64> = OnceLock::new();
static C3: OnceLock<i64> = OnceLock::new();
static C4: OnceLock<i64> = OnceLock::new();
static C5: OnceLock<i64> = OnceLock::new();
let cell = match n {
2 => &C2,
3 => &C3,
4 => &C4,
5 => &C5,
_ => unreachable!("forward identity size n must be in 2..=5"),
};
*cell.get_or_init(|| {
let nn = 1usize << n;
let mut probe = vec![0i64; nn];
probe[0] = 4096;
inverse_identity(&mut probe, n);
probe[0]
})
}
fn forward_identity_n(t: &mut [i64], n: u32) {
assert!(
(2..=5).contains(&n),
"oxideav-av1 forward_identity_n requires n in 2..=5, got {n}"
);
let nn = 1usize << n;
assert!(
t.len() >= nn,
"oxideav-av1 forward_identity_n: buffer too short for length {nn}"
);
let c = forward_identity_scalar(n);
for slot in t.iter_mut().take(nn) {
*slot = round2(*slot * c, 12);
}
}
pub fn forward_idtx_4(t: &mut [i64]) {
forward_identity_n(t, 2);
}
pub fn forward_idtx_8(t: &mut [i64]) {
forward_identity_n(t, 3);
}
pub fn forward_idtx_16(t: &mut [i64]) {
forward_identity_n(t, 4);
}
pub fn forward_idtx_32(t: &mut [i64]) {
forward_identity_n(t, 5);
}
fn forward_idtx_nxn(input: &[i64], side: usize) -> Vec<i64> {
assert!(
matches!(side, 4 | 8 | 16 | 32),
"oxideav-av1 forward_idtx_nxn requires side in {{4,8,16,32}}, got {side}"
);
assert_eq!(
input.len(),
side * side,
"oxideav-av1 forward_idtx_nxn expects side * side = {} samples",
side * side
);
let n: u32 = match side {
4 => 2,
8 => 3,
16 => 4,
32 => 5,
_ => unreachable!(),
};
let mut work = input.to_vec();
let mut row_buf = vec![0i64; side];
for i in 0..side {
row_buf.copy_from_slice(&work[i * side..(i + 1) * side]);
forward_identity_n(&mut row_buf, n);
work[i * side..(i + 1) * side].copy_from_slice(&row_buf);
}
let mut col_buf = vec![0i64; side];
for j in 0..side {
for i in 0..side {
col_buf[i] = work[i * side + j];
}
forward_identity_n(&mut col_buf, n);
for i in 0..side {
work[i * side + j] = col_buf[i];
}
}
work
}
pub fn forward_idtx_4x4(input: &[i64]) -> [i64; 16] {
let v = forward_idtx_nxn(input, 4);
let mut out = [0i64; 16];
out.copy_from_slice(&v);
out
}
pub fn forward_idtx_8x8(input: &[i64]) -> [i64; 64] {
let v = forward_idtx_nxn(input, 8);
let mut out = [0i64; 64];
out.copy_from_slice(&v);
out
}
pub fn forward_idtx_16x16(input: &[i64]) -> [i64; 256] {
let v = forward_idtx_nxn(input, 16);
let mut out = [0i64; 256];
out.copy_from_slice(&v);
out
}
pub fn forward_idtx_32x32(input: &[i64]) -> Vec<i64> {
forward_idtx_nxn(input, 32)
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn forward_idtx_4_zero_input_yields_zero() {
let mut t = [0i64; 4];
forward_idtx_4(&mut t);
assert_eq!(t, [0; 4]);
}
#[test]
fn forward_idtx_8_zero_input_yields_zero() {
let mut t = [0i64; 8];
forward_idtx_8(&mut t);
assert_eq!(t, [0; 8]);
}
#[test]
fn forward_idtx_16_zero_input_yields_zero() {
let mut t = [0i64; 16];
forward_idtx_16(&mut t);
assert_eq!(t, [0; 16]);
}
#[test]
fn forward_idtx_32_zero_input_yields_zero() {
let mut t = [0i64; 32];
forward_idtx_32(&mut t);
assert_eq!(t, [0; 32]);
}
#[test]
fn forward_idtx_4_per_cell_scalar_matches_spec_5793() {
let mut t = [1024i64; 4];
forward_idtx_4(&mut t);
assert_eq!(t, [1448; 4]);
}
#[test]
fn forward_idtx_8_per_cell_doubles_exactly() {
let mut t = [1000i64; 8];
forward_idtx_8(&mut t);
assert_eq!(t, [2000; 8]);
}
#[test]
fn forward_idtx_16_per_cell_scalar_matches_spec_11586() {
let mut t = [1024i64; 16];
forward_idtx_16(&mut t);
assert_eq!(t, [2897; 16]);
}
#[test]
fn forward_idtx_32_per_cell_quadruples_exactly() {
let mut t = [1000i64; 32];
forward_idtx_32(&mut t);
assert_eq!(t, [4000; 32]);
}
#[test]
fn forward_idtx_8_roundtrip_via_inverse_is_exact_4x() {
let mut buf = [37i64, -22, 18, 0, -100, 64, 7, -1];
forward_idtx_8(&mut buf);
crate::transform::inverse_identity(&mut buf, 3);
let expected: [i64; 8] = [37 * 4, -22 * 4, 18 * 4, 0, -100 * 4, 64 * 4, 7 * 4, -4];
assert_eq!(buf, expected);
}
#[test]
fn forward_idtx_32_roundtrip_via_inverse_is_exact_16x() {
let mut buf = [0i64; 32];
for (i, slot) in buf.iter_mut().enumerate() {
*slot = (i as i64) - 16;
}
let orig = buf;
forward_idtx_32(&mut buf);
crate::transform::inverse_identity(&mut buf, 5);
for (i, (&got, &o)) in buf.iter().zip(orig.iter()).enumerate() {
assert_eq!(got, 16 * o, "n=5 cell {i}: got {got}, expected {}", 16 * o);
}
}
#[test]
fn forward_idtx_4_roundtrip_via_inverse_within_noise() {
let orig = [1024i64; 4];
let mut buf = orig;
forward_idtx_4(&mut buf);
crate::transform::inverse_identity(&mut buf, 2);
for (i, &got) in buf.iter().enumerate() {
assert!(
(got - 2 * orig[i]).abs() <= 4,
"n=2 cell {i}: got {got}, expected≈{} (|err| {})",
2 * orig[i],
(got - 2 * orig[i]).abs()
);
}
}
#[test]
fn forward_idtx_16_roundtrip_via_inverse_within_noise() {
let orig = [1024i64; 16];
let mut buf = orig;
forward_idtx_16(&mut buf);
crate::transform::inverse_identity(&mut buf, 4);
for (i, &got) in buf.iter().enumerate() {
assert!(
(got - 8 * orig[i]).abs() <= 16,
"n=4 cell {i}: got {got}, expected≈{} (|err| {})",
8 * orig[i],
(got - 8 * orig[i]).abs()
);
}
}
#[test]
fn forward_idtx_4x4_zero_input_yields_zero() {
let input = [0i64; 16];
let out = forward_idtx_4x4(&input);
assert_eq!(out, [0; 16]);
}
#[test]
fn forward_idtx_8x8_zero_input_yields_zero() {
let input = [0i64; 64];
let out = forward_idtx_8x8(&input);
assert_eq!(out, [0; 64]);
}
#[test]
fn forward_idtx_16x16_zero_input_yields_zero() {
let input = [0i64; 256];
let out = forward_idtx_16x16(&input);
assert_eq!(out, [0; 256]);
}
#[test]
fn forward_idtx_32x32_zero_input_yields_zero() {
let input = vec![0i64; 1024];
let out = forward_idtx_32x32(&input);
assert_eq!(out.len(), 1024);
for &v in out.iter() {
assert_eq!(v, 0);
}
}
#[test]
fn forward_idtx_8x8_per_cell_quadruples_exactly() {
let input = [37i64; 64];
let out = forward_idtx_8x8(&input);
for (i, &v) in out.iter().enumerate() {
assert_eq!(v, 148, "cell {i}: got {v}");
}
}
#[test]
fn forward_idtx_32x32_per_cell_multiplies_by_sixteen_exactly() {
let input = vec![25i64; 1024];
let out = forward_idtx_32x32(&input);
for (i, &v) in out.iter().enumerate() {
assert_eq!(v, 400, "cell {i}: got {v}");
}
}
}