///|
pub(all) struct DefiningWord {
  mask : Int32
  sign : Int32
} derive(Debug, Eq)

///|
pub extend DefiningWord with @debug.Debug::{to_repr}

///|
pub extend DefiningWord with Eq::{equal, not_equal}

///|
fn parity(x : Int32) -> Int32 {
  let mut n = x
  let mut count = 0
  while n > 0 {
    count += n & 1
    n = n >> 1
  }
  count
}

///|
pub fn defining_relation(
  base : Int32,
  generators : Array[Generator],
) -> Array[DefiningWord] raise DesignError {
  if base < 1 ||
    base > 16 ||
    generators.length() > 10 ||
    base > 16 - generators.length() {
    raise Budget("defining relation exceeds factor/word budget")
  }
  let words : Array[DefiningWord] = [{ mask: 0, sign: 1, }]
  for i, g in generators {
    if g.mask <= 0 || g.mask >= 1 << base || (g.sign != 1 && g.sign != -1) {
      raise Invalid("invalid generator")
    }
    let word = g.mask | (1 << (base + i))
    let size = words.length()
    for j in 0.. Int32? raise DesignError {
  let mut result : Int32? = None
  for word in defining_relation(base, generators) {
    if word.mask != 0 {
      let length = parity(word.mask)
      result = match result {
        None => Some(length)
        Some(n) => Some(if length < n { length } else { n })
      }
    }
  }
  result
}

///|
pub fn aliases(
  effect : Int32,
  base : Int32,
  generators : Array[Generator],
) -> Array[DefiningWord] raise DesignError {
  let words = defining_relation(base, generators)
  if effect < 0 || effect >= 1 << (base + generators.length()) {
    raise Invalid("effect mask out of range")
  }
  [
    for word in words => { mask: word.mask ^ effect, sign: word.sign, }
  ]
}

///|
pub fn effect_name(
  mask : Int32,
  names : Array[String],
) -> String raise DesignError {
  if names.length() > 16 || mask < 0 || mask >= 1 << names.length() {
    raise Invalid("effect mask out of range")
  }
  if mask == 0 {
    return "I"
  }
  let selected = []
  for j, name in names {
    if (mask & (1 << j)) != 0 {
      selected.push(name)
    }
  }
  selected.join("*")
}