///|
pub fn Graph::topology(self : Graph) -> Value raise {
let remaining : Map[String, Int32] = Map([])
let ids = self.nodes.keys().collect()
ids.sort()
for id in ids {
remaining[id] = self.incoming[id].length()
}
let order : Array[String] = []
let layers : Array[Value] = []
let done : Map[String, Bool] = Map([])
while true {
let ready = ids.filter(fn(id) { !done.contains(id) && remaining[id] == 0 })
if ready.is_empty() {
break
}
layers.push(strings(ready))
for id in ready {
done[id] = true
order.push(id)
for index in self.outgoing[id] {
let target = str(get(self.edges[index], "to"))
remaining[target] = remaining[target] - 1
}
}
}
let blocked = ids.filter(fn(id) { !done.contains(id) })
let cycles = self
.strong_components()
.filter(fn(component) {
component.length() > 1 ||
self.outgoing[component[0]].any(fn(i) {
get(self.edges[i], "to") == String(component[0])
})
})
record([
("acyclic", Bool(blocked.is_empty())),
("order", strings(order)),
("layers", Array(layers)),
("blocked", strings(blocked)),
("cycles", Array(cycles.map(strings))),
("edge_direction", String("prerequisite to dependent")),
])
}