///|
fn mask_set(n : Int32, mask : Int32) -> Array[Int32] {
  let xs = []
  for e = 0; e < n; e = e + 1 {
    if (mask & (1 << e)) != 0 {
      xs.push(e)
    }
  }
  xs
}

///|
fn set_mask(xs : Array[Int32]) -> Int32 {
  let mut mask = 0
  for e in xs {
    mask = mask | (1 << e)
  }
  mask
}

///|
/// Fully checks local rank axioms; these imply all finite rank axioms.
/// Tables are limited to 12 elements (4096 rows).
pub fn Matroid::from_ranks(
  n : Int32,
  ranks : Array[Int32],
) -> Matroid raise MatroidError {
  if n < 0 || n > 12 {
    raise InvalidInput("rank table supports 0..12 elements")
  }
  let count = 1 << n
  if ranks.length() != count || ranks[0] != 0 {
    raise InvalidInput("rank table length or empty rank")
  }
  for mask = 0; mask < count; mask = mask + 1 {
    let r = ranks[mask]
    if r < 0 || r > mask_set(n, mask).length() {
      raise InvalidInput("rank outside cardinality bound")
    }
    for i = 0; i < n; i = i + 1 {
      if (mask & (1 << i)) == 0 {
        let one = mask | (1 << i)
        if ranks[one] < r || ranks[one] > r + 1 {
          raise InvalidInput("rank monotonicity or unit increment")
        }
        for j = i + 1; j < n; j = j + 1 {
          if (mask & (1 << j)) == 0 {
            let two = mask | (1 << j)
            if ranks[one] + ranks[two] < r + ranks[one | (1 << j)] {
              raise InvalidInput("rank violates diminishing returns")
            }
          }
        }
      }
    }
  }
  let owned = ranks.copy()
  { size: n, model: "rank-table", evaluate: xs => owned[set_mask(xs)], }
}

///|
pub fn Matroid::rank_snapshot(
  self : Matroid,
) -> Array[Int32] raise MatroidError {
  if self.size > 12 {
    raise InvalidInput("rank snapshot supports at most 12 elements")
  }
  Array::makei(1 << self.size, mask => {
    (self.evaluate)(mask_set(self.size, mask))
  })
}