///|
/// The returned ground is reindexed by the supplied ordered labels.
pub fn Matroid::relabel(
  self : Matroid,
  order : Array[Int32],
) -> Matroid raise MatroidError {
  checked_set(self.size, order) |> ignore
  if order.length() != self.size {
    raise InvalidInput("relabel requires a complete permutation")
  }
  let labels = order.copy()
  {
    size: self.size,
    model: "relabel",
    evaluate: xs => (self.evaluate)(xs.map(e => labels[e])),
  }
}

///|
/// Retained labels are ascending; new index i denotes retained[i].
pub fn Matroid::restrict_to(
  self : Matroid,
  retained : Array[Int32],
) -> Matroid raise MatroidError {
  let labels = checked_set(self.size, retained)
  {
    size: labels.length(),
    model: "restriction",
    evaluate: xs => (self.evaluate)(xs.map(e => labels[e])),
  }
}

///|
pub fn Matroid::delete(
  self : Matroid,
  removed : Array[Int32],
) -> Matroid raise MatroidError {
  let xs = checked_set(self.size, removed)
  self.restrict_to(complement(self.size, xs))
}

///|
/// Contracts even dependent sets using r(X union C)-r(C).
pub fn Matroid::contract(
  self : Matroid,
  contracted : Array[Int32],
) -> Matroid raise MatroidError {
  let fixed = checked_set(self.size, contracted)
  let labels = complement(self.size, fixed)
  let offset = (self.evaluate)(fixed)
  {
    size: labels.length(),
    model: "contraction",
    evaluate: xs => {
      let expanded = fixed.copy()
      for e in xs {
        expanded.push(labels[e])
      }
      (self.evaluate)(expanded) - offset
    },
  }
}

///|
pub fn Matroid::dual(self : Matroid) -> Matroid {
  let full = self.full_rank()
  {
    size: self.size,
    model: "dual",
    evaluate: xs => {
      xs.length() + (self.evaluate)(complement(self.size, xs)) - full
    },
  }
}

///|
pub fn Matroid::truncate(
  self : Matroid,
  rank : Int32,
) -> Matroid raise MatroidError {
  if rank < 0 || rank > self.full_rank() {
    raise InvalidInput("truncation rank outside 0..r(E)")
  }
  {
    size: self.size,
    model: "truncation",
    evaluate: xs => (self.evaluate)(xs).min(rank),
  }
}