///|
fn enumeration_count(
  m : Matroid,
  max_subsets : Int32,
) -> Int32 raise MatroidError {
  if m.size > 18 {
    raise InvalidInput("enumeration supports at most 18 elements")
  }
  if max_subsets < 1 || max_subsets > 262144 {
    raise InvalidInput("subset budget outside 1..262144")
  }
  let count = 1 << m.size
  if count > max_subsets {
    raise BudgetExceeded("complete subset enumeration exceeds budget")
  }
  count
}

///|
/// Complete enumeration in ascending bitmask order, or an explicit budget error.
/// The budget is charged before traversal; a partial answer is never returned.
pub fn Matroid::bases(
  self : Matroid,
  max_subsets~ : Int32 = 262144,
) -> Array[Array[Int32]] raise MatroidError {
  let count = enumeration_count(self, max_subsets)
  let r = self.full_rank()
  let result = []
  for mask = 0; mask < count; mask = mask + 1 {
    let xs = mask_set(self.size, mask)
    if xs.length() == r && (self.evaluate)(xs) == r {
      result.push(xs)
    }
  }
  result
}

///|
pub fn Matroid::circuits(
  self : Matroid,
  max_subsets~ : Int32 = 262144,
) -> Array[Array[Int32]] raise MatroidError {
  let count = enumeration_count(self, max_subsets)
  let result = []
  for mask = 1; mask < count; mask = mask + 1 {
    let xs = mask_set(self.size, mask)
    if self.is_circuit(xs) {
      result.push(xs)
    }
  }
  result
}

///|
pub fn Matroid::flats(
  self : Matroid,
  max_subsets~ : Int32 = 262144,
) -> Array[Array[Int32]] raise MatroidError {
  let count = enumeration_count(self, max_subsets)
  let result = []
  for mask = 0; mask < count; mask = mask + 1 {
    let xs = mask_set(self.size, mask)
    if self.is_flat(xs) {
      result.push(xs)
    }
  }
  result
}

///|
/// Entry [cardinality][rank] counts all subsets with those invariants.
pub fn Matroid::rank_distribution(
  self : Matroid,
  max_subsets~ : Int32 = 262144,
) -> Array[Array[Int64]] raise MatroidError {
  let count = enumeration_count(self, max_subsets)
  let result = Array::makei(self.size + 1, _ => {
    Array::make(self.full_rank() + 1, 0L)
  })
  for mask = 0; mask < count; mask = mask + 1 {
    let xs = mask_set(self.size, mask)
    let r = (self.evaluate)(xs)
    result[xs.length()][r] = result[xs.length()][r] + 1L
  }
  result
}