theprogrammablemind
Version:
213 lines (189 loc) • 4.44 kB
JavaScript
const _ = require('lodash')
const toA = (edge) => {
if (Array.isArray(edge)) {
return edge
} else {
return [edge.child, edge.parent]
}
}
class Digraph {
// edges maybe either [child, parent] or { child, parent }
constructor (edges = []) {
// dont make a copy of edges. this is shared and that breaks stuff. TODO fix this
this._edges = edges
}
// BFS
path (from, to) {
const frontier = { [from]: [[]] }
const done = new Set()
while (Object.keys(frontier).length > 0) {
const n = Object.keys(frontier)[0]
const ps = frontier[n]
if (to == n) {
return ps[0]
}
if (done.has(n)) {
delete frontier[n]
continue
}
done.add(n)
for (const edge of this._edges) {
if (edge.child == n) {
if (!frontier[edge.parent]) {
frontier[edge.parent] = []
}
for (const path of ps) {
if (edge.parent == to) {
return [...path, edge]
}
frontier[edge.parent].push([...path, edge])
}
}
}
}
}
get length () {
return this._edges.length
}
addEdges (edges) {
for (const edge of edges) {
this.addEdge(edge)
}
}
addEdge (edge) {
edge = toA(edge)
if (this._edges.find((existing) => _.isEqual(edge, existing))) {
return false
}
this._edges.push(edge)
return true
}
get edges () {
return this._edges
}
set edges (edges) {
this._edges = edges
}
/*
set edges(edges) {
this._edges = edges.map( toA )
}
*/
acdcs (s, from, to) {
const todo = [s]
const seen = new Set([s])
const acdcs = new Set([])
while (todo.length > 0) {
const n = todo.pop()
this._edges.forEach((e) => {
e = toA(e)
if (e[from] === n) {
acdcs.add(e[to])
if (!seen.has(e[to])) {
todo.push(e[to])
seen.add(e[to])
}
}
})
}
return acdcs
}
isA (low, high) {
if (low === high) {
return true
}
return this.ancestors(low).has(high)
}
lessThan (low, high) {
if (low === high) {
return false
}
return this.ancestors(low).has(high)
}
descendants (s) {
return this.acdcs(s, 1, 0)
}
ancestors (s, { includeSelf } = {}) {
const ancestors = this.acdcs(s, 0, 1)
if (includeSelf) {
ancestors.add(s)
}
return ancestors
}
minima (nodes) {
nodes = new Set(nodes)
const nodeToDescendants = {}
for (const node of nodes) {
nodeToDescendants[node] = this.descendants(node)
}
const minima = new Set()
for (const node of nodes) {
let okay = true
for (const key of nodeToDescendants[node]) {
if (nodes.has(key)) {
if (key in nodeToDescendants && nodeToDescendants[key].has(node)) {
continue
}
okay = false
break
}
}
if (okay) {
minima.add(node)
}
}
return minima
}
lub (nodes) {
if (nodes.length === 0) {
return new Set([])
}
nodes = Array.from(nodes)
let common = this.ancestors(nodes[0], { includeSelf: true })
for (let i = 1; i < nodes.length; i++) {
const ancestors = this.ancestors(nodes[i], { includeSelf: true })
common = new Set([...common].filter(x => ancestors.has(x)))
if (common.size === 0) {
break
}
}
return this.minima(common)
}
/*
maxima (nodes) {
const maxima = new Set(nodes)
const descendants = new Set([])
nodes.forEach((node) => {
this.descendants(node).forEach((n) => descendants.add(n))
})
descendants.forEach((n) => maxima.delete(n))
return maxima
}
*/
add (child, parent) {
this._edges.push([child, parent])
}
exists (child, parent) {
return this._edges.find((edge) => edge[0] == child && edge[1] == parent)
}
addList (l) {
for (let i = 1; i < l.length; ++i) {
if (!this.exists(l[i - 1], l[i])) {
this._edges.push([l[i - 1], l[i]])
}
}
}
order (todo) {
const ordered = []
while (todo.length > 0) {
const nodes = this.minima(todo)
todo = todo.filter((e) => !nodes.has(e))
for (const node of nodes) {
ordered.push(node)
}
// ordered = ordered.concat([...nodes])
}
return ordered
}
}
module.exports = Digraph