diff --git a/src/main/scala/leon/purescala/CallGraph.scala b/src/main/scala/leon/purescala/CallGraph.scala
index a07b807ddaad26817322878c46d575f0a4ec1306..edeaab26953ef6734b2a334e440e04dfb59d38c5 100644
--- a/src/main/scala/leon/purescala/CallGraph.scala
+++ b/src/main/scala/leon/purescala/CallGraph.scala
@@ -7,106 +7,77 @@ import Definitions._
 import Expressions._
 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 var _callees = Map[FunDef, Set[FunDef]]() // if 'foo' calls 'bar': Map(foo -> Set(bar))
+  private def collectCallsInPats(fd: FunDef)(p: Pattern): Set[(FunDef, FunDef)] =
+    (p match {
+      case u: UnapplyPattern => Set((fd, u.unapplyFun.fd))
+      case _ => Set()
+    }) ++ p.subPatterns.flatMap(collectCallsInPats(fd))
 
-  private var _transitiveCalls   = Set[(FunDef, FunDef)]()
-  private var _transitiveCallers = Map[FunDef, Set[FunDef]]()
-  private var _transitiveCallees = Map[FunDef, Set[FunDef]]()
+  private def collectCalls(fd: FunDef)(e: Expr): Set[(FunDef, FunDef)] = e match {
+    case f @ FunctionInvocation(f2, _) => Set((fd, f2.fd))
+    case MatchExpr(_, cases) => cases.toSet.flatMap((mc: MatchCase) => collectCallsInPats(fd)(mc.pattern))
+    case _ => Set()
+  }
 
-  def allCalls = _calls
-  def allTransitiveCalls = _transitiveCalls
+  lazy val graph: DiGraph[FunDef, SimpleEdge[FunDef]] = {
+    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)
-  def callers(to: FunDef)   = _callers.getOrElse(to, Set())
-  def callees(from: FunDef) = _callees.getOrElse(from, Set())
+    g
+  }
 
-  def transitivelyCalls(from: FunDef, to: FunDef) = _transitiveCalls contains (from -> to)
-  def transitiveCallers(to: FunDef)   = _transitiveCallers.getOrElse(to, Set())
-  def transitiveCallees(from: FunDef) = _transitiveCallees.getOrElse(from, Set())
+  lazy val allCalls = graph.E.map(e => e._1 -> e._2)
 
-  // multi-source/dest
-  def callees(from: Set[FunDef]): Set[FunDef] = from.flatMap(callees)
-  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)
+  def isRecursive(f: FunDef) = {
+    graph.transitiveSucc(f) contains f
+  }
 
-  lazy val stronglyConnectedComponents: Seq[Set[FunDef]] = {
-    def rec(funs: Set[FunDef]): Seq[Set[FunDef]] = {
-      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 calls(from: FunDef, to: FunDef) = {
+    graph.E contains SimpleEdge(from, to)
   }
-  
-  def stronglyConnectedComponent(fd: FunDef) = 
-    stronglyConnectedComponents.find{ _.contains(fd) }.getOrElse(Set(fd))
 
-  private def init() {
-    _calls   = Set()
-    _callers = Map()
-    _callees = Map()
+  def callers(to: FunDef): Set[FunDef] = {
+    graph.pred(to)
+  }
 
+  def callers(tos: Set[FunDef]): Set[FunDef] = {
+    tos.flatMap(callers)
+  }
 
-    // Collect all calls
-    p.definedFunctions.foreach(scanForCalls)
+  def callees(from: FunDef): Set[FunDef] = {
+    graph.succ(from)
+  }
 
-    _transitiveCalls   = _calls
-    _transitiveCallers = _callers
-    _transitiveCallees = _callees
+  def callees(froms: Set[FunDef]): Set[FunDef] = {
+    froms.flatMap(callees)
+  }
 
-    // Transitive calls
-    transitiveClosure()
+  def transitiveCallers(to: FunDef): Set[FunDef] = {
+    graph.transitivePred(to)
   }
 
-  private def collectCallsInPats(fd: FunDef)(p: Pattern): Set[(FunDef, FunDef)] =
-    (p match {
-      case u: UnapplyPattern => Set((fd, u.unapplyFun.fd))
-      case _ => Set()
-    }) ++ p.subPatterns.flatMap(collectCallsInPats(fd))
+  def transitiveCallers(tos: Set[FunDef]): Set[FunDef] = {
+    tos.flatMap(transitiveCallers)
+  }
 
-  private def collectCalls(fd: FunDef)(e: Expr): Set[(FunDef, FunDef)] = e match {
-    case f @ FunctionInvocation(f2, _) => Set((fd, f2.fd))
-    case MatchExpr(_, cases) => cases.toSet.flatMap((mc: MatchCase) => collectCallsInPats(fd)(mc.pattern))
-    case _ => Set()
+  def transitiveCallees(from: FunDef): Set[FunDef] = {
+    graph.transitiveSucc(from)
   }
 
-  private def scanForCalls(fd: FunDef) {
-    for( (from, to) <- collect(collectCalls(fd))(fd.fullBody) ) {
-      _calls   += (from -> to)
-      _callees += (from -> (_callees.getOrElse(from, Set()) + to))
-      _callers += (to   -> (_callers.getOrElse(to, Set()) + from))
-    }
+  def transitiveCallees(froms: Set[FunDef]): Set[FunDef] = {
+    froms.flatMap(transitiveCallees)
   }
 
-  private def transitiveClosure() {
-    var changed = true
-    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
-      }
-    }
+  def transitivelyCalls(from: FunDef, to: FunDef): Boolean = {
+    graph.transitiveSucc(from) contains to
   }
 
-  init()
+  lazy val stronglyConnectedComponents = graph.stronglyConnectedComponents.N
 }
diff --git a/src/main/scala/leon/utils/Graphs.scala b/src/main/scala/leon/utils/Graphs.scala
index b3aeef558790ce9427e376d0e0adba2851813b7b..1910c2aa25715acd325c53c9874bd1b86ec2a6f5 100644
--- a/src/main/scala/leon/utils/Graphs.scala
+++ b/src/main/scala/leon/utils/Graphs.scala
@@ -4,212 +4,228 @@ package leon
 package utils
 
 object Graphs {
-  abstract class VertexAbs extends Serializable {
-    val name: String
-
-    override def toString = name
+  trait EdgeLike[Node] {
+    def _1: Node
+    def _2: Node
   }
 
-  abstract class EdgeAbs[V <: VertexAbs] extends Serializable  {
-    val v1: V
-    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
+  case class SimpleEdge[Node](_1: Node, _2: Node) extends EdgeLike[Node]
+  case class LabeledEdge[Label, Node](_1: Node, l: Label, _2: Node) extends EdgeLike[Node]
 
+  trait DiGraphLike[Node, Edge <: EdgeLike[Node], G <: DiGraphLike[Node, Edge, G]] {
     // The vertices
-    def V: Set[Vertex]
+    def N: Set[Node]
     // The edges
     def E: Set[Edge]
+
     // 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
-    def outEdges(v: Vertex) = E.filter(_.v1 == v)
+    def outEdges(n: Node)  = E.filter(_._1 == n)
 
     // Returns the set of edges between two vertices
-    def edgesBetween(from: Vertex, to: Vertex) = {
-      E.filter(e => e.v1 == from && e.v2 == to)
+    def edgesBetween(from: Node, to: Node) = {
+      E.filter(e => e._1 == from && e._2 == to)
     }
 
-    /**
-     * Basic Graph Operations:
-     */
-
     // Adds a new vertex
-    def + (v: Vertex): This
+    def + (n: Node): G
     // Adds new vertices
-    def ++ (vs: Traversable[Vertex]): This
+    def ++ (ns: Traversable[Node]): G
     // Adds a new edge
-    def + (e: Edge): This
+    def + (e: Edge): G
     // Removes a vertex from the graph
-    def - (from: Vertex): This
+    def - (from: Node): G
     // Removes a number of vertices from the graph
-    def -- (from: Traversable[Vertex]): This
+    def -- (from: Traversable[Node]): G
     // Removes an edge from the graph
-    def - (from: Edge): This
-
-    /**
-     * 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
+    def - (from: Edge): G
   }
 
- case class DirectedGraphImp[Vertex <: VertexAbs, Edge <: EdgeAbs[Vertex]](
-    vertices: Set[Vertex],
-    edges: Set[Edge],
-    ins: Map[VertexAbs, Set[Edge]],
-    outs: Map[VertexAbs, Set[Edge]]
-  ) extends DirectedGraph[Vertex, Edge, DirectedGraphImp[Vertex, Edge]] {
-
-    override def equals(o: Any): Boolean = o match {
-      case other: DirectedGraphImp[_, _] =>
-        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
+  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]]{
+    def +(n: Node) = copy(N=N+n)
+    def ++(ns: Traversable[Node]) = copy(N=N++ns)
+    def +(e: Edge) = (this+e._1+e._2).copy(E = E + e)
+
+    def -(n: Node) = copy(N = N-n, E = E.filterNot(e => e._1 == n || e._2 == n))
+    def --(ns: Traversable[Node]) = {
+      val toRemove = ns.toSet
+      copy(N = N--ns, E = E.filterNot(e => toRemove.contains(e._1) || toRemove.contains(e._2)))
     }
 
-    def this (vertices: Set[Vertex], edges: Set[Edge]) =
-      this(vertices,
-           edges,
-           edges.groupBy(_.v2: VertexAbs).withDefaultValue(Set()),
-           edges.groupBy(_.v1: VertexAbs).withDefaultValue(Set()))
-
-    def this() = this(Set(), Set())
-
-    val V = vertices
-    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 -(e: Edge) = copy(E = E-e)
+  }
+
+
+  trait DiGraphOps[Node, Edge <: EdgeLike[Node], G <: DiGraphLike[Node, Edge, G]] {
+    this: G =>
+
+    def sources: Set[Node] = {
+      N -- E.map(_._2)
     }
 
-    def transitivePredecessors(v: Vertex): Set[Vertex] = {
-      var seen = Set[Vertex]()
-      def rec(v: Vertex): Set[Vertex] = {
-        if (seen(v)) {
-          Set()
-        } else {
-          seen += v
-          val ins = inEdges(v).map(_.v1)
-          ins ++ ins.flatMap(rec)
+    def sinks: Set[Node] = {
+      N -- E.map(_._1)
+    }
+
+    def stronglyConnectedComponents: DiGraph[Set[Node], SimpleEdge[Set[Node]]] = {
+      // Tarjan's algorithm
+      var index = 0
+      var stack = List[Node]()
+
+      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]()
-      def rec(v: Vertex): Set[Vertex] = {
-        if (seen(v)) {
-          Set()
-        } else {
-          seen += v
-          val outs = outEdges(v).map(_.v2)
-          outs ++ outs.flatMap(rec)
+
+      for (n <- N if !(indexes contains n)) {
+        strongConnect(n)
+      }
+
+      for (e <- E) {
+        val s1 = nodesToScc(e._1)
+        val s2 = nodesToScc(e._2)
+        if (s1 != s2) {
+          res += SimpleEdge(s1, s2)
         }
       }
-      rec(v)
+
+      res
     }
 
-    def isReachable(v1: Vertex, v2: Vertex): Boolean = {
-      var seen = Set[Vertex]()
-      def rec(v: Vertex): Boolean = {
-        if (seen(v)) {
-          false
-        } else {
-          seen += v
-          val outs = outEdges(v).map(_.v2)
-          if (outs(v2)) {
-            true
-          } else {
-            outs.exists(rec)
+    def topSort: Seq[Node] = {
+      var res = List[Node]()
+
+      var temp = Set[Node]()
+      var perm = Set[Node]()
+
+      def visit(n: Node) {
+        if (temp(n)) {
+          throw new IllegalArgumentException("Graph is not a DAG")
+        } else if (!perm(n)) {
+          temp += n
+          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
+        }
+      }
+    }
   }
 }
diff --git a/src/test/scala/leon/regression/verification/purescala/PureScalaVerificationSuite.scala b/src/test/scala/leon/regression/verification/purescala/PureScalaVerificationSuite.scala
index 569eb95df2f7f66a05d7a825765adb6123b4d854..93b2a2699c7894737056e5c7fa969cebdcfe255f 100644
--- a/src/test/scala/leon/regression/verification/purescala/PureScalaVerificationSuite.scala
+++ b/src/test/scala/leon/regression/verification/purescala/PureScalaVerificationSuite.scala
@@ -12,7 +12,7 @@ abstract class PureScalaVerificationSuite extends VerificationSuite {
   val testDir = "regression/verification/purescala/"
 
   val isZ3Available = try {
-    Z3Interpreter.buildDefault.free()
+    new Z3Interpreter("z3", Array("-in", "-smt2"))
     true
   } catch {
     case e: java.io.IOException =>
@@ -20,7 +20,7 @@ abstract class PureScalaVerificationSuite extends VerificationSuite {
   }
 
   val isCVC4Available = try {
-    CVC4Interpreter.buildDefault.free()
+    new CVC4Interpreter("cvc4", Array("-q", "--lang", "smt2.5"))
     true
   } catch {
     case e: java.io.IOException =>
diff --git a/src/test/scala/leon/unit/utils/GraphsSuite.scala b/src/test/scala/leon/unit/utils/GraphsSuite.scala
new file mode 100644
index 0000000000000000000000000000000000000000..984a427fafa5632aacacd2d667f5f1d0dec7a435
--- /dev/null
+++ b/src/test/scala/leon/unit/utils/GraphsSuite.scala
@@ -0,0 +1,275 @@
+/* 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))
+  }
+}