///|
/// Inter-frame block reconstruction: the tile leaf that mounts the inter syntax
/// of `av1_inter_mode.mbt` onto the existing partition walk, and turns it into
/// samples (AV1 搂5.11.17 through 搂5.11.37).
///
/// The partition tree itself is shared with intra frames, so only the leaf
/// changes. An inter frame's intra blocks reuse the intra reading with the
/// inter-frame luma mode table; both share one tile entropy decoder, one set of
/// adaptive probabilities and one pair of reconstructed planes.
///
/// Everything a reference prediction has to do is here: read the motion, store
/// it into the frame's motion field, size the transforms, interpolate the
/// reference, then add the residual.

///|
/// Transform extents a coding block may reach (AV1 搂5.11.15, Max_Tx_Size_Rect):
/// each axis is capped at 64 samples.
fn av1_max_rect_tx(width : Int, height : Int) -> (Int, Int) {
  (if width > 64 { 64 } else { width }, if height > 64 { 64 } else { height })
}

///|
/// Split_Tx_Size (AV1 搂5.11.15): halve the long axis, or both axes when the
/// transform is square.
fn av1_tx_split(width : Int, height : Int) -> (Int, Int) {
  if width > height {
    (width / 2, height)
  } else if height > width {
    (width, height / 2)
  } else {
    (width / 2, height / 2)
  }
}

///|
/// get_tx_size( plane, txSz ) for a chroma plane (AV1 搂5.11.35): the chroma
/// residual block's own maximum, with any 64-sample axis folded back to 32.
fn av1_chroma_tx_size(width : Int, height : Int, lossless : Bool) -> (Int, Int) {
  if lossless {
    return (4, 4)
  }
  let cap = av1_max_rect_tx(width, height)
  if cap.0 == 64 || cap.1 == 64 {
    if cap.0 == 16 {
      (16, 32)
    } else if cap.1 == 16 {
      (32, 16)
    } else {
      (32, 32)
    }
  } else {
    cap
  }
}

///|
/// The residual block size of one plane (AV1 搂5.11.36, get_plane_residual_size
/// over the Subsampled_Size table). A residual block is never narrower or
/// shorter than four samples.
fn av1_plane_residual_size(
  width : Int,
  height : Int,
  plane : Int,
  sub_x : Int,
  sub_y : Int,
) -> (Int, Int) {
  if plane == 0 {
    return (width, height)
  }
  let scaled_w = if width >> sub_x < 4 { 4 } else { width >> sub_x }
  let scaled_h = if height >> sub_y < 4 { 4 } else { height >> sub_y }
  (scaled_w, scaled_h)
}

///|
/// get_tx_set for an inter block (AV1 搂5.11.48). Sets 1 to 3 replace the two
/// intra sets; 0 means the transform type is fixed to DCT_DCT.
fn av1_inter_tx_set(width : Int, height : Int, reduced : Bool) -> Int {
  let larger = if width > height { width } else { height }
  let smaller = if width < height { width } else { height }
  let up = av1_msac_floor_log2(larger) - 2
  let sqr = av1_msac_floor_log2(smaller) - 2
  if up > 3 {
    0
  } else if reduced || up == 3 {
    3
  } else if sqr == 2 {
    2
  } else {
    1
  }
}

///|
/// Tx_Type_Inter_Inv_Set1..3 (AV1 搂5.11.47): the decoded index back to a
/// transform type. Set 1 keeps the plain DCT_DCT in the middle of its range -
/// index 7, between the four colour variants and the mixed pairs - exactly as
/// libaom's `tx_type_inter_inv_set1` and libdav1d's `dav1d_tx_types_per_set`
/// lay it out.
fn av1_inter_tx_type_of(set : Int, index : Int) -> Av1TxType {
  match (set, index) {
    (1, 0) => Av1TxIdentityIdentity
    (1, 1) => Av1TxVerticalDct
    (1, 2) => Av1TxHorizontalDct
    (1, 3) => Av1TxVerticalAdst
    (1, 4) => Av1TxHorizontalAdst
    (1, 5) => Av1TxVerticalFlipadst
    (1, 6) => Av1TxHorizontalFlipadst
    (1, 7) => Av1TxDctDct
    (1, 8) => Av1TxAdstDct
    (1, 9) => Av1TxDctAdst
    (1, 10) => Av1TxFlipadstDct
    (1, 11) => Av1TxDctFlipadst
    (1, 12) => Av1TxAdstAdst
    (1, 13) => Av1TxFlipadstFlipadst
    (1, 14) => Av1TxAdstFlipadst
    (1, 15) => Av1TxFlipadstAdst
    (2, 0) => Av1TxIdentityIdentity
    (2, 1) => Av1TxVerticalDct
    (2, 2) => Av1TxHorizontalDct
    (2, 3) => Av1TxDctDct
    (2, 4) => Av1TxAdstDct
    (2, 5) => Av1TxDctAdst
    (2, 6) => Av1TxFlipadstDct
    (2, 7) => Av1TxDctFlipadst
    (2, 8) => Av1TxAdstAdst
    (2, 9) => Av1TxFlipadstFlipadst
    (2, 10) => Av1TxAdstFlipadst
    (2, 11) => Av1TxFlipadstAdst
    (3, 0) => Av1TxIdentityIdentity
    (3, 1) => Av1TxDctDct
    _ => Av1TxDctDct
  }
}

///|
/// How many transform types one inter set codes.
fn av1_inter_tx_set_symbols(set : Int) -> Int {
  match set {
    1 => 16
    2 => 12
    3 => 2
    _ => 0
  }
}

///|
/// is_tx_type_in_set for an inter block (AV1 搂5.11.47): a chroma transform may
/// only reuse a luma type its own set contains.
fn av1_inter_tx_type_in_set(set : Int, tx_type : Av1TxType) -> Bool {
  // Set 3 contains exactly DCT_DCT and IDTX. In particular, a 32-pixel
  // chroma axis cannot inherit a one-dimensional luma transform: that would
  // select the wrong EOB/scan CDFs as well as the wrong inverse transform.
  if set == 3 {
    return tx_type is Av1TxDctDct || tx_type is Av1TxIdentityIdentity
  }
  match tx_type {
    Av1TxDctDct => true
    Av1TxVerticalAdst
    | Av1TxHorizontalAdst
    | Av1TxVerticalFlipadst
    | Av1TxHorizontalFlipadst => set == 1
    _ => set == 1 || set == 2
  }
}

///|
/// Spec TX_TYPES index of a transform type (AV1 搂6.10.4). The MoonBit enum
/// carries three extra identity combinations, so the mapping is explicit.
fn av1_tx_type_code(tx_type : Av1TxType) -> Int {
  match tx_type {
    Av1TxDctDct => 0
    Av1TxAdstDct => 1
    Av1TxDctAdst => 2
    Av1TxAdstAdst => 3
    Av1TxFlipadstDct => 4
    Av1TxDctFlipadst => 5
    Av1TxFlipadstFlipadst => 6
    Av1TxAdstFlipadst => 7
    Av1TxFlipadstAdst => 8
    Av1TxIdentityIdentity => 9
    Av1TxVerticalDct => 10
    Av1TxHorizontalDct => 11
    Av1TxVerticalAdst => 12
    Av1TxHorizontalAdst => 13
    Av1TxVerticalFlipadst => 14
    Av1TxHorizontalFlipadst => 15
    _ => 0
  }
}

///|
fn av1_tx_type_of_code(code : Int) -> Av1TxType {
  match code {
    0 => Av1TxDctDct
    1 => Av1TxAdstDct
    2 => Av1TxDctAdst
    3 => Av1TxAdstAdst
    4 => Av1TxFlipadstDct
    5 => Av1TxDctFlipadst
    6 => Av1TxFlipadstFlipadst
    7 => Av1TxAdstFlipadst
    8 => Av1TxFlipadstAdst
    9 => Av1TxIdentityIdentity
    10 => Av1TxVerticalDct
    11 => Av1TxHorizontalDct
    12 => Av1TxVerticalAdst
    13 => Av1TxHorizontalAdst
    14 => Av1TxVerticalFlipadst
    15 => Av1TxHorizontalFlipadst
    _ => Av1TxDctDct
  }
}

///|
/// TxTypes[ y4 ][ x4 ] over one transform's luma footprint (AV1 搂5.11.47), which
/// is what a chroma transform of the same block reads back.
fn av1_inter_record_tx_types(
  field : Av1MotionField,
  x4 : Int,
  y4 : Int,
  width : Int,
  height : Int,
  tx_type : Av1TxType,
) -> Unit {
  let code = av1_tx_type_code(tx_type)
  for row in y4..<(y4 + height / 4) {
    for col in x4..<(x4 + width / 4) {
      if row >= 0 && row < field.rows && col >= 0 && col < field.cols {
        field.tx_types[row * field.cols + col] = code
      }
    }
  }
}

///|
/// transform_type( x4, y4, txSz ) for an inter block (AV1 搂5.11.47). The type is
/// only coded when the transform set offers a choice and the quantizer is not
/// zero; otherwise it is DCT_DCT, but it is still recorded for chroma.
fn av1_inter_luma_tx_type(
  decoder : Av1SymbolDecoder,
  state : Av1IntraState,
  info : Av1InterInfo,
  width : Int,
  height : Int,
  x4 : Int,
  y4 : Int,
) -> Av1TxType {
  let set = av1_inter_tx_set(width, height, state.reduced_tx_set)
  let sqr = av1_msac_floor_log2(if width < height { width } else { height }) - 2
  // Transform origins are tile-local; the motion field spans the whole frame.
  let frame_x4 = info.tile_col_start + x4
  let frame_y4 = info.tile_row_start + y4
  if set == 0 || state.seg_syntax_q_idx == 0 {
    av1_inter_record_tx_types(
      info.field,
      frame_x4,
      frame_y4,
      width,
      height,
      Av1TxDctDct,
    )
    return Av1TxDctDct
  }
  let cdfs = info.cdfs
  let index = if set == 1 {
    if sqr < 0 || sqr >= cdfs.inter_tx_type_set1.length() {
      return Av1TxDctDct
    }
    decoder.symbol(cdfs.inter_tx_type_set1[sqr])
  } else if set == 2 {
    decoder.symbol(cdfs.inter_tx_type_set2)
  } else {
    if sqr < 0 || sqr >= cdfs.inter_tx_type_set3.length() {
      return Av1TxDctDct
    }
    decoder.symbol(cdfs.inter_tx_type_set3[sqr])
  }
  if index < 0 || index >= av1_inter_tx_set_symbols(set) {
    return Av1TxDctDct
  }
  let tx_type = av1_inter_tx_type_of(set, index)
  av1_inter_record_tx_types(
    info.field,
    frame_x4,
    frame_y4,
    width,
    height,
    tx_type,
  )
  tx_type
}

///|
/// compute_tx_type( plane, txSz, blockX, blockY ) for the chroma planes of an
/// inter block (AV1 搂5.11.47): inherit the luma type at the transform's own
/// origin, falling back to DCT_DCT when it is outside the chroma set.
fn av1_inter_chroma_tx_type(
  state : Av1IntraState,
  info : Av1InterInfo,
  current : Av1InterBlock,
  width : Int,
  height : Int,
  x : Int,
  y : Int,
) -> Av1TxType {
  let set = av1_inter_tx_set(width, height, state.reduced_tx_set)
  if set == 0 {
    return Av1TxDctDct
  }
  let field = info.field
  let x4 = info.tile_col_start + ((x / 4) << state.sub_x)
  let y4 = info.tile_row_start + ((y / 4) << state.sub_y)
  let luma_col = if x4 < current.mi_col { current.mi_col } else { x4 }
  let luma_row = if y4 < current.mi_row { current.mi_row } else { y4 }
  if luma_row >= field.rows || luma_col >= field.cols {
    return Av1TxDctDct
  }
  let code = field.tx_types[luma_row * field.cols + luma_col]
  let tx_type = av1_tx_type_of_code(code)
  if av1_inter_tx_type_in_set(set, tx_type) {
    tx_type
  } else {
    Av1TxDctDct
  }
}

///|
/// read_var_tx_size( row, col, txSz, depth ) (AV1 搂5.11.16). Positions are frame
/// MI units; leaves are collected in the order the tree is read, which is the
/// order their coefficients appear in the bitstream.
fn av1_read_var_tx_size(
  decoder : Av1SymbolDecoder,
  state : Av1IntraState,
  info : Av1InterInfo,
  block : Av1InterBlock,
  row : Int,
  col : Int,
  tx_width : Int,
  tx_height : Int,
  depth : Int,
  leaves : Array[Av1TxNode],
) -> Bool {
  if row >= info.mi_rows || col >= info.mi_cols {
    return true
  }
  let field = info.field
  let split = if tx_width == 4 && tx_height == 4 {
    0
  } else if depth >= 2 {
    0
  } else {
    let above = av1_above_tx_width(
      field,
      block.mi_row,
      row,
      col,
      row == block.mi_row && block.avail_u,
    )
    let left = av1_left_tx_height(
      field,
      block.mi_col,
      row,
      col,
      col == block.mi_col && block.avail_l,
    )
    let ctx = av1_txfm_split_ctx(
      above,
      left,
      block.block_width,
      block.block_height,
      tx_width,
      tx_height,
    )
    if ctx < 0 || ctx >= state.vartx.rows.length() {
      return false
    }
    decoder.symbol(state.vartx.rows[ctx])
  }
  let width4 = tx_width / 4
  let height4 = tx_height / 4
  // Splitting an 8x8, 4x8 or 8x4 node produces 4x4 leaves. Those children
  // consume no further split symbols, as handled by the 4x4 base case above.
  if split == 1 {
    let (sub_width, sub_height) = av1_tx_split(tx_width, tx_height)
    for i = 0; i < height4; i = i + sub_height / 4 {
      for j = 0; j < width4; j = j + sub_width / 4 {
        if !av1_read_var_tx_size(
            decoder,
            state,
            info,
            block,
            row + i,
            col + j,
            sub_width,
            sub_height,
            depth + 1,
            leaves,
          ) {
          return false
        }
      }
    }
    return true
  }
  av1_motion_field_store_tx(
    field, row, col, width4, height4, tx_width, tx_height,
  )
  leaves.push({
    row: (row - info.tile_row_start) * 4,
    col: (col - info.tile_col_start) * 4,
    width: tx_width,
    height: tx_height,
  })
  true
}

///|
/// read_block_tx_size( ) (AV1 搂5.11.15). A non-skipped inter block under
/// TX_MODE_SELECT reads the variable transform tree; everything else reads at
/// most one tx_depth symbol. The luma result is also recorded into the frame's
/// motion field, because later blocks measure their neighbours against it.
fn av1_read_block_tx_size(
  decoder : Av1SymbolDecoder,
  state : Av1IntraState,
  info : Av1InterInfo,
  block : Av1InterBlock,
) -> (Int, Int, Array[Array[Av1TxNode]])? {
  let roots : Array[Array[Av1TxNode]] = []
  let (max_width, max_height) = av1_max_rect_tx(
    block.block_width,
    block.block_height,
  )
  if state.tx_mode_select &&
    block.size_index > 0 &&
    block.is_inter &&
    block.skip == 0 &&
    !state.seg_lossless {
    let (tx_w4, tx_h4) = (max_width / 4, max_height / 4)
    let mut row = block.mi_row
    while row < block.mi_row + block.height4 {
      let mut col = block.mi_col
      while col < block.mi_col + block.width4 {
        // One root per 64-pixel residual chunk, in the order residual() walks
        // them, so the leaf lists can be consumed chunk by chunk.
        let leaves : Array[Av1TxNode] = []
        roots.push(leaves)
        if !av1_read_var_tx_size(
            decoder, state, info, block, row, col, max_width, max_height, 0, leaves,
          ) {
          return None
        }
        col = col + tx_w4
      }
      row = row + tx_h4
    }
    return Some((max_width, max_height, roots))
  }
  let allow_select = !block.is_inter || block.skip == 0
  if state.seg_lossless {
    av1_motion_field_store_tx(
      info.field,
      block.mi_row,
      block.mi_col,
      block.width4,
      block.height4,
      4,
      4,
    )
    return Some((4, 4, []))
  }
  let mut tx_width = max_width
  let mut tx_height = max_height
  if allow_select && block.size_index > 0 && state.tx_mode_select {
    let context = av1_tx_depth_ctx(info, block, max_width, max_height)
    let depth = match
      av1_decode_tx_depth_for_block(
        decoder,
        state.tx_depth,
        block.block_width,
        block.block_height,
        context,
      ) {
      Some(value) => value
      None => return None
    }
    for _ in 0.. Int {
  let field = info.field
  let above = if !block.avail_u {
    0
  } else {
    let unit = (block.mi_row - 1) * field.cols + block.mi_col
    if field.is_inter[unit] {
      field.width4[unit] * 4
    } else {
      field.tx_width[unit]
    }
  }
  let left = if !block.avail_l {
    0
  } else {
    let unit = block.mi_row * field.cols + block.mi_col - 1
    if field.is_inter[unit] {
      field.height4[unit] * 4
    } else {
      field.tx_height[unit]
    }
  }
  (if above >= max_width { 1 } else { 0 }) +
  (if left >= max_height { 1 } else { 0 })
}

///|
/// compute_prediction( ) for a single-reference block (AV1 搂7.11.3.1). Each plane
/// is interpolated from the reference named by the motion field at the plane's
/// own sample grid, so a chroma block that is merged with its neighbour reads the
/// neighbour's vector rather than its own.
fn av1_inter_predict(
  state : Av1IntraState,
  info : Av1InterInfo,
  block : Av1InterBlock,
  tile_block : Av1SbBlock,
) -> Bool {
  let field = info.field
  // Only the block that owns the chroma samples of a merged group predicts
  // them; the other one has no chroma syntax at all (AV1 搂5.11.17).
  let plane_count = if av1_intra_has_chroma(state, tile_block) { 3 } else { 1 }
  let mut diffwtd_luma : Array[Int] = []
  for plane in 0..> sub_x
    let mut pred_h = block.block_height >> sub_y
    let base_x = (block.mi_col >> sub_x) * 4
    let base_y = (block.mi_row >> sub_y) * 4
    let cand_row = block.mi_row >> sub_y << sub_y
    let cand_col = block.mi_col >> sub_x << sub_x
    // Sub-8 chroma uses neighbouring vectors only when every participating
    // luma block is inter. An intra neighbour makes the whole shared footprint
    // use the owner's vector/filter (dav1d recon_tmpl.c sub8x8 derivation).
    let mut own_chroma = false
    if plane > 0 && (pred_w < 4 || pred_h < 4) {
      for row in 0..<(residual_h / pred_h) {
        for col in 0..<(residual_w / pred_w) {
          let unit = (cand_row + row) * field.cols + cand_col + col
          if field.refs[unit * 2] <= av1_intra_frame {
            own_chroma = true
          }
        }
      }
      if own_chroma {
        pred_w = residual_w
        pred_h = residual_h
      }
    }
    let target = state.planes[plane]
    // A LOCALWARP block with a representable model skips the whole
    // motion-compensation path: each 8x8 chunk of the plane is filtered
    // through the model itself, so there is no single subpel phase to filter
    // at and no filter symbol to read (AV1 搂7.11.3.8). The warp filter works on
    // 8-pixel chunks, so libdav1d only routes a plane through it when that
    // plane's own block is at least 8x8 samples; a smaller chroma block
    // predicts through the ordinary motion-compensation path at the block's own
    // vector and the eight-tap regular filter it reads no symbol for.
    if block.motion_mode == 2 &&
      block.warp.length() == 1 &&
      pred_w >= 8 &&
      pred_h >= 8 {
      let reference = match
        av1_inter_reference(
          info,
          field.refs[(block.mi_row * field.cols + block.mi_col) * 2],
        ) {
        Some(value) => value
        None => return false
      }
      let prediction = av1_warp_predict(
        reference,
        block.warp[0],
        plane,
        block.mi_col,
        block.mi_row,
        base_x,
        base_y,
        pred_w,
        pred_h,
      )
      av1_motion_store(
        target.pixels,
        target.width,
        prediction,
        pred_w,
        pred_h,
        base_x - (info.tile_col_start >> sub_x) * 4,
        base_y - (info.tile_row_start >> sub_y) * 4,
        (1 << state.bit_depth) - 1,
      )
      continue
    }
    // A GLOBAL(Global)MV block whose reference carries a non-identity
    // frame-level model predicts through that model per pixel, exactly as a
    // LOCALWARP block does (AV1 搂7.11.3.8). Compound lists select their own
    // model below and retain the fractional precision needed by the blend.
    if block.motion_mode != 2 &&
      pred_w >= 8 &&
      pred_h >= 8 &&
      block.y_mode == av1_mode_global_mv &&
      block.ref_frame1 <= av1_intra_frame &&
      av1_gm_type(info, block.ref_frame) > av1_gm_translation &&
      info.frame.force_integer_mv == 0 {
      match av1_warp_from_global_motion(info.frame.gm_params[block.ref_frame]) {
        Some(model) => {
          let reference = match av1_inter_reference(info, block.ref_frame) {
            Some(value) => value
            None => return false
          }
          let prediction = av1_warp_predict(
            reference,
            model,
            plane,
            block.mi_col,
            block.mi_row,
            base_x,
            base_y,
            pred_w,
            pred_h,
          )
          av1_motion_store(
            target.pixels,
            target.width,
            prediction,
            pred_w,
            pred_h,
            base_x - (info.tile_col_start >> sub_x) * 4,
            base_y - (info.tile_row_start >> sub_y) * 4,
            (1 << state.bit_depth) - 1,
          )
          continue
        }
        None => ()
      }
    }
    let mut r = 0
    let mut y = 0
    while y < residual_h / 4 * 4 {
      let mut c = 0
      let mut x = 0
      while x < residual_w / 4 * 4 {
        let unit = if own_chroma {
          (block.mi_row * field.cols + block.mi_col) * 2
        } else {
          ((cand_row + r) * field.cols + cand_col + c) * 2
        }
        if unit + 1 >= field.refs.length() {
          return false
        }
        let name = field.refs[unit]
        if name <= av1_intra_frame {
          // The current owner is always inter; an absent reference here is
          // invalid state rather than a legal mixed shared-chroma group.
          return false
        }
        let reference = match av1_inter_reference(info, name) {
          Some(value) => value
          None => return false
        }
        let second_name = field.refs[unit + 1]
        // Every branch below interpolates the block's own vector with its own
        // filters; only the compound one needs a second request.
        let single_request : Av1MotionRequest = {
          reference,
          frame_width: info.frame.frame_width,
          frame_height: info.frame.frame_height,
          plane,
          x: base_x + x,
          y: base_y + y,
          width: pred_w,
          height: pred_h,
          mv_row: field.mv_rows[unit],
          mv_col: field.mv_cols[unit],
          filter_vertical: field.interp_v[unit / 2],
          filter_horizontal: field.interp_h[unit / 2],
          compound: false,
        }
        let prediction = if block.interintra != 0 {
          // An interintra block mixes the motion-compensated prediction with an
          // intra one of this block's neighbourhood (AV1 搂7.11.3.10). The intra
          // edges come from the reconstructed neighbourhood, and the weight is
          // either the mode-variant ramp or a wedge mask.
          let inter = av1_motion_compensate(single_request)
          let px = base_x + x - (info.tile_col_start >> sub_x) * 4
          let py = base_y + y - (info.tile_row_start >> sub_y) * 4
          let have_above = block.avail_u && py > 0
          let have_left = block.avail_l && px > 0
          let above : Array[Int] = []
          let left : Array[Int] = []
          if have_above {
            for i in 0.. value
            None => return false
          }
          let weights = if block.wedge_interintra == 0 {
            // The intra-variant ramp: the intra share along one axis, scaled by
            // the block's larger dimension (AV1 搂7.11.3.17).
            let big = if pred_w < pred_h { pred_h } else { pred_w }
            let scale = 128 / big
            let built = Array::make(pred_w * pred_h, 0)
            for i in 0.. value
              None => return false
            }
          }
          let hi = (1 << state.bit_depth) - 1
          let blended = Array::make(inter.length(), 0)
          for k in 0.. hi {
              hi
            } else {
              inter[k]
            }
            let v = (m * intra[k] + (64 - m) * p0 + 32) >> 6
            blended[k] = if v < 0 { 0 } else if v > hi { hi } else { v }
          }
          blended
        } else if second_name > av1_intra_frame {
          // A compound block interpolates both lists with the block's own
          // filters, and the intermediate results are combined one step later
          // than a single-reference decode rounds them (AV1 搂7.11.3.10,
          // `interPostRound`).
          let filter_vertical = field.interp_v[(cand_row + r) * field.cols +
            cand_col +
            c]
          let filter_horizontal = field.interp_h[(cand_row + r) * field.cols +
            cand_col +
            c]
          let first_request : Av1MotionRequest = {
            reference,
            frame_width: info.frame.frame_width,
            frame_height: info.frame.frame_height,
            plane,
            x: base_x + x,
            y: base_y + y,
            width: pred_w,
            height: pred_h,
            mv_row: field.mv_rows[unit],
            mv_col: field.mv_cols[unit],
            filter_vertical,
            filter_horizontal,
            compound: true,
          }
          let second_reference = match av1_inter_reference(info, second_name) {
            Some(value) => value
            None => return false
          }
          let second_request : Av1MotionRequest = {
            reference: second_reference,
            frame_width: info.frame.frame_width,
            frame_height: info.frame.frame_height,
            plane,
            x: base_x + x,
            y: base_y + y,
            width: pred_w,
            height: pred_h,
            mv_row: field.mv_rows[unit + 1],
            mv_col: field.mv_cols[unit + 1],
            filter_vertical,
            filter_horizontal,
            compound: true,
          }
          // GLOBAL_GLOBALMV chooses independently for each reference. Scaled
          // references and small chroma planes use ordinary MC, as do invalid
          // shear models and frame headers forcing integer motion vectors.
          let predictions : Array[Array[Int]] = []
          for list in 0..<2 {
            let request = if list == 0 { first_request } else { second_request }
            let ref_name = if list == 0 { name } else { second_name }
            let model = if av1_compound_both_global(block.y_mode) &&
              info.frame.force_integer_mv == 0 &&
              pred_w >= 8 &&
              pred_h >= 8 &&
              av1_gm_type(info, ref_name) > av1_gm_translation &&
              request.reference.width == info.frame.frame_width &&
              request.reference.height == info.frame.frame_height {
              av1_warp_from_global_motion(info.frame.gm_params[ref_name])
            } else {
              None
            }
            predictions.push(
              match model {
                Some(model) =>
                  av1_warp_predict(
                    request.reference,
                    model,
                    plane,
                    block.mi_col,
                    block.mi_row,
                    base_x + x,
                    base_y + y,
                    pred_w,
                    pred_h,
                    compound=true,
                  )
                None => av1_motion_compensate(request)
              },
            )
          }
          let first = predictions[0]
          let second = predictions[1]
          let post = if state.bit_depth == 12 { 2 } else { 4 }
          let blended = Array::make(first.length(), 0)
          if block.compound_type == av1_compound_diffwtd ||
            block.compound_type == av1_compound_wedge {
            // A masked blend: per-sample weights pick how much of each list the
            // sample takes, and the weights come either from the difference
            // between the two predictions (搂7.11.3.12) or from the wedge table
            // (搂7.11.3.11). The mask's sign selects the first list's share;
            // its mirrored variant complements that share (搂7.11.3.14).
            let weights = if block.compound_type == av1_compound_diffwtd {
              // The weights come from the luma plane: chroma takes the
              // luma mask averaged down to its sample grid, the same way
              // the wedge variant does (搂7.11.3.14).
              if plane == 0 {
                let built = av1_diffwtd_mask(
                  first,
                  second,
                  pred_w,
                  pred_h,
                  0,
                  0,
                  block.mask_type,
                  state.bit_depth,
                )
                diffwtd_luma = built
                built
              } else {
                av1_reduce_plane_mask(
                  diffwtd_luma,
                  block.block_width,
                  block.block_height,
                  sub_x,
                  sub_y,
                )
              }
            } else {
              let mask = av1_wedge_plane_mask(
                block.block_width,
                block.block_height,
                block.wedge_index,
                block.wedge_sign,
                sub_x,
                sub_y,
              )
              match mask {
                Some(value) => value
                None => return false
              }
            }
            // Both mask builders already express the signed first-list share.
            // Swapping the lists again would cancel a difference-mask sign.
            let head = first
            let tail = second
            let shift = 6 + post
            let bias = 32 << post
            for k in 0..> shift
            }
          } else if block.compound_type == av1_compound_distance {
            // The distance weighting scales each list's interpolation by its
            // reference's order-hint distance (AV1 搂7.11.3.15).
            let (forward, backward) = av1_distance_weights(
              info, name, second_name,
            )
            let shift = 4 + post
            let bias = 1 << (shift - 1)
            for k in 0..>
                shift
            }
          } else {
            let shift = 1 + post
            let bias = 1 << (shift - 1)
            for k in 0..> shift
            }
          }
          blended
        } else {
          av1_motion_compensate(single_request)
        }
        av1_motion_store(
          target.pixels,
          target.width,
          prediction,
          pred_w,
          pred_h,
          base_x + x - (info.tile_col_start >> sub_x) * 4,
          base_y + y - (info.tile_row_start >> sub_y) * 4,
          (1 << state.bit_depth) - 1,
        )
        c = c + 1
        x = x + pred_w
      }
      r = r + 1
      y = y + pred_h
    }
    // A block that coded OBMC blends its neighbours' vectors over the strips
    // they share with it, on top of the prediction just written.
    if block.motion_mode == 1 && !av1_obmc_blend(state, info, block, plane) {
      return false
    }
  }
  true
}

///|
/// The five OBMC blending ramps (AV1 搂7.11.3.9). A strip picks the ramp whose
/// extent matches its own length, so an 8-sample strip and a 32-sample one
/// reach full weight at different distances from the shared edge.
let av1_obmc_masks : Array[Array[Int]] = [
  [45, 64],
  [39, 50, 59, 64],
  [36, 42, 48, 53, 57, 61, 64, 64],
  [34, 37, 40, 43, 46, 49, 52, 54, 56, 58, 60, 61, 64, 64, 64, 64],
  [
    33, 35, 36, 38, 40, 41, 43, 44, 45, 47, 48, 50, 51, 52, 53, 55, 56, 57, 58, 59,
    60, 60, 61, 62, 64, 64, 64, 64, 64, 64, 64, 64,
  ],
]

///|
/// get_obmc_mask( length ).
fn av1_obmc_mask(length : Int) -> Array[Int] {
  if length <= 2 {
    av1_obmc_masks[0]
  } else if length <= 4 {
    av1_obmc_masks[1]
  } else if length <= 8 {
    av1_obmc_masks[2]
  } else if length <= 16 {
    av1_obmc_masks[3]
  } else {
    av1_obmc_masks[4]
  }
}

///|
/// predict_overlap for one OBMC candidate (AV1 搂7.11.3.9). The neighbour's own
/// vector and its own interpolation filter interpolate a strip of the reference,
/// the strip is clipped to the sample range, and then it is blended into the
/// prediction the block already wrote into the plane.
fn av1_obmc_overlap(
  state : Av1IntraState,
  info : Av1InterInfo,
  target : Av1IntraPlane,
  plane : Int,
  tile_x : Int,
  tile_y : Int,
  limit : Int,
  cand_row : Int,
  cand_col : Int,
  x4 : Int,
  y4 : Int,
  sub_x : Int,
  sub_y : Int,
  pred_w : Int,
  pred_h : Int,
  mask : Array[Int],
  pass : Int,
) -> Bool {
  let field = info.field
  let index = cand_row * field.cols + cand_col
  let unit = index * 2
  let reference = match av1_inter_reference(info, field.refs[unit]) {
    Some(value) => value
    None => return false
  }
  let pred_x = (x4 * 4) >> sub_x
  let pred_y = (y4 * 4) >> sub_y
  let request : Av1MotionRequest = {
    reference,
    frame_width: info.frame.frame_width,
    frame_height: info.frame.frame_height,
    plane,
    x: pred_x,
    y: pred_y,
    width: pred_w,
    height: pred_h,
    mv_row: field.mv_rows[unit],
    mv_col: field.mv_cols[unit],
    filter_vertical: field.interp_v[index],
    filter_horizontal: field.interp_h[index],
    compound: false,
  }
  let obmc = av1_motion_compensate(request)
  for i in 0..= target.height || column >= target.width {
        return false
      }
      let at = row * target.width + column
      let neighbour = av1_mc_clip(0, limit, obmc[i * pred_w + j])
      let blended = weight * target.pixels[at] + (64 - weight) * neighbour + 32
      target.pixels[at] = blended >> 6
    }
  }
  true
}

///|
/// overlapped_motion_compensation( ) for one plane (AV1 搂7.11.3.9). The above
/// pass blends the top half of each column strip from the neighbours above and
/// the left pass blends the left half of each row strip from the neighbours to
/// the left. Both read the plane's current contents, so the two passes compose
/// in the order the specification applies them.
fn av1_obmc_blend(
  state : Av1IntraState,
  info : Av1InterInfo,
  block : Av1InterBlock,
  plane : Int,
) -> Bool {
  let field = info.field
  let sub_x = if plane == 0 { 0 } else { state.sub_x }
  let sub_y = if plane == 0 { 0 } else { state.sub_y }
  let w = block.block_width >> sub_x
  let h = block.block_height >> sub_y
  let target = state.planes[plane]
  let tile_x = (info.tile_col_start >> sub_x) * 4
  let tile_y = (info.tile_row_start >> sub_y) * 4
  let limit = (1 << state.bit_depth) - 1
  // Chroma above-OBMC is suppressed only for blocks whose width + height < 16.
  if block.avail_u && (plane == 0 || w + h >= 16) {
    let log2_width = av1_msac_floor_log2(block.width4)
    let n_limit = if 4 < log2_width { 4 } else { log2_width }
    let mut x4 = block.mi_col
    let mut count = 0
    while count < n_limit &&
          x4 < info.mi_cols &&
          x4 < block.mi_col + block.width4 {
      let cand_row = block.mi_row - 1
      let cand_col = x4 | 1
      if cand_row < 0 || cand_col < 0 || cand_col >= field.cols {
        return false
      }
      let index = cand_row * field.cols + cand_col
      let extent = field.width4[index]
      let step4 = if extent < 2 { 2 } else if extent > 16 { 16 } else { extent }
      if field.refs[index * 2] > av1_intra_frame {
        count = count + 1
        let scaled = (step4 * 4) >> sub_x
        let pred_w = if w < scaled { w } else { scaled }
        let half = 32 >> sub_y
        let pred_h = if half < h >> 1 { half } else { h >> 1 }
        if !av1_obmc_overlap(
            state,
            info,
            target,
            plane,
            tile_x,
            tile_y,
            limit,
            cand_row,
            cand_col,
            x4,
            block.mi_row,
            sub_x,
            sub_y,
            pred_w,
            pred_h,
            av1_obmc_mask(pred_h),
            0,
          ) {
          return false
        }
      }
      x4 = x4 + step4
    }
  }
  if block.avail_l {
    let log2_height = av1_msac_floor_log2(block.height4)
    let n_limit = if 4 < log2_height { 4 } else { log2_height }
    let mut y4 = block.mi_row
    let mut count = 0
    while count < n_limit &&
          y4 < info.mi_rows &&
          y4 < block.mi_row + block.height4 {
      let cand_col = block.mi_col - 1
      let cand_row = y4 | 1
      if cand_col < 0 || cand_row < 0 || cand_row >= field.rows {
        return false
      }
      let index = cand_row * field.cols + cand_col
      let extent = field.height4[index]
      let step4 = if extent < 2 { 2 } else if extent > 16 { 16 } else { extent }
      if field.refs[index * 2] > av1_intra_frame {
        count = count + 1
        let scaled = (step4 * 4) >> sub_y
        let pred_h = if h < scaled { h } else { scaled }
        let half = 32 >> sub_x
        let pred_w = if half < w >> 1 { half } else { w >> 1 }
        if !av1_obmc_overlap(
            state,
            info,
            target,
            plane,
            tile_x,
            tile_y,
            limit,
            cand_row,
            cand_col,
            block.mi_col,
            y4,
            sub_x,
            sub_y,
            pred_w,
            pred_h,
            av1_obmc_mask(pred_w),
            1,
          ) {
          return false
        }
      }
      y4 = y4 + step4
    }
  }
  true
}

///|
/// The stored reference frame behind one reference name.
fn av1_inter_reference(info : Av1InterInfo, name : Int) -> Av1RefFrame? {
  if name < av1_last_frame || name >= info.refs.length() {
    None
  } else {
    info.refs[name]
  }
}

///|
/// residual( ) for an inter block (AV1 搂5.11.34). Prediction already filled the
/// block, so every transform only adds its residual. Luma follows the transform
/// tree the block recorded; chroma keeps a uniform raster inside each 64-pixel
/// chunk, and both share the intra path's transform decoding.
fn av1_inter_residual(
  decoder : Av1SymbolDecoder,
  state : Av1IntraState,
  block : Av1InterBlock,
  tile_block : Av1SbBlock,
  luma_width : Int,
  luma_height : Int,
  roots : Array[Array[Av1TxNode]],
) -> Bool {
  let has_chroma = av1_intra_has_chroma(state, tile_block)
  let plane_count = if has_chroma { 3 } else { 1 }
  // Sub-eight-pixel inter blocks can share chroma with a later intra CfL
  // owner. Preserve their reconstructed luma, including skipped residuals,
  // so that owner sees the whole subsampled footprint rather than stale data.
  av1_cfl_begin_block(state.cfl, tile_block, !state.monochrome && !has_chroma)
  let chunks_x = if block.block_width > 64 { block.block_width / 64 } else { 1 }
  let chunks_y = if block.block_height > 64 {
    block.block_height / 64
  } else {
    1
  }
  let chunk_width = block.block_width / chunks_x
  let chunk_height = block.block_height / chunks_y
  for chunk_y in 0..= roots.length() {
          return false
        }
        for leaf in roots[chunk] {
          if !av1_intra_transform(
              decoder,
              state,
              0,
              0,
              leaf.col,
              leaf.row,
              leaf.width,
              leaf.height,
              chunk_width,
              chunk_height,
              block.skip,
              inter=true,
            ) {
            return false
          }
        }
      }
      for plane in 1.. Bool {
  let mut y = 0
  while y < height {
    let mut x = 0
    while x < width {
      if !av1_intra_transform(
          decoder,
          state,
          plane,
          0,
          origin_x + x,
          origin_y + y,
          tx_width,
          tx_height,
          width,
          height,
          block.skip,
          inter=true,
        ) {
        return false
      }
      x = x + tx_width
    }
    y = y + tx_height
  }
  true
}

///|
/// intra_block_mode_info( ) for an intra block coded inside an inter frame
/// (AV1 搂5.11.27). The luma mode reads the size-group table rather than the
/// neighbour modes an intra frame would use, and the transform size comes from
/// the same read_block_tx_size the inter branch uses.
fn av1_intra_in_inter_block(
  decoder : Av1SymbolDecoder,
  state : Av1IntraState,
  info : Av1InterInfo,
  inter : Av1InterBlock,
  block : Av1SbBlock,
  block_index : Int,
  has_chroma : Bool,
) -> Bool {
  let group = if block_index < av1_size_group.length() {
    av1_size_group[block_index]
  } else {
    return false
  }
  if group >= info.cdfs.y_mode.length() {
    return false
  }
  let y_mode = decoder.symbol(info.cdfs.y_mode[group])
  if y_mode < 0 || y_mode > 12 {
    return false
  }
  if !av1_intra_read_mode_angle(decoder, state, y_mode, block_index) {
    return false
  }
  let mut uv_mode = AV1_INTRA_DC
  if has_chroma {
    let cfl_allowed = if state.seg_lossless {
      block.width <= 4 << state.sub_x && block.height <= 4 << state.sub_y
    } else {
      block.width <= 32 && block.height <= 32
    }
    let mode = decoder.symbol(
      if cfl_allowed {
        state.uv_cfl[y_mode]
      } else {
        state.uv_no_cfl[y_mode]
      },
    )
    if mode == AV1_INTRA_CFL {
      if !cfl_allowed || !av1_cfl_read_alphas(decoder, state.cfl) {
        return false
      }
    } else if !av1_intra_read_mode_angle(
        decoder,
        state,
        mode,
        block_index,
        plane=1,
      ) {
      return false
    }
    uv_mode = mode
  }
  // `palette_mode_info` (AV1 搂5.11.21) sits between the uv mode and filter
  // intra, exactly as in the intra-only block path.
  if !av1_palette_read_colors(
      decoder,
      state.palette,
      block,
      state.bit_depth,
      y_mode,
      uv_mode,
      has_chroma,
      state.allow_screen_content_tools,
    ) {
    return false
  }
  // `filter_intra_mode_info` requires PaletteSizeY == 0 (AV1 搂5.11.29).
  if state.enable_filter_intra &&
    y_mode == AV1_INTRA_DC &&
    state.palette.y_colors.is_empty() &&
    block.width <= 32 &&
    block.height <= 32 {
    match decoder.symbol(state.filter_intra[block_index]) {
      0 => ()
      1 => {
        let mode = decoder.symbol(state.filter_intra_mode_cdf)
        if mode < 0 || mode >= 5 {
          return false
        }
        state.filter_intra_mode = mode
      }
      _ => return false
    }
  }
  // The palette index map precedes transform-size syntax, as for an intra frame.
  if !av1_intra_palette_maps(decoder, state, block) {
    return false
  }
  inter.y_mode = y_mode
  // Intra blocks still publish their reference pair and geometry. In
  // particular, the unused second reference is NONE, not the empty motion
  // field's zero: the next block's single/compound context reads that value.
  av1_motion_field_store(
    info.field,
    inter.mi_row,
    inter.mi_col,
    inter.width4,
    inter.height4,
    y_mode,
    [av1_intra_frame, av1_none_frame],
    [0, 0],
    [0, 0],
    skip=inter.skip,
  )
  let size = match av1_read_block_tx_size(decoder, state, info, inter) {
    Some(value) => value
    None => return false
  }
  let (tx_width, tx_height, _) = size
  av1_intra_record_modes(state, block, y_mode, uv_mode, has_chroma, inter.skip)
  if !av1_intra_residuals(
      decoder,
      state,
      block,
      y_mode,
      uv_mode,
      tx_width,
      tx_height,
      inter.skip,
    ) {
    return false
  }
  av1_tx_traversal_record_size(
    state.tx_neighbors,
    {
      row: block.row,
      col: block.col,
      width: block.width,
      height: block.height,
    },
    tx_width,
    tx_height,
  )
  true
}

///|
/// Record the tile-local mode and skip grids a later block's contexts read.
fn av1_intra_record_modes(
  state : Av1IntraState,
  block : Av1SbBlock,
  y_mode : Int,
  uv_mode : Int,
  has_chroma : Bool,
  skip : Int,
) -> Unit {
  let x4 = block.col / 4
  let y4 = block.row / 4
  for row in y4..<(y4 + block.height / 4) {
    if row >= state.rows {
      break
    }
    for col in x4..<(x4 + block.width / 4) {
      if col >= state.cols {
        break
      }
      state.skips[row * state.cols + col] = skip
      state.y_modes[row * state.cols + col] = y_mode
      if has_chroma {
        state.uv_modes[row * state.cols + col] = uv_mode
      }
    }
  }
}

///|
/// decode_block( ) for an inter frame (AV1 搂5.11.17). The leaf replaces the
/// intra one entirely: skip may be implied by skip_mode, the block may be intra,
/// and an inter block's samples come from motion compensation rather than from
/// its own neighbourhood.
fn av1_inter_tile_block(
  decoder : Av1SymbolDecoder,
  state : Av1IntraState,
  info : Av1InterInfo,
  block : Av1SbBlock,
  partition : Av1SbPartition,
) -> Bool {
  state.prediction_partition = partition
  state.y_angle_delta = 0
  state.uv_angle_delta = 0
  state.filter_intra_mode = -1
  let block_index = match av1_intra_block_index(block.width, block.height) {
    Some(value) => value
    None => return false
  }
  let x4 = block.col / 4
  let y4 = block.row / 4
  let mi_row = info.tile_row_start + y4
  let mi_col = info.tile_col_start + x4
  let has_chroma = av1_intra_has_chroma(state, block)
  let current = av1_inter_block(
    mi_row,
    mi_col,
    block.width / 4,
    block.height / 4,
    block_index,
    mi_row > info.tile_row_start,
    mi_col > info.tile_col_start,
  )
  let mut seg_id = -1
  if state.segmentation.seg_id_pre_skip || !state.segmentation.update_map {
    seg_id = av1_read_segment_id(
      decoder,
      state,
      y4,
      x4,
      current.avail_u,
      current.avail_l,
      0,
      true,
      width4=current.width4,
      height4=current.height4,
    )
  }
  current.segment_skip = av1_segment_feature(state.segmentation, seg_id, 6)
  current.segment_global = av1_segment_feature(state.segmentation, seg_id, 7)
  current.segment_ref = if av1_segment_feature(state.segmentation, seg_id, 5) {
    state.segmentation.feature_data[seg_id][5]
  } else {
    -1
  }
  if !av1_read_skip_mode(decoder, info, current) {
    return false
  }
  if !current.skip_mode {
    let mut skip_context = 0
    if y4 > 0 {
      skip_context = skip_context + state.skips[(y4 - 1) * state.cols + x4]
    }
    if x4 > 0 {
      skip_context = skip_context + state.skips[y4 * state.cols + x4 - 1]
    }
    let skip = if current.segment_skip {
      1
    } else {
      decoder.symbol(state.skip_cdfs[skip_context])
    }
    if skip != 0 && skip != 1 {
      return false
    }
    current.skip = skip
  }
  // `read_segment_id` sits between skip and the CDEF index (AV1 搂5.11.13), and
  // the segment it names decides the block's quantizer index from here on. A
  // block that skip mode forced to skip takes the predicted id and codes no
  // symbol, which the reader itself applies. The gate one level up refuses a
  // frame whose features would read the symbol before skip, so the preskip
  // form cannot reach this decoder.
  if !state.segmentation.seg_id_pre_skip && state.segmentation.update_map {
    seg_id = av1_read_segment_id(
      decoder,
      state,
      y4,
      x4,
      current.avail_u,
      current.avail_l,
      current.skip,
      false,
      width4=current.width4,
      height4=current.height4,
    )
  }
  if !av1_intra_cdef_index(decoder, state, block, current.skip) {
    return false
  }
  // The superblock's quantizer delta follows the CDEF index (AV1 搂6.10.10); a
  // whole-superblock block that is skipped is the one case libdav1d does not
  // read it for.
  if av1_at_superblock_origin(state, current.mi_row, current.mi_col) &&
    !(current.skip == 1 &&
    av1_superblock_block_is_whole(
      block.width,
      block.height,
      state.superblock_size,
    )) {
    av1_read_superblock_delta_q(decoder, state)
    av1_read_superblock_delta_lf(decoder, state)
  }
  // The segment's ALT_Q builds on this superblock's index, so the block's
  // quantizer index is settled after the delta, not before it.
  av1_seg_apply_block(state, y4, x4, current.width4, current.height4, seg_id)
  if !av1_read_is_inter(decoder, info, current) {
    return false
  }
  if !current.is_inter {
    return av1_intra_in_inter_block(
      decoder, state, info, current, block, block_index, has_chroma,
    )
  }
  let have_top_right = match
    av1_intra_edge_table(true, block_index, partition) {
    Some(table) =>
      av1_intra_has_top_right(
        block.width,
        block.height,
        mi_row,
        mi_col,
        0,
        0,
        block.width,
        0,
        state.superblock_size,
        current.avail_u,
        mi_col + current.width4 < info.tile_col_end,
        table,
      )
    None => false
  }
  if !av1_inter_block_motion(decoder, info, current, have_top_right~) {
    return false
  }
  // The specification writes the motion field before transform sizing and
  // before prediction, so a block's own entries are already visible to both.
  av1_motion_field_store(
    info.field,
    mi_row,
    mi_col,
    current.width4,
    current.height4,
    current.y_mode,
    [current.ref_frame, current.ref_frame1],
    [current.mv_row, current.mv_row1],
    [current.mv_col, current.mv_col1],
    skip=current.skip,
    skip_mode=if current.skip_mode { 1 } else { 0 },
    compound_idx=current.compound_idx,
    comp_group_idx=current.comp_group_idx,
    interp=[current.interp_vertical, current.interp_horizontal],
  )
  av1_intra_record_modes(
    state,
    block,
    current.y_mode,
    AV1_INTRA_DC,
    has_chroma,
    current.skip,
  )
  let sized = match av1_read_block_tx_size(decoder, state, info, current) {
    Some(value) => value
    None => return false
  }
  let (tx_width, tx_height, roots) = sized
  state.current = Some(current)
  if !av1_inter_predict(state, info, current, block) {
    return false
  }
  if !av1_inter_residual(
      decoder, state, current, block, tx_width, tx_height, roots,
    ) {
    return false
  }
  true
}

///|
/// Validate frame-level prerequisites for inter reconstruction. Warp eligibility
/// is decided for each prediction and does not invalidate a legal frame.
pub fn av1_inter_frame_supported(
  header : Av1FrameHeaderInfo,
  sequence : Av1SequenceInfo,
) -> Bool {
  // AV1 7.11.3.1 derives useWarp per prediction. Integer-motion frames and
  // legal global models failing the shear limits select ordinary MC; those
  // conditions do not invalidate the frame. Both single and compound paths
  // already apply that choice before their final rounding and blending.
  header.frame_is_intra || !header.tile_payload.is_empty()
}