Skip to content
Snippets Groups Projects
Commit 46211591 authored by Etienne Kneuss's avatar Etienne Kneuss
Browse files

Proper graph library to encode callgraph and what not

parent cb76cc04
No related branches found
No related tags found
No related merge requests found
...@@ -7,106 +7,77 @@ import Definitions._ ...@@ -7,106 +7,77 @@ import Definitions._
import Expressions._ import Expressions._
import ExprOps._ import ExprOps._
class CallGraph(p: Program) { import utils.Graphs._
private var _calls = Set[(FunDef, FunDef)]() class CallGraph(p: Program) {
private var _callers = Map[FunDef, Set[FunDef]]() // if 'foo' calls 'bar': Map(bar -> Set(foo)) private def collectCallsInPats(fd: FunDef)(p: Pattern): Set[(FunDef, FunDef)] =
private var _callees = Map[FunDef, Set[FunDef]]() // if 'foo' calls 'bar': Map(foo -> Set(bar)) (p match {
case u: UnapplyPattern => Set((fd, u.unapplyFun.fd))
case _ => Set()
}) ++ p.subPatterns.flatMap(collectCallsInPats(fd))
private var _transitiveCalls = Set[(FunDef, FunDef)]() private def collectCalls(fd: FunDef)(e: Expr): Set[(FunDef, FunDef)] = e match {
private var _transitiveCallers = Map[FunDef, Set[FunDef]]() case f @ FunctionInvocation(f2, _) => Set((fd, f2.fd))
private var _transitiveCallees = Map[FunDef, Set[FunDef]]() case MatchExpr(_, cases) => cases.toSet.flatMap((mc: MatchCase) => collectCallsInPats(fd)(mc.pattern))
case _ => Set()
}
def allCalls = _calls lazy val graph: DiGraph[FunDef, SimpleEdge[FunDef]] = {
def allTransitiveCalls = _transitiveCalls var g = DiGraph[FunDef, SimpleEdge[FunDef]]()
def isRecursive(f: FunDef) = transitivelyCalls(f, f) for (fd <- p.definedFunctions; c <- collect(collectCalls(fd))(fd.fullBody)) {
g += SimpleEdge(c._1, c._2)
}
def calls(from: FunDef, to: FunDef) = _calls contains (from -> to) g
def callers(to: FunDef) = _callers.getOrElse(to, Set()) }
def callees(from: FunDef) = _callees.getOrElse(from, Set())
def transitivelyCalls(from: FunDef, to: FunDef) = _transitiveCalls contains (from -> to) lazy val allCalls = graph.E.map(e => e._1 -> e._2)
def transitiveCallers(to: FunDef) = _transitiveCallers.getOrElse(to, Set())
def transitiveCallees(from: FunDef) = _transitiveCallees.getOrElse(from, Set())
// multi-source/dest def isRecursive(f: FunDef) = {
def callees(from: Set[FunDef]): Set[FunDef] = from.flatMap(callees) graph.transitiveSucc(f) contains f
def callers(to: Set[FunDef]): Set[FunDef] = to.flatMap(callers) }
def transitiveCallees(from: Set[FunDef]): Set[FunDef] = from.flatMap(transitiveCallees)
def transitiveCallers(to: Set[FunDef]): Set[FunDef] = to.flatMap(transitiveCallers)
lazy val stronglyConnectedComponents: Seq[Set[FunDef]] = { def calls(from: FunDef, to: FunDef) = {
def rec(funs: Set[FunDef]): Seq[Set[FunDef]] = { graph.E contains SimpleEdge(from, to)
if (funs.isEmpty) Seq()
else {
val h = funs.head
val component = transitiveCallees(h).filter{ transitivelyCalls(_, h) } + h
component +: rec(funs -- component)
}
}
rec(p.definedFunctions.toSet)
} }
def stronglyConnectedComponent(fd: FunDef) =
stronglyConnectedComponents.find{ _.contains(fd) }.getOrElse(Set(fd))
private def init() { def callers(to: FunDef): Set[FunDef] = {
_calls = Set() graph.pred(to)
_callers = Map() }
_callees = Map()
def callers(tos: Set[FunDef]): Set[FunDef] = {
tos.flatMap(callers)
}
// Collect all calls def callees(from: FunDef): Set[FunDef] = {
p.definedFunctions.foreach(scanForCalls) graph.succ(from)
}
_transitiveCalls = _calls def callees(froms: Set[FunDef]): Set[FunDef] = {
_transitiveCallers = _callers froms.flatMap(callees)
_transitiveCallees = _callees }
// Transitive calls def transitiveCallers(to: FunDef): Set[FunDef] = {
transitiveClosure() graph.transitivePred(to)
} }
private def collectCallsInPats(fd: FunDef)(p: Pattern): Set[(FunDef, FunDef)] = def transitiveCallers(tos: Set[FunDef]): Set[FunDef] = {
(p match { tos.flatMap(transitiveCallers)
case u: UnapplyPattern => Set((fd, u.unapplyFun.fd)) }
case _ => Set()
}) ++ p.subPatterns.flatMap(collectCallsInPats(fd))
private def collectCalls(fd: FunDef)(e: Expr): Set[(FunDef, FunDef)] = e match { def transitiveCallees(from: FunDef): Set[FunDef] = {
case f @ FunctionInvocation(f2, _) => Set((fd, f2.fd)) graph.transitiveSucc(from)
case MatchExpr(_, cases) => cases.toSet.flatMap((mc: MatchCase) => collectCallsInPats(fd)(mc.pattern))
case _ => Set()
} }
private def scanForCalls(fd: FunDef) { def transitiveCallees(froms: Set[FunDef]): Set[FunDef] = {
for( (from, to) <- collect(collectCalls(fd))(fd.fullBody) ) { froms.flatMap(transitiveCallees)
_calls += (from -> to)
_callees += (from -> (_callees.getOrElse(from, Set()) + to))
_callers += (to -> (_callers.getOrElse(to, Set()) + from))
}
} }
private def transitiveClosure() { def transitivelyCalls(from: FunDef, to: FunDef): Boolean = {
var changed = true graph.transitiveSucc(from) contains to
while(changed) {
val newCalls = _transitiveCalls.flatMap {
case (from, to) => _transitiveCallees.getOrElse(to, Set()).map((from, _))
} -- _transitiveCalls
if (newCalls.nonEmpty) {
for ((from, to) <- newCalls) {
_transitiveCalls += (from -> to)
_transitiveCallees += (from -> (_transitiveCallees.getOrElse(from, Set()) + to))
_transitiveCallers += (to -> (_transitiveCallers.getOrElse(to, Set()) + from))
}
} else {
changed =false
}
}
} }
init() lazy val stronglyConnectedComponents = graph.stronglyConnectedComponents.N
} }
...@@ -4,212 +4,228 @@ package leon ...@@ -4,212 +4,228 @@ package leon
package utils package utils
object Graphs { object Graphs {
abstract class VertexAbs extends Serializable { trait EdgeLike[Node] {
val name: String def _1: Node
def _2: Node
override def toString = name
} }
abstract class EdgeAbs[V <: VertexAbs] extends Serializable { case class SimpleEdge[Node](_1: Node, _2: Node) extends EdgeLike[Node]
val v1: V case class LabeledEdge[Label, Node](_1: Node, l: Label, _2: Node) extends EdgeLike[Node]
val v2: V
override def toString = v1 + "->" + v2
}
case class SimpleEdge[V <: VertexAbs](v1: V, v2: V) extends EdgeAbs[V]
abstract class LabeledEdgeAbs[T, V <: VertexAbs] extends EdgeAbs[V] {
val label: T
}
case class SimpleLabeledEdge[T, V <: VertexAbs](v1: V, label: T, v2: V) extends LabeledEdgeAbs[T, V]
trait DirectedGraph[V <: VertexAbs, E <: EdgeAbs[V], G <: DirectedGraph[V,E,G]] extends Serializable {
type Vertex = V
type Edge = E
type This = G
trait DiGraphLike[Node, Edge <: EdgeLike[Node], G <: DiGraphLike[Node, Edge, G]] {
// The vertices // The vertices
def V: Set[Vertex] def N: Set[Node]
// The edges // The edges
def E: Set[Edge] def E: Set[Edge]
// Returns the set of incoming edges for a given vertex // Returns the set of incoming edges for a given vertex
def inEdges(v: Vertex) = E.filter(_.v2 == v) def inEdges(n: Node) = E.filter(_._2 == n)
// Returns the set of outgoing edges for a given vertex // Returns the set of outgoing edges for a given vertex
def outEdges(v: Vertex) = E.filter(_.v1 == v) def outEdges(n: Node) = E.filter(_._1 == n)
// Returns the set of edges between two vertices // Returns the set of edges between two vertices
def edgesBetween(from: Vertex, to: Vertex) = { def edgesBetween(from: Node, to: Node) = {
E.filter(e => e.v1 == from && e.v2 == to) E.filter(e => e._1 == from && e._2 == to)
} }
/**
* Basic Graph Operations:
*/
// Adds a new vertex // Adds a new vertex
def + (v: Vertex): This def + (n: Node): G
// Adds new vertices // Adds new vertices
def ++ (vs: Traversable[Vertex]): This def ++ (ns: Traversable[Node]): G
// Adds a new edge // Adds a new edge
def + (e: Edge): This def + (e: Edge): G
// Removes a vertex from the graph // Removes a vertex from the graph
def - (from: Vertex): This def - (from: Node): G
// Removes a number of vertices from the graph // Removes a number of vertices from the graph
def -- (from: Traversable[Vertex]): This def -- (from: Traversable[Node]): G
// Removes an edge from the graph // Removes an edge from the graph
def - (from: Edge): This def - (from: Edge): G
/**
* Advanced Graph Operations:
*/
// Merges two graphs
def union(that: This): This
// Return the strongly connected components, sorted topologically
def stronglyConnectedComponents: Seq[Set[Vertex]]
// Topological sorting
def topSort: Option[Seq[Vertex]]
// All nodes leading to v
def transitivePredecessors(v: Vertex): Set[Vertex]
// All nodes reachable from v
def transitiveSuccessors(v: Vertex): Set[Vertex]
// Is v1 reachable from v2
def isReachable(v1: Vertex, v2: Vertex): Boolean
} }
case class DirectedGraphImp[Vertex <: VertexAbs, Edge <: EdgeAbs[Vertex]]( case class DiGraph[Node, Edge <: EdgeLike[Node]](N: Set[Node] = Set[Node](), E: Set[Edge] = Set[Edge]()) extends DiGraphLike[Node, Edge, DiGraph[Node, Edge]] with DiGraphOps[Node, Edge, DiGraph[Node, Edge]]{
vertices: Set[Vertex], def +(n: Node) = copy(N=N+n)
edges: Set[Edge], def ++(ns: Traversable[Node]) = copy(N=N++ns)
ins: Map[VertexAbs, Set[Edge]], def +(e: Edge) = (this+e._1+e._2).copy(E = E + e)
outs: Map[VertexAbs, Set[Edge]]
) extends DirectedGraph[Vertex, Edge, DirectedGraphImp[Vertex, Edge]] { def -(n: Node) = copy(N = N-n, E = E.filterNot(e => e._1 == n || e._2 == n))
def --(ns: Traversable[Node]) = {
override def equals(o: Any): Boolean = o match { val toRemove = ns.toSet
case other: DirectedGraphImp[_, _] => copy(N = N--ns, E = E.filterNot(e => toRemove.contains(e._1) || toRemove.contains(e._2)))
this.vertices == other.vertices &&
this.edges == other.edges &&
(this.ins.keySet ++ other.ins.keySet).forall (k => this.ins(k) == other.ins(k)) &&
(this.outs.keySet ++ other.outs.keySet).forall(k => this.outs(k) == other.outs(k))
case _ => false
} }
def this (vertices: Set[Vertex], edges: Set[Edge]) = def -(e: Edge) = copy(E = E-e)
this(vertices, }
edges,
edges.groupBy(_.v2: VertexAbs).withDefaultValue(Set()),
edges.groupBy(_.v1: VertexAbs).withDefaultValue(Set())) trait DiGraphOps[Node, Edge <: EdgeLike[Node], G <: DiGraphLike[Node, Edge, G]] {
this: G =>
def this() = this(Set(), Set())
def sources: Set[Node] = {
val V = vertices N -- E.map(_._2)
val E = edges
def + (v: Vertex) = copy(
vertices = vertices+v
)
override def inEdges(v: Vertex) = ins(v)
override def outEdges(v: Vertex) = outs(v)
def ++ (vs: Traversable[Vertex]) = copy(
vertices = vertices++vs
)
def -- (vs: Traversable[Vertex]) = copy(
vertices = vertices--vs,
edges = edges -- vs.flatMap(outs) -- vs.flatMap(ins),
ins = ((ins -- vs) map { case (vm, edges) => vm -> (edges -- vs.flatMap(outs)) }).withDefaultValue(Set()) ,
outs = ((outs -- vs) map { case (vm, edges) => vm -> (edges -- vs.flatMap(ins)) }).withDefaultValue(Set())
)
def + (e: Edge) = copy(
vertices = vertices + e.v1 + e.v2,
edges = edges + e,
ins = ins + (e.v2 -> (ins(e.v2) + e)),
outs = outs + (e.v1 -> (outs(e.v1) + e))
)
def - (v: Vertex) = copy(
vertices = vertices-v,
edges = edges -- outs(v) -- ins(v),
ins = ((ins - v) map { case (vm, edges) => vm -> (edges -- outs(v)) }).withDefaultValue(Set()) ,
outs = ((outs - v) map { case (vm, edges) => vm -> (edges -- ins(v)) }).withDefaultValue(Set())
)
def - (e: Edge) = copy(
vertices = vertices,
edges = edges-e,
ins = ins + (e.v2 -> (ins(e.v2) - e)),
outs = outs + (e.v1 -> (outs(e.v1) - e))
)
def union(that: This): This = copy(
vertices = this.V ++ that.V,
edges = this.E ++ that.E,
ins = ((this.ins.keySet ++ that.ins.keySet) map { k => k -> (this.ins(k) ++ that.ins(k)) }).toMap.withDefaultValue(Set()),
outs = ((this.outs.keySet ++ that.outs.keySet) map { k => k -> (this.outs(k) ++ that.outs(k)) }).toMap.withDefaultValue(Set())
)
def stronglyConnectedComponents: Seq[Set[Vertex]] = ???
def topSort = {
val sccs = stronglyConnectedComponents
if (sccs.forall(_.size == 1)) {
Some(sccs.flatten)
} else {
None
}
} }
def transitivePredecessors(v: Vertex): Set[Vertex] = { def sinks: Set[Node] = {
var seen = Set[Vertex]() N -- E.map(_._1)
def rec(v: Vertex): Set[Vertex] = { }
if (seen(v)) {
Set() def stronglyConnectedComponents: DiGraph[Set[Node], SimpleEdge[Set[Node]]] = {
} else { // Tarjan's algorithm
seen += v var index = 0
val ins = inEdges(v).map(_.v1) var stack = List[Node]()
ins ++ ins.flatMap(rec)
var indexes = Map[Node, Int]()
var lowlinks = Map[Node, Int]()
var onStack = Set[Node]()
var nodesToScc = Map[Node, Set[Node]]()
var res = DiGraph[Set[Node], SimpleEdge[Set[Node]]]()
def strongConnect(n: Node): Unit = {
indexes += n -> index
lowlinks += n -> index
index += 1
stack = n :: stack
onStack += n
for (m <- succ(n)) {
if (!(indexes contains m)) {
strongConnect(m)
lowlinks += n -> (lowlinks(n) min lowlinks(m))
} else if (onStack(m)) {
lowlinks += n -> (lowlinks(n) min indexes(m))
}
}
if (lowlinks(n) == indexes(n)) {
val i = stack.indexOf(n)+1
val ns = stack.take(i)
stack = stack.drop(i)
val scc = ns.toSet
onStack --= ns
nodesToScc ++= ns.map(n => n -> scc)
res += scc
} }
} }
rec(v)
}
def transitiveSuccessors(v: Vertex): Set[Vertex] = {
var seen = Set[Vertex]() for (n <- N if !(indexes contains n)) {
def rec(v: Vertex): Set[Vertex] = { strongConnect(n)
if (seen(v)) { }
Set()
} else { for (e <- E) {
seen += v val s1 = nodesToScc(e._1)
val outs = outEdges(v).map(_.v2) val s2 = nodesToScc(e._2)
outs ++ outs.flatMap(rec) if (s1 != s2) {
res += SimpleEdge(s1, s2)
} }
} }
rec(v)
res
} }
def isReachable(v1: Vertex, v2: Vertex): Boolean = { def topSort: Seq[Node] = {
var seen = Set[Vertex]() var res = List[Node]()
def rec(v: Vertex): Boolean = {
if (seen(v)) { var temp = Set[Node]()
false var perm = Set[Node]()
} else {
seen += v def visit(n: Node) {
val outs = outEdges(v).map(_.v2) if (temp(n)) {
if (outs(v2)) { throw new IllegalArgumentException("Graph is not a DAG")
true } else if (!perm(n)) {
} else { temp += n
outs.exists(rec) for (n2 <- succ(n)) {
visit(n2)
} }
perm += n
temp -= n
res ::= n
} }
} }
rec(v1)
for (n <- N if !temp(n) && !perm(n)) {
visit(n)
}
res
}
def depthFirstSearch(from: Node)(f: Node => Unit): Unit = {
var visited = Set[Node]()
val stack = new collection.mutable.Stack[Node]()
stack.push(from)
while(stack.nonEmpty) {
val n = stack.pop
visited += n
f(n)
for (n2 <- succ(n) if !visited(n2)) {
stack.push(n2)
}
}
}
def fold[T](from: Node)(
follow: Node => Traversable[Node],
map: Node => T,
compose: List[T] => T): T = {
var visited = Set[Node]()
def visit(n: Node): T = {
visited += n
val toFollow = follow(n).filterNot(visited)
visited ++= toFollow
compose(map(n) :: toFollow.toList.map(visit))
}
compose(follow(from).toList.map(visit))
}
def succ(from: Node): Set[Node] = {
outEdges(from).map(_._2)
}
def pred(from: Node): Set[Node] = {
inEdges(from).map(_._1)
}
def transitiveSucc(from: Node): Set[Node] = {
fold[Set[Node]](from)(
succ,
Set(_),
_.toSet.flatten
)
} }
override def toString = "DGraph[V: "+vertices+" | E:"+edges+"]" def transitivePred(from: Node): Set[Node] = {
fold[Set[Node]](from)(
pred,
Set(_),
_.toSet.flatten
)
}
def breadthFirstSearch(from: Node)(f: Node => Unit): Unit = {
var visited = Set[Node]()
val queue = new collection.mutable.Queue[Node]()
queue += from
while(queue.nonEmpty) {
val n = queue.dequeue
visited += n
f(n)
for (n2 <- succ(n) if !visited(n2)) {
queue += n2
}
}
}
} }
} }
...@@ -12,7 +12,7 @@ abstract class PureScalaVerificationSuite extends VerificationSuite { ...@@ -12,7 +12,7 @@ abstract class PureScalaVerificationSuite extends VerificationSuite {
val testDir = "regression/verification/purescala/" val testDir = "regression/verification/purescala/"
val isZ3Available = try { val isZ3Available = try {
Z3Interpreter.buildDefault.free() new Z3Interpreter("z3", Array("-in", "-smt2"))
true true
} catch { } catch {
case e: java.io.IOException => case e: java.io.IOException =>
...@@ -20,7 +20,7 @@ abstract class PureScalaVerificationSuite extends VerificationSuite { ...@@ -20,7 +20,7 @@ abstract class PureScalaVerificationSuite extends VerificationSuite {
} }
val isCVC4Available = try { val isCVC4Available = try {
CVC4Interpreter.buildDefault.free() new CVC4Interpreter("cvc4", Array("-q", "--lang", "smt2.5"))
true true
} catch { } catch {
case e: java.io.IOException => case e: java.io.IOException =>
......
/* Copyright 2009-2015 EPFL, Lausanne */
package leon.unit.utils
import leon.test._
import leon.purescala.Common._
import leon.utils.Graphs._
class GraphsSuite extends LeonTestSuite {
abstract class Node
case object A extends Node
case object B extends Node
case object C extends Node
case object D extends Node
case object E extends Node
case object F extends Node
case object G extends Node
case object H extends Node
import scala.language.implicitConversions
implicit def tToEdge(t: (Node, Node)): SimpleEdge[Node] = {
SimpleEdge(t._1, t._2)
}
def g1 = {
var g = new DiGraph[Node, SimpleEdge[Node]]()
g ++= Set(A, B, C, D)
g += A -> B
g += B -> A
g += B -> C
g += C -> D
g += D -> C
// A D
// ↕ ↕
// B → C
g
}
def g2 = {
var g = new DiGraph[Node, SimpleEdge[Node]]()
g ++= Set(A, B, C, D)
g += A -> B
g += B -> B
g += B -> C
g += C -> D
// A → B → C → D
// ↻
g
}
def g3 = {
var g = new DiGraph[Node, SimpleEdge[Node]]()
g ++= Set(A, B, C, D, E, F, G, H)
g += A -> B
g += B -> C
g += C -> D
g += E -> A
g += A -> F
g += B -> F
g += F -> E
g += F -> G
g += C -> G
g += G -> C
g += H -> G
// A → B → C → D
// ↑↘ ↓ ↕
// E ← F → G ← H
g
}
def g4 = {
var g = new DiGraph[Node, SimpleEdge[Node]]()
g ++= Set(A, B, C, D, E, F, G, H)
g += A -> B
g += B -> C
g += C -> D
g += A -> F
g += B -> F
g += F -> E
g += F -> G
g += C -> G
g += H -> G
// A → B → C → D
// ↘ ↓ ↓
// E ← F → G ← H
g
}
def g5 = {
var g = new DiGraph[Node, SimpleEdge[Node]]()
g ++= Set(A, B, C, D, E, F, G, H)
g += A -> B
g += B -> C
g += A -> D
g += D -> E
// A → B → C F G H
// ↘
// D → E
g
}
test("Graphs basic 1") { ctx =>
val g = g1
assert(g.N.size === 4)
assert(g.E.size === 5)
assert(g.outEdges(A) === Set(SimpleEdge(A, B)))
assert(g.outEdges(B) === Set(SimpleEdge(B, A), SimpleEdge(B, C)))
assert(g.inEdges(B) === Set(SimpleEdge(A, B)))
assert(g.inEdges(C) === Set(SimpleEdge(B, C), SimpleEdge(D, C)))
assert(g.edgesBetween(A, B).size === 1)
}
test("Graphs sinks/sources 1") { ctx =>
val g = g1
assert(g.sources == Set())
assert(g.sinks == Set())
}
test("Graphs sinks/sources 2") { ctx =>
val g = g2
assert(g.N.size === 4)
assert(g.E.size === 4)
assert(g.sources == Set(A))
assert(g.sinks == Set(D))
}
test("Graphs SCC 1") { ctx =>
val g = g1
val gs = g.stronglyConnectedComponents
assert(gs.N.size === 2)
assert(gs.E.size === 1)
}
test("Graphs SCC 2") { ctx =>
val g = g2
val gs = g.stronglyConnectedComponents
assert(gs.N.size === 4)
assert(gs.E.size === 3)
}
test("Graphs SCC 3") { ctx =>
val g = g3
val gs = g.stronglyConnectedComponents
assert(gs.N === Set(Set(A, B, E, F), Set(C, G), Set(D), Set(H)))
}
def assertBefore[T](s: Seq[T])(n1: T, n2: T) {
assert(s.indexOf(n1) < s.indexOf(n2), s"Node '$n1' should be before '$n2'");
}
test("Graphs top-sort 1") { ctx =>
val g = g4
val seq = g.topSort
val before = assertBefore(seq)_
before(A, B)
before(B, F)
before(A, C)
}
test("Graphs top-sort 2 (cyclic graph fails)") { ctx =>
val g = g1
intercept[IllegalArgumentException] {
g.topSort
}
}
test("Graphs top-sort 3 (SCC is acyclic)") { ctx =>
val g = g3
val gs = g.stronglyConnectedComponents
val c1: Set[Node] = Set(A, B, E, F)
val c2: Set[Node] = Set(C, G)
val c3: Set[Node] = Set(D)
val c4: Set[Node] = Set(H)
val ns = gs.topSort
val before = assertBefore(ns)_
before(c1, c2)
before(c2, c3)
before(c4, c2)
}
test("Graphs DFS") { ctx =>
val g = g5
var visited = List[Node]()
g.depthFirstSearch(A) { n =>
visited ::= n
}
visited = visited.reverse
def isBefore(a: Node, b: Node) = visited.indexOf(a) < visited.indexOf(b)
assert(!isBefore(B, D) || isBefore(C, E))
assert(!isBefore(D, B) || isBefore(E, C))
}
test("Graphs BFS") { ctx =>
val g = g5
var visited = List[Node]()
g.breadthFirstSearch(A) { n =>
visited ::= n
}
visited = visited.reverse
def isBefore(a: Node, b: Node) = visited.indexOf(a) < visited.indexOf(b)
assert(isBefore(B, E))
assert(isBefore(D, C))
}
test("Graphs pred/succ 1") { ctx =>
val g = g2
assert(g.succ(B) == Set(B, C))
assert(g.pred(B) == Set(B, A))
assert(g.transitiveSucc(B) == Set(B, C, D))
assert(g.transitivePred(B) == Set(A, B))
assert(g.transitivePred(C) == Set(A, B))
}
}
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment