import scala.language.higherKinds
import scala.reflect.ClassTag
import scala.collection.mutable.Builder
import scalax.collection.GraphEdge._
import scalax.collection.GraphPredef._
import scalax.collection.constrained._
import scalax.collection.constrained.generic.GraphConstrainedCompanion
import scalax.collection.constrained.config.ConstrainedConfig
import scalax.collection.constrained.{Graph => CGraph, GraphLike => CGraphLike}
import scalax.collection.constrained.constraints.Acyclic
import scalax.collection.constrained.Graph
import scalax.collection.constrained.CompanionAlias
import scalax.collection.constrained.constraints.Acyclic
class CycleException(msg: String) extends IllegalArgumentException(msg)
/**
* A strongly typed DAG that throws an exception on illegal edge addition.
*/
final class TypedDAG
// Getting the types right here is black magic
[ N
, E[X] <: DiEdge[X]
, +G[NN,EE[Y]<:EdgeLikeIn[Y]] <: CGraph[NN,EE] with CGraphLike[NN,EE,G] ]
(graphCompanion: GraphConstrainedCompanion[G])
(implicit edgeT: ClassTag[E[N]]) {
protected object AcyclicConstrainedCompanion extends ConstraintCompanion[Constraint] {
def apply[N0, E0[X] <: EdgeLikeIn[X]] (self: Graph[N0,E0]): Constraint[N0,E0] =
new Acyclic(self.asInstanceOf[G[N,E]]){
override def onAdditionRefused( refusedNodes: Traversable[N],
refusedEdges: Traversable[E[N]],
graph: Graph[N,E]) =
{ throw new CycleException(
"Addition refused: " +
"nodes = " + refusedNodes.toString.take(64) + "... , " +
"edges = " + refusedEdges.toString.take(64) + "..."
)
}
}.asInstanceOf[Constraint[N0,E0]]
}
protected implicit val config: Config = AcyclicConstrainedCompanion.withStringPrefix("DAG")
object DAG {
def empty: G[N,E] = graphCompanion.empty
def newBuilder: Builder[Param[N,E], G[N,E]] = graphCompanion.newBuilder
def apply(elems: Param[N,E]*): G[N,E] = (newBuilder ++= elems).result
}
}
object test_1 {
val typedDAG = new TypedDAG[Int,DiEdge,Graph](Graph)
import typedDAG.DAG
val ga = DAG(1 ~> 2, 1 ~> 3, 2 ~> 4, 3 ~> 4)
println(ga)
println(ga.get(1).pathTo(ga.get(4)))
// This works!
// DAG(1 ~> 2, 1 ~> 3, 2 ~> 4, 3 ~> 4)
// Some(Path(1, 1~>2, 2, 2~>4, 4))
def foo(g: DAG[Int]): Unit = ()
foo(DAG(1 ~> 2))
// Perfect!
//val gb = DAG(1.0 ~> 2.0) // type mismatch; found: Double(2.0) required: Int
//val gc = DAG(1 ~ 2) // type mismatch; found: UnDiEdge[Int] required: Param[Int,DiEdge]
// Throws a CycleException, excellent!
val gd = DAG(1 ~> 2, 2 ~> 1)
}
object test_2 {
import scalax.collection.edge.LDiEdge
val typedDAG = new TypedDAG[Int,LDiEdge,Graph](Graph)
import typedDAG.DAG
// Nooooooo!
// DAG.scala:79: type arguments [Int,scalax.collection.edge.LDiEdge,scalax.collection.constrained.Graph] do not conform to class TypedDAG's type parameter bounds [N,E[X] <: scalax.collection.GraphEdge.DiEdge[X],+G[NN, EE[Y] <: scalax.collection.GraphPredef.EdgeLikeIn[Y]] <: scalax.collection.constrained.Graph[NN,EE] with scalax.collection.constrained.GraphLike[NN,EE,G]]
// val typedDAG = new TypedDAG[Int,LDiEdge,Graph](Graph)
// ^
// /home/samey/Local/pingov2/sbtfiddle/src/main/scala/fiddle/graphs/GoDAG.scala:79: type arguments [Int,scalax.collection.edge.LDiEdge,scalax.collection.constrained.Graph] do not conform to class TypedDAG's type parameter bounds [N,E[X] <: scalax.collection.GraphEdge.DiEdge[X],+G[NN, EE[Y] <: scalax.collection.GraphPredef.EdgeLikeIn[Y]] <: scalax.collection.constrained.Graph[NN,EE] with scalax.collection.constrained.GraphLike[NN,EE,G]]
// val typedDAG = new TypedDAG[Int,LDiEdge,Graph](Graph)
// ^
}