///|
/// Host-side exact patch, not SPARQL UPDATE. All preconditions are checked on
/// the original snapshot; failure leaves the dataset and its indexes unchanged.
pub fn Dataset::apply_patch(
  self : Dataset,
  remove : Array[Quad],
  add : Array[Quad],
  limits~ : Limits = Limits::default(),
) -> Dataset raise QueryError {
  let budget = Budget::new(limits)
  if self.facts.length() > limits.max_triples ||
    remove.length() > limits.max_triples ||
    add.length() > limits.max_triples {
    raise Resource("patch input quads")
  }
  let current : Map[String, Bool] = Map([])
  for q in self.facts {
    budget.tick()
    current[q.key()] = true
  }
  let deletions : Map[String, Bool] = Map([])
  for q in remove {
    budget.tick()
    let k = q.key()
    if !current.contains(k) || deletions.contains(k) {
      raise InvalidInput("missing or repeated patch removal")
    }
    deletions[k] = true
  }
  let additions : Map[String, Bool] = Map([])
  for q in add {
    budget.tick()
    let k = q.key()
    if current.contains(k) || deletions.contains(k) || additions.contains(k) {
      raise InvalidInput("existing or repeated patch addition")
    }
    additions[k] = true
  }
  let retained = self.facts.length() - remove.length()
  if add.length() > limits.max_triples - retained {
    raise Resource("patched dataset size")
  }
  let output : Array[Quad] = []
  for q in self.facts {
    budget.tick()
    if !deletions.contains(q.key()) {
      output.push(q)
    }
  }
  for q in add {
    output.push(q)
  }
  Dataset::from_quads(output, limits~)
}