///|
/// Owned coordinate permutation: indices[new_index] is the old index.
pub struct Permutation {
  priv indices : Array[Int32]
} derive(Debug)

///|
pub fn Permutation::new(indices : Array[Int32]) -> Permutation raise CodeError {
  if indices.length() > 65536 {
    raise ResourceLimit("permutation size")
  }
  let seen = Array::make(indices.length(), false)
  for i in indices {
    if i < 0 || i >= indices.length() {
      raise InvalidIndex(-1, i)
    }
    if seen[i] {
      raise DuplicateEntry(-1, i)
    }
    seen[i] = true
  }
  { indices: indices.copy(), }
}

///|
/// Reorders values without interpreting their type, including LLRs and erasures.
pub fn[T] Permutation::apply(
  self : Permutation,
  values : Array[T],
) -> Array[T] raise CodeError {
  if values.length() != self.indices.length() {
    raise LengthMismatch(self.indices.length(), values.length())
  }
  self.indices.map(fn(i) { values[i] })
}

///|
pub fn Permutation::inverse(self : Permutation) -> Permutation {
  let indices = Array::make(self.indices.length(), 0)
  for new_index, old_index in self.indices {
    indices[old_index] = new_index
  }
  { indices, }
}

///|
/// Composition applies self first and next second.
pub fn Permutation::then(
  self : Permutation,
  next : Permutation,
) -> Permutation raise CodeError {
  { indices: next.apply(self.indices), }
}

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