///|
fn add_run_history(
  run_length : Int32,
  history : Array[Int32],
  size : Int32,
) -> Unit {
  let len = if history[0] == 0 { run_length + size } else { run_length }
  for i = 6; i >= 1; i = i - 1 {
    history[i] = history[i - 1]
  }
  history[0] = len
}

///|
fn count_finder_patterns(history : Array[Int32]) -> Int32 {
  let n = history[1]
  let core = n > 0 &&
    history[2] == n &&
    history[3] == n * 3 &&
    history[4] == n &&
    history[5] == n
  let mut count = 0
  if core && history[0] >= n * 4 && history[6] >= n {
    count += 1
  }
  if core && history[6] >= n * 4 && history[0] >= n {
    count += 1
  }
  count
}

///|
/// N1 (runs) and N3 (finder-like patterns) for one row.
fn penalty_row(m : Matrix, y : Int32) -> Int32 {
  let size = m.size
  let mut result = 0
  let mut run_color = false
  let mut run_len = 0
  let history = Array::make(7, 0)
  for x in 0.. 5 {
        result += 1
      }
    } else {
      add_run_history(run_len, history, size)
      if !run_color {
        result += count_finder_patterns(history) * 40
      }
      run_color = color
      run_len = 1
    }
  }
  let mut tail = run_len
  if run_color {
    add_run_history(tail, history, size)
    tail = 0
  }
  tail += size
  add_run_history(tail, history, size)
  result + count_finder_patterns(history) * 40
}

///|
/// N1 and N3 for one column.
fn penalty_column(m : Matrix, x : Int32) -> Int32 {
  let size = m.size
  let mut result = 0
  let mut run_color = false
  let mut run_len = 0
  let history = Array::make(7, 0)
  for y in 0.. 5 {
        result += 1
      }
    } else {
      add_run_history(run_len, history, size)
      if !run_color {
        result += count_finder_patterns(history) * 40
      }
      run_color = color
      run_len = 1
    }
  }
  let mut tail = run_len
  if run_color {
    add_run_history(tail, history, size)
    tail = 0
  }
  tail += size
  add_run_history(tail, history, size)
  result + count_finder_patterns(history) * 40
}

///|
/// ISO Annex J penalty score: N1 runs, N2 blocks, N3 finder-like, N4 balance.
fn penalty(m : Matrix) -> Int32 {
  let size = m.size
  let mut result = 0
  for y in 0..= total * 10 {
    dark * 20 - total * 10
  } else {
    total * 10 - dark * 20
  }
  let k = (diff + total - 1) / total - 1
  result + k * 10
}