///|
/// Closer indexes.
///
/// They map closing delimiters to the position where they
/// start. Shortcuts forward searches in inline parsing. See
/// `inline_struct`
priv enum Closer {
  /// Run length of backticks.
  Backticks(Int32)
  RightBrack
  RightParen
  MathSpanMarks(Int32)
} derive(Eq, Hash, Debug)

///|
type Positions = Array[Int32]

///|
priv struct CloserIndex(Map[Closer, Positions]) derive(Debug)

///|
test {
  // Prevent warning about unused Debug.
  (fn(c : CloserIndex) { @debug.to_string(c) }) |> ignore()
}

///|
fn CloserIndex::new() -> CloserIndex {
  Map([])
}

///|
fn CloserIndex::add(self : CloserIndex, key : Closer, pos : Int32) -> Unit {
  (match self.0.get(key) {
    Some(ps) => ps
    None => {
      let ps = []
      self.0[key] = ps
      ps
    }
  }).push(pos)
}

///|
fn CloserIndex::pos(self : CloserIndex, key : Closer, after~ : Int32) -> Int32? {
  guard self.0.get(key) is Some(positions) else { return None }
  let len = positions.length()
  let first = for lo = 0, hi = len {
    if lo >= hi {
      break lo
    }
    let mid = (lo + hi) / 2
    if positions[mid] <= after {
      continue mid + 1, hi
    }
    continue lo, mid
  }
  if first < len {
    Some(positions[first])
  } else {
    None
  }
}

///|
fn CloserIndex::exists(
  self : CloserIndex,
  key : Closer,
  after~ : Int32,
) -> Bool {
  !(self.pos(key, after~) is None)
}