/// Pure 8-bit AV1 intra prediction (ยง7.11.2).
///
/// References are supplied explicitly so block traversal can decide frame
/// availability. `above` and `left` contain at least width/height samples when
/// available; unavailable edges may be empty. Output is a row-major block.

///|
pub const AV1_INTRA_DC : Int = 0

///|
pub const AV1_INTRA_V : Int = 1

///|
pub const AV1_INTRA_H : Int = 2

///|
pub const AV1_INTRA_SMOOTH : Int = 9

///|
pub const AV1_INTRA_SMOOTH_V : Int = 10

///|
pub const AV1_INTRA_SMOOTH_H : Int = 11

///|
pub const AV1_INTRA_PAETH : Int = 12

///|
fn av1_intra_log2(size : Int) -> Int? {
  if size == 4 {
    Some(2)
  } else if size == 8 {
    Some(3)
  } else if size == 16 {
    Some(4)
  } else if size == 32 {
    Some(5)
  } else if size == 64 {
    Some(6)
  } else {
    None
  }
}

///|
fn av1_intra_clip(value : Int, bit_depth : Int) -> Int {
  let hi = (1 << bit_depth) - 1
  if value < 0 {
    0
  } else if value > hi {
    hi
  } else {
    value
  }
}

///|
fn av1_intra_abs(value : Int) -> Int {
  if value < 0 {
    -value
  } else {
    value
  }
}

///|
fn av1_intra_round2(value : Int64, shift : Int) -> Int {
  ((value + (1L << (shift - 1))) >> shift).to_int()
}

///|
fn av1_smooth_weights(log2 : Int) -> Array[Int] {
  if log2 == 2 {
    [255, 149, 85, 64]
  } else if log2 == 3 {
    [255, 197, 146, 105, 73, 50, 37, 32]
  } else if log2 == 4 {
    [255, 225, 196, 170, 145, 123, 102, 84, 68, 54, 43, 33, 26, 20, 17, 16]
  } else if log2 == 5 {
    [
      255, 240, 225, 210, 196, 182, 169, 157, 145, 133, 122, 111, 101, 92, 83, 74,
      66, 59, 52, 45, 39, 34, 29, 25, 21, 17, 14, 12, 10, 9, 8, 8,
    ]
  } else {
    [
      255, 248, 240, 233, 225, 218, 210, 203, 196, 189, 182, 176, 169, 163, 156,
      150, 144, 138, 133, 127, 121, 116, 111, 106, 101, 96, 91, 86, 82, 77, 73, 69,
      65, 61, 57, 54, 50, 47, 44, 41, 38, 35, 32, 29, 27, 25, 22, 20, 18, 16, 15,
      13, 12, 10, 9, 8, 7, 6, 6, 5, 5, 4, 4, 4,
    ]
  }
}

///|
fn av1_intra_edges(
  above : Array[Int],
  left : Array[Int],
  top_left : Int,
  width : Int,
  height : Int,
  have_above : Bool,
  have_left : Bool,
  bit_depth : Int,
) -> (Array[Int], Array[Int], Int)? {
  if have_above && above.length() < width {
    return None
  }
  if have_left && left.length() < height {
    return None
  }
  let base = 1 << (bit_depth - 1)
  let above_out : Array[Int] = Array::make(width, 0)
  let left_out : Array[Int] = Array::make(height, 0)
  if have_above {
    for j in 0.. Array[Int]? {
  let logw = match av1_intra_log2(width) {
    Some(v) => v
    None => return None
  }
  let logh = match av1_intra_log2(height) {
    Some(v) => v
    None => return None
  }
  if bit_depth < 8 || bit_depth > 12 {
    return None
  }
  let (a, l, tl) = match
    av1_intra_edges(
      above, left, top_left, width, height, have_above, have_left, bit_depth,
    ) {
    Some(v) => v
    None => return None
  }
  let out : Array[Int] = Array::make(width * height, 0)
  if mode == AV1_INTRA_DC {
    let mut sum = 0
    if have_above {
      for j in 0..> 1)) / (width + height)
    } else if have_above {
      av1_intra_clip((sum + (width >> 1)) >> logw, bit_depth)
    } else if have_left {
      av1_intra_clip((sum + (height >> 1)) >> logh, bit_depth)
    } else {
      1 << (bit_depth - 1)
    }
    for i in 0..