///|
/// Maximal spans satisfying every typed fact condition at one knowledge cutoff.
/// Repeated keys are ANDed; an empty condition list is the whole bounds.
pub fn Ledger::matching_conditions(
self : Ledger,
conditions : Array[(String, SpanFilter)],
bounds : Span,
known_at : Int64,
max_conditions~ : Int32 = 64,
max_segments~ : Int32 = 4096,
max_input_segments~ : Int32 = 16384,
) -> Array[Span] raise TemporalError {
if max_conditions < 0 ||
max_conditions > 1024 ||
conditions.length() > max_conditions {
raise TooManyKeys(maximum=max_conditions)
}
if max_segments <= 0 {
raise TooManySegments(maximum=max_segments)
}
let grouped : Map[String, Array[SpanFilter]] = Map([])
let keys : Array[String] = []
for (key, filter) in conditions {
if key.is_empty() {
raise EmptyKey
}
match grouped.get(key) {
Some(filters) => filters.push(filter)
None => {
grouped[key] = [filter]
keys.push(key)
}
}
}
let rows = self.aligned_timeline(
keys,
bounds,
known_at,
max_keys=max_conditions,
max_segments~,
max_cells=1048576,
max_input_segments~,
)
let result : Array[Span] = []
for row in rows {
let mut matches = true
for fact in row.facts {
for filter in grouped[fact.key] {
let segment : Segment = { valid: row.valid, source: fact.source, }
if !filter.accepts(segment) {
matches = false
break
}
}
if !matches {
break
}
}
if matches {
if !result.is_empty() && result[result.length() - 1].touches(row.valid) {
let i = result.length() - 1
result[i] = { start: result[i].start, end: row.valid.end, }
} else {
result.push(row.valid)
}
}
}
result
}