///|
/// Small exact puzzle models backed by finite-domain primitives.
///
/// These helpers are useful as regression fixtures as well as educational
/// examples: a puzzle carries its dimensions and clues, generates legal line
/// patterns, propagates forced cells, and exposes a stable verification path.
pub struct NonogramLine {
  length : Int
  clues : Array[Int]
}

///|
/// Create a nonogram line clue.
pub fn nonogram_line(length : Int, clues : Array[Int]) -> NonogramLine? {
  if length < 0 || (clues.length() == 0 && length > 0) {
    return None
  }
  for clue in clues {
    if clue < 1 {
      return None
    }
  }
  Some({ length, clues: clues.copy() })
}

///|
/// Return the minimum cells required by clues and separators.
pub fn NonogramLine::minimum_used(self : NonogramLine) -> Int {
  let mut result = 0
  for clue in self.clues {
    result += clue
  }
  if self.clues.length() > 0 {
    result += self.clues.length() - 1
  }
  result
}

///|
/// Return whether a binary line satisfies this clue.
pub fn NonogramLine::matches(self : NonogramLine, cells : Array[Int]) -> Bool {
  if cells.length() != self.length {
    return false
  }
  let runs : Array[Int] = []
  let mut current = 0
  for cell in cells {
    if cell != 0 {
      current += 1
    } else if current > 0 {
      runs.push(current)
      current = 0
    }
  }
  if current > 0 {
    runs.push(current)
  }
  if runs.length() != self.clues.length() {
    return false
  }
  for index in 0.. Array[Array[Int]] {
  let result : Array[Array[Int]] = []
  let current : Array[Int] = []
  for _ in 0.. Unit {
  if clue_index >= line.clues.length() {
    result.push(cells.copy())
    return
  }
  let remaining = line.clues.length() - clue_index - 1
  let mut required_after = remaining
  let clue_start = clue_index + 1
  for index in clue_start..= line.clues.length() {
      nonogram_generate(line, clue_index + 1, line.length, cells, result)
    } else {
      nonogram_generate(line, clue_index + 1, next, cells, result)
    }
    for cell in start.. NonogramPuzzle? {
  if rows.length() == 0 || columns.length() == 0 {
    return None
  }
  for row in rows {
    if row.length != columns.length() {
      return None
    }
  }
  for column in columns {
    if column.length != rows.length() {
      return None
    }
  }
  Some({
    rows: rows.copy(),
    columns: columns.copy(),
    cells: filled_matrix(rows.length(), columns.length(), -1),
  })
}

///|
/// Read puzzle dimensions.
pub fn NonogramPuzzle::row_count(self : NonogramPuzzle) -> Int {
  self.rows.length()
}

///|
/// Read column count.
pub fn NonogramPuzzle::column_count(self : NonogramPuzzle) -> Int {
  self.columns.length()
}

///|
/// Fix a cell to empty or filled.
pub fn NonogramPuzzle::fix(
  self : NonogramPuzzle,
  row : Int,
  column : Int,
  value : Int,
) -> Bool {
  if (value != 0 && value != 1) || !self.cells.valid_cell(row, column) {
    return false
  }
  let current = self.cells.get(row, column)
  if current == -1 {
    ignore(self.cells.set(row, column, value))
    true
  } else {
    current == value
  }
}

///|
/// Read a cell, or -1 when outside the puzzle.
pub fn NonogramPuzzle::cell(
  self : NonogramPuzzle,
  row : Int,
  column : Int,
) -> Int {
  if !self.cells.valid_cell(row, column) {
    -1
  } else {
    self.cells.get(row, column)
  }
}

///|
/// Filter line patterns by fixed cells.
fn nonogram_compatible(
  patterns : Array[Array[Int]],
  fixed : Array[Int],
) -> Array[Array[Int]] {
  let result : Array[Array[Int]] = []
  for pattern in patterns {
    let mut compatible = true
    for index, value in fixed {
      if value >= 0 && value != pattern[index] {
        compatible = false
      }
    }
    if compatible {
      result.push(pattern)
    }
  }
  result
}

///|
/// Propagate forced cells from row and column pattern intersections.
pub fn NonogramPuzzle::propagate(self : NonogramPuzzle) -> Int {
  let mut changes = 0
  for row in 0..= 0 && self.cell(row, column) != value {
        ignore(self.cells.set(row, column, value))
        changes += 1
      }
    }
  }
  for column in 0..= 0 && self.cell(row, column) != value {
        ignore(self.cells.set(row, column, value))
        changes += 1
      }
    }
  }
  changes
}

///|
/// Return whether every row and column is solved.
pub fn NonogramPuzzle::solved(self : NonogramPuzzle) -> Bool {
  for row in 0.. Bool {
  for row in 0.. String {
  let builder = StringBuilder()
  for row in 0.. 0 {
      builder.write_char('\n')
    }
    for column in 0.. LatinPuzzle? {
  if size < 1 {
    return None
  }
  Some({ size, values: filled_matrix(size, size, -1) })
}

///|
/// Set a Latin square cell.
pub fn LatinPuzzle::set(
  self : LatinPuzzle,
  row : Int,
  column : Int,
  value : Int,
) -> Bool {
  if row < 0 ||
    row >= self.size ||
    column < 0 ||
    column >= self.size ||
    value < 0 ||
    value >= self.size {
    return false
  }
  if self.values.row(row).contains(value) ||
    self.values.column(column).contains(value) {
    return self.values.get(row, column) == value
  }
  ignore(self.values.set(row, column, value))
  true
}

///|
/// Return legal values for a Latin square cell.
pub fn LatinPuzzle::candidates(
  self : LatinPuzzle,
  row : Int,
  column : Int,
) -> Array[Int] {
  let result : Array[Int] = []
  if row < 0 || row >= self.size || column < 0 || column >= self.size {
    return result
  }
  let row_values = self.values.row(row)
  let column_values = self.values.column(column)
  for value in 0.. Bool {
  for row in 0.. Bool {
  if values.length() != size {
    return false
  }
  for value in 0.. LatinPuzzle? {
  match latin_puzzle(size) {
    None => None
    Some(puzzle) => {
      for row in 0.. Int {
  self.values.signature() + self.size * 17
}