///|
fn projected_ledger(source : Ledger, selected : Array[Revision]) -> Ledger {
  let revisions : Array[Revision] = []
  for item in selected {
    revisions.push({
      key: item.key,
      valid: item.valid,
      known_at: item.known_at,
      value: item.value,
      sequence: revisions.length(),
    })
  }
  let latest_time = if revisions.is_empty() {
    None
  } else {
    Some(revisions[revisions.length() - 1].known_at)
  }
  {
    revisions,
    index: build_index(revisions),
    latest_time,
    maximum: source.maximum,
  }
}

///|
/// Materialize the ledger prefix known at or before a chosen instant. The
/// resulting ledger can be archived independently, preserving earlier views.
pub fn Ledger::as_known_at(self : Ledger, cutoff : Int64) -> Ledger {
  let selected : Array[Revision] = []
  for revision in self.revisions {
    if revision.known_at > cutoff {
      break
    }
    selected.push(revision)
  }
  projected_ledger(self, selected)
}

///|
/// Materialize only requested keys. Sequence numbers are renumbered in the
/// projection; knowledge instants and valid spans retain their original values.
pub fn Ledger::select_keys(self : Ledger, keys : Array[String]) -> Ledger {
  let selected_keys : Map[String, Bool] = Map([])
  for key in keys {
    selected_keys[key] = true
  }
  let selected : Array[Revision] = []
  for revision in self.revisions {
    if selected_keys.contains(revision.key) {
      selected.push(revision)
    }
  }
  projected_ledger(self, selected)
}

///|
/// The number of revisions about a key, independent of current visibility.
pub fn Ledger::history_count(self : Ledger, key : String) -> Int {
  match self.index.get(key) {
    Some(positions) => positions.length()
    None => 0
  }
}