///|
pub struct GraphReport {
  variables : Int32
  checks : Int32
  edges : Int32
  isolated_variables : Int32
  empty_checks : Int32
  components : Int32
  variable_degrees : Array[Int32]
  check_degrees : Array[Int32]
  four_cycles : Int64
} derive(Debug, ToJson)

///|
/// Computes exact four-cycle count. Comparison budget bounds dense adversarial input.
pub fn ParityCheck::analyze(self : ParityCheck) -> GraphReport raise CodeError {
  let mut work = 0L
  for column in self.columns {
    let degree = column.length().to_int64()
    work += degree * (degree - 1L) / 2L
    if work > 2000000L {
      raise ResourceLimit("four-cycle pair budget")
    }
  }
  let pairs : Map[(Int32, Int32), Int32] = Map([])
  let mut four_cycles = 0L
  for column in self.columns {
    for a in 0.. Int32? raise CodeError {
  let total = self.n + self.checks()
  if total > 8192 {
    raise ResourceLimit("girth vertices")
  }
  let mut work = 0
  let mut best = total + 1
  for start in 0.. 20000000 {
          raise ResourceLimit("girth edge visits")
        }
        if distances[target] < 0 {
          distances[target] = distances[node] + 1
          parents[target] = node
          queue.push(target)
        } else if parents[node] != target {
          best = best.min(distances[node] + distances[target] + 1)
        }
      }
    }
    if best == 4 {
      return Some(4)
    }
  }
  if best > total {
    None
  } else {
    Some(best)
  }
}

///|
pub struct DistanceReport {
  dimension : Int32
  examined_nonzero_words : Int32
  minimum_distance : Int32?
  weight_distribution : Array[Int32]
} derive(Debug, ToJson)

///|
/// Exhaustive exact analysis of small linear codes only (k<=20, n*2^k<=8M).
pub fn Encoder::distance_report(
  self : Encoder,
) -> DistanceReport raise CodeError {
  let k = self.dimension()
  if k > 20 {
    raise ResourceLimit("distance dimension")
  }
  let count = 1 << k
  if self.code.n.to_int64() * count.to_int64() > 8000000L {
    raise ResourceLimit("distance enumeration work")
  }
  let generator = self.generator()
  let word = Array::make(self.code.n, 0)
  let distribution = Array::make(self.code.n + 1, 0)
  distribution[0] = 1
  let mut previous_gray = 0
  let mut minimum = self.code.n + 1
  for index in 1..> 1)
    let changed = gray ^ previous_gray
    let mut bit = 0
    while ((changed >> bit) & 1) == 0 {
      bit += 1
    }
    let mut weight = 0
    for v in 0..