///|
/// Sequence analytics for traces, rosters, and solver decisions.
///
/// The routines are allocation-conscious for small integer traces and expose
/// stable edit, transition, run, and normalization metrics used by diagnostics.
pub struct SequenceReport {
  length : Int
  minimum : Int
  maximum : Int
  transitions : Int
  runs : Int
  checksum : Int
}

///|
/// Analyze an integer sequence.
pub fn sequence_report(values : Array[Int]) -> SequenceReport {
  let mut minimum = 0
  let mut maximum = 0
  if values.length() > 0 {
    minimum = values[0]
    maximum = values[0]
    for value in values {
      if value < minimum {
        minimum = value
      }
      if value > maximum {
        maximum = value
      }
    }
  }
  {
    length: values.length(),
    minimum,
    maximum,
    transitions: assignment_transitions(values),
    runs: sequence_run_count(values),
    checksum: sequence_checksum(values),
  }
}

///|
/// Count maximal runs.
pub fn sequence_run_count(values : Array[Int]) -> Int {
  if values.length() == 0 {
    return 0
  }
  let mut result = 1
  for index in 1.. Array[Int] {
  let result : Array[Int] = []
  if values.length() == 0 {
    return result
  }
  let mut current = 1
  for index in 1.. Array[(Int, Int)] {
  let result : Array[(Int, Int)] = []
  if values.length() == 0 {
    return result
  }
  let mut value = values[0]
  let mut length = 1
  for index in 1.. Int {
  let mut result = 17
  for index, value in values {
    result = result * 31 + value + index * 7
  }
  result
}

///|
/// Normalize values by subtracting the minimum.
pub fn sequence_normalize(values : Array[Int]) -> Array[Int] {
  let result : Array[Int] = []
  if values.length() == 0 {
    return result
  }
  let mut minimum = values[0]
  for value in values {
    if value < minimum {
      minimum = value
    }
  }
  for value in values {
    result.push(value - minimum)
  }
  result
}

///|
/// Return a cumulative sum sequence.
pub fn sequence_cumulative(values : Array[Int]) -> Array[Int] {
  let result : Array[Int] = []
  let mut total = 0
  for value in values {
    total += value
    result.push(total)
  }
  result
}

///|
/// Return a first-difference sequence with an initial zero.
pub fn sequence_delta(values : Array[Int]) -> Array[Int] {
  let result : Array[Int] = []
  if values.length() == 0 {
    return result
  }
  result.push(0)
  for index in 1.. Array[Int] {
  let result : Array[Int] = []
  if kernel.length() == 0 {
    return values.copy()
  }
  let center = kernel.length() / 2
  for index in 0..= 0 && source < values.length() {
        total += values[source] * kernel[kernel_index]
      }
    }
    result.push(total)
  }
  result
}

///|
/// Return the longest common subsequence length.
pub fn sequence_lcs_length(left : Array[Int], right : Array[Int]) -> Int {
  let table = int_matrix(left.length() + 1, right.length() + 1)
  for row in 1..<(left.length() + 1) {
    for column in 1..<(right.length() + 1) {
      let value = if left[row - 1] == right[column - 1] {
        table.get(row - 1, column - 1) + 1
      } else if table.get(row - 1, column) > table.get(row, column - 1) {
        table.get(row - 1, column)
      } else {
        table.get(row, column - 1)
      }
      ignore(table.set(row, column, value))
    }
  }
  table.get(left.length(), right.length())
}

///|
/// Return Levenshtein edit distance.
pub fn sequence_edit_distance(left : Array[Int], right : Array[Int]) -> Int {
  let table = int_matrix(left.length() + 1, right.length() + 1)
  for row in 0..<(left.length() + 1) {
    ignore(table.set(row, 0, row))
  }
  for column in 0..<(right.length() + 1) {
    ignore(table.set(0, column, column))
  }
  for row in 1..<(left.length() + 1) {
    for column in 1..<(right.length() + 1) {
      let mismatch = if left[row - 1] == right[column - 1] { 0 } else { 1 }
      let substitution = table.get(row - 1, column - 1) + mismatch
      let insertion = table.get(row, column - 1) + 1
      let deletion = table.get(row - 1, column) + 1
      let best = if substitution < insertion { substitution } else { insertion }
      ignore(
        table.set(row, column, if deletion < best { deletion } else { best }),
      )
    }
  }
  table.get(left.length(), right.length())
}

///|
/// Return whether a sequence contains a contiguous pattern.
pub fn sequence_contains(values : Array[Int], pattern : Array[Int]) -> Bool {
  if pattern.length() == 0 {
    return true
  }
  if pattern.length() > values.length() {
    return false
  }
  for start in 0..<(values.length() - pattern.length() + 1) {
    let mut matches = true
    for index in 0.. Bool {
  if values.length() < 2 {
    return true
  }
  for index in 0.. String {
  "length=\{self.length}, min=\{self.minimum}, max=\{self.maximum}, transitions=\{self.transitions}, runs=\{self.runs}, checksum=\{self.checksum}"
}