///|
/// Row/column transform kind: 0=DCT, 1=ADST, 2=identity.
fn av1_inverse_axis(
  input : Array[Int],
  log_size : Int,
  kind : Int,
  clamp_bits : Int,
) -> Array[Int]? {
  if kind == 0 {
    av1_inverse_dct(input, log_size, clamp_bits)
  } else if kind == 1 {
    if log_size == 2 {
      Some(av1_inv_adst4_1d(input[0], input[1], input[2], input[3]))
    } else {
      av1_inverse_adst_1d(input, log_size, clamp_bits)
    }
  } else if kind == 2 {
    av1_inverse_identity_1d(input, log_size)
  } else {
    None
  }
}

///|
/// AV1 inverse-transform assembly: horizontal rows, inter-stage rounding
/// and clipping, then vertical columns. Rectangular normalization depends
/// on both axes, independently of the selected DCT/ADST/identity kinds.
fn av1_inverse_separable(
  input : Array[Int],
  width : Int,
  height : Int,
  bit_depth : Int,
  row_kind : Int,
  col_kind : Int,
  row_flip : Bool,
  col_flip : Bool,
) -> Array[Int]? {
  let row_shift = match av1_tx_row_shift(width, height) {
    Some(value) => value
    None => return None
  }
  if input.length() != width * height || bit_depth < 8 || bit_depth > 16 {
    return None
  }
  let row_log = av1_msac_floor_log2(width)
  let col_log = av1_msac_floor_log2(height)
  let row_clamp = bit_depth + 8
  let col_clamp = if bit_depth + 6 > 16 { bit_depth + 6 } else { 16 }
  let minimum = -(1 << (col_clamp - 1))
  let maximum = (1 << (col_clamp - 1)) - 1
  let rect_scale = width == 2 * height || height == 2 * width
  let temp = Array::make(width * height, 0)
  for y in 0..> 12).to_int()
      } else {
        value
      }
    }
    let values = match av1_inverse_axis(row, row_log, row_kind, row_clamp) {
      Some(value) => value
      None => return None
    }
    for x in 0.. value
      None => return None
    }
    for y in 0..