All Downloads are FREE. Search and download functionalities are using the official Maven repository.

scalax.collection.GraphTraversal.scala Maven / Gradle / Ivy

The newest version!
package scalax.collection

import scala.language.{higherKinds, implicitConversions}
import scala.collection.{AbstractIterable, AbstractTraversable}
import scala.collection.mutable.{ArrayBuffer, Builder}
import scala.math.min

import GraphPredef.{EdgeLikeIn, OuterElem, OuterEdge, OutParam, InnerNodeParam, InnerEdgeParam}
import mutable.EqHashMap

/** Graph-related functionality such as traversals, path finding, cycle detection etc.
 *  All algorithms including breadth-first, depth-first, white-gray-black search and
 *  Dijkstra's algorithm are tail recursive.
 *  
 * Before starting a traversal a `Traverser` such as [[scalax.collection.GraphTraversal#InnerNodeTraverser]]
 * is instantiated explicitly or implicitly. It holds settings like `maxDepth`, `subgraph` or `ordering`
 * providing a fine-grained control of the traversal. `Traverser`s also extend
 * `scala.collection.Traversable` meaning that you can process the visited nodes and edges
 * in a functional way.  
 *
 * @see [[http://www.scala-graph.org/guides/core-traversing]] 
 *
 * @define INTOACC taking optional properties like
 *         subgraph restriction, ordering or maximum depth into account.
 * @define EXTNODEVISITOR Alternatively, an instance of `ExtendedNodeVisitor`
 *         may be passed to obtain additional state information such as the current
 *         depth. The concrete type of the last argument, the informer
 *         depends on the underlying implementation so you need to match against it.
 * @define ORD If a `NodeOrdering` or `EdgeOrdering` different from `NoOrdering` is supplied
 *         neighbor nodes will visited during the traversal according to this ordering.
 * @define MAXWEIGHT An optional maximum weight that limits the scope of the traversal or search.
 *         If defined and the sum of edge weights between the root of the traversal and a node
 *         exceeds the given maximum, that node will no more be visited.
 * @define CONSIDERING considering all traversal properties passed to the traverser
 *         factory method like [[scalax.collection.GraphTraversal#innerNodeTraverser]]
  *         or altered by any `with*` method.
 * @define OPTVISITOR An optional function that is applied for its side-effect to
 *         every element visited during graph traversal.
 * @define DUETOSUBG due to withSubgraph settings this path was out of scope.
 * @define EXTENDSTYPE which extends `scala.collection.Traversable` with elements of type
 * @define SETROOT and sets its `root` to this node
 * @define TOSTART To start a traversal call one of the graph traversal methods or 
 *         any appropriate method inherited from [[scala.collection.Traversable]] on this
 *         instance.
 * @define ROOT The node where subsequent graph traversals start.
 * @define PARAMETERS The properties controlling subsequent traversals.
 * @define SUBGRAPHNODES Restricts subsequent graph traversals to visit only nodes
 *         holding this predicate.      
 * @define SUBGRAPHEDGES Restricts subsequent graph traversals to walk only along edges
 *         that hold this predicate.
 * @define DOWNUPBOOLEAN where the `Boolean` parameter is `true` if the traversal takes
 *         place in downward and `false` if it takes place in upward direction.
 * @define PATHSYNTAX `::= ''node'' { ''edge'' ''node'' }`
 * @define WALKPATH A walk/path contains at least one node followed by any number of
 *         consecutive pairs of an edge and a node.
 *         The first element is the start node, the second is an edge with its source
 *         being the start node and its target being the third element etc.
 * @define SANECHECK This optional check is sane if there is reasonable doubt
 *         about the correctness of some algorithm results.
 * @define BUILDERADDS Nodes and edges may be added either alternating or node by node
 *         respectively edge by edge. Either way, the builder ensures that the added
 *         elements build a valid
 * @define BUILDERREC It is recommended using `add` instead of `+=` to track failed
 *         additions.
 * @define EDGESELECTOR Determines the edge to be selected between neighbor nodes
 *         if an edge is not supplied explicitly. This is only relevant in case of
 *         multigraphs.
 * @define SEEFLUENT See `componentTraverser` for more control by means of `FluentProperties`.
 * @define SORTVISITOR called for each inner node or inner edge visited during the sort.
 * 
 * @author Peter Empen
 */
trait GraphTraversal[N, E[X] <: EdgeLikeIn[X]] extends GraphBase[N,E] {
  thisGraph =>

  import GraphTraversal._
  import Visitor._

  /** Whether `this` graph is connected if it is undirected or
   *  weakly connected if it is directed.
   */
  def isConnected = nodes.headOption map { head =>
    head.innerNodeTraverser(
        Parameters(kind = DepthFirst, direction = AnyConnected)).size == nodes.size
  } getOrElse true
  
  /** Whether `this` graph has at least one cycle in any of its components.
   */
  @inline final def isCyclic: Boolean = findCycle().isDefined

  /** Whether `this` graph has no cycle.
   */
  @inline final def isAcyclic: Boolean = ! isCyclic

  /** Finds a cycle in `this` graph in any of its components
   *  and calls `visitor` for each inner element visited during the search.
   *  $SEEFLUENT
   */
  final def findCycle[U](implicit visitor: InnerElem => U = empty): Option[Cycle] =
    componentTraverser().findCycle(visitor)

  /** Represents a topological sort layer. */
  case class Layer protected[collection](index: Int, nodes: IndexedSeq[NodeT])
  
  /** The result of a topological sort in the layered view. */
  type Layers = Traversable[Layer]
  
  /** Topologically ordered nodes or layers of a topological order of a graph or of an isolated graph component.
   *  @tparam A one of `NodeT`, `N`
   *  @tparam T one of `A` or `(Int, Iterable[A])`
   *  @define NEWFLAVOR Creates a new flavor of this `TopologicalOrder` or `LayeredTopologicalOrder` */
  sealed abstract class AbstractTopologicalOrder[+A, +T]
      extends AbstractTraversable[T] {
    
    protected val layers: Layers
    protected def toA: NodeT => A

    def layerOrdering: NodeOrdering
    protected def ordered(nodes: IndexedSeq[NodeT]): IndexedSeq[NodeT] =
      if (layerOrdering.isDefined) nodes.sorted(layerOrdering)
      else nodes
    
    /** The number of layers of this topological order. */
    def nrOfLayers = layers.size
    
    /** $NEWFLAVOR with nodes ordered by `newOrdering` within the layers.
     *  A layer ordering is also useful to ensure a stable topological order over graph instances. */
    def withLayerOrdering(newOrdering: NodeOrdering): AbstractTopologicalOrder[A, T]
      
    /** $NEWFLAVOR that is traversable for its inner nodes zipped with their layers. */
    def toLayered: LayeredTopologicalOrder[A]
    
    override def hashCode: Int = layers.##

    override def equals(other: Any): Boolean = other match {
      case that: AbstractTopologicalOrder[_, _] => (this.layers eq that.layers) ||
                                                    this.layers == that.layers 
      case _                                    => false
    }
    
    override def stringPrefix = getClass.getSimpleName
  }
  
  /** A traversable topological order of nodes of a graph or of an isolated graph component.
   *  @tparam A one of `NodeT`, `N` */
  final class TopologicalOrder[+A] protected[collection](
        override protected val layers: Layers,
        override protected val toA: NodeT => A)
       (implicit override val layerOrdering: NodeOrdering = NodeOrdering.None)
      extends AbstractTopologicalOrder[A, A] {
    
    def foreach[U](f: A => U): Unit =
      for (layer <- layers;
           node  <- ordered(layer.nodes)) f(toA(node))
            
    def withLayerOrdering(newOrdering: NodeOrdering): TopologicalOrder[A] =
      new TopologicalOrder(layers, toA)(newOrdering)
    
    def toOuter: TopologicalOrder[N] =
      new TopologicalOrder(layers, _.value)(layerOrdering)
      
    def toLayered: LayeredTopologicalOrder[A] =
      new LayeredTopologicalOrder[A](layers, toA)(layerOrdering)
  }

  /** Layers of a topological order of a graph or of an isolated graph component.
   *  The layers of a topological sort can roughly be defined as follows:
   *      a. layer 0 contains all nodes having no predecessors,
   *      a. layer n contains those nodes that have only predecessors in anchestor layers
   *         with at least one of them contained in layer n - 1
   *  @tparam A one of `NodeT`, `N` */
  final class LayeredTopologicalOrder[+A] protected[collection](
        override protected val layers: Layers,
        override protected val toA: NodeT => A)
       (implicit override val layerOrdering: NodeOrdering = NodeOrdering.None)
      extends AbstractTopologicalOrder[A, (Int, Iterable[A])] {
    
    def foreach[U](f: Tuple2[Int, Iterable[A]] => U): Unit =
      for (layer <- layers) f(layer.index -> toIterable(layer.nodes))
           
    def withLayerOrdering(newOrdering: NodeOrdering): LayeredTopologicalOrder[A] =
      new LayeredTopologicalOrder(layers, toA)(newOrdering)
    
    def toOuter: LayeredTopologicalOrder[N] =
      new LayeredTopologicalOrder(layers, _.value)(layerOrdering)
      
    def toLayered: LayeredTopologicalOrder[A] = this
    
    // O(1) view to avoid exposure of the possibly mutable `layers`
    private def toIterable(iSeq: IndexedSeq[NodeT]): Iterable[A] = new AbstractIterable[A] {
      def iterator: Iterator[A] = new AbstractIterator[A] {
        private val it = ordered(iSeq).toIterator
        def hasNext: Boolean = it.hasNext
        def next(): A = toA(it.next)
      }
      override def stringPrefix = "Nodes"
    }
  }
  
  /** Either a `Right` containing a valid topological order or a `Left` containing a node on a cycle. */
  type CycleNodeOrTopologicalOrder = Either[NodeT, TopologicalOrder[NodeT]]
  
  /** Sorts this graph topologically.
   *  @param visitor $SORTVISITOR
   *  $SEEFLUENT */
  final def topologicalSort[U](implicit visitor: InnerElem => U = empty): CycleNodeOrTopologicalOrder =
    componentTraverser().topologicalSort(visitor)

  /** Sorts every isolated component of this graph topologically.
   *  @param visitor $SORTVISITOR
   *  $SEEFLUENT */
  final def topologicalSortByComponent[U](implicit visitor: InnerElem => U = empty): Traversable[CycleNodeOrTopologicalOrder] =
    componentTraverser().topologicalSortByComponent(visitor)

  @inline final protected def defaultPathSize: Int = min(256, nodes.size * 2)

  /** Represents a walk in this graph where `walk` $PATHSYNTAX
   * $WALKPATH
   * @define CUMWEIGHT The cumulated weight of all edges on this path/walk.    
   */
  trait Walk extends Traversable[InnerElem]
  {
    override def stringPrefix = "Walk"

    /** All nodes on this path/walk in proper order. */
    def nodes: Traversable[NodeT]
    /** All edges of this path/walk in proper order. */
    def edges: Traversable[EdgeT]

    /** $CUMWEIGHT */
    final def weight: Long = (0L /: edges)((sum, edge) => sum + edge.weight)
    /** $CUMWEIGHT
     *  @param f The weight function overriding edge weights. */
    final def weight[T: Numeric](f: EdgeT => T): T = {
      val num = implicitly[Numeric[T]] 
      import num._
     (num.zero /: edges)((sum, edge) => sum + f(edge))
    }

    /** The number of edges on this path/walk. */
    def length: Int = nodes.size - 1
    /** The number of nodes and edges on this path/walk. */
    override def size: Int = 2 * length + 1

    def startNode: NodeT
    def endNode:   NodeT
    
    def foreach[U](f: InnerElem => U): Unit = {
      f(nodes.head)
      val edges = this.edges.toIterator
      for (n <- nodes.tail;
           e = edges.next) {
        f(e)
        f(n)
      }
    }

    /** Returns whether the nodes and edges of this walk are valid with respect
     *  to this graph. $SANECHECK */
    def isValid: Boolean = {
      val isValidNode = nodeValidator.apply _
      nodes.headOption filter isValidNode exists { startNode =>
        val edges = this.edges.toIterator
        (nodes.head /: nodes.tail){ (prev: NodeT, n: NodeT) =>
          if (isValidNode(n) && edges.hasNext) {
            val e = edges.next
            if (! e.matches((x: NodeT) => x eq prev,
                            (x: NodeT) => x eq n   )) return false
            n
          } else return false
        }
        true
      }
    }

    protected trait NodeValidator {
      def apply(node: NodeT): Boolean
    }
    protected def nodeValidator: NodeValidator =
      new NodeValidator { 
        def apply(node: NodeT): Boolean = true
    }
  }
  object Walk {
    protected[GraphTraversal] trait Zero {
      this: Walk =>
      protected def single: NodeT
      val nodes = List(single)
      def edges = Nil
      def startNode = single
      def endNode = single
      override def isValid = true
    }
    
    /** A walk of zero length that is a single node. */
    def zero(node: NodeT) =
      new Walk with Zero {
        protected final def single = node
      }
  }
  
  /** A `Builder` for valid walks in this graph.
   * 
   * $BUILDERADDS walk.
   *   
   * A node addition fails if the node to be added is not a direct successor of the
   * previously added node or of the target node of the previously added edge.
   * An edge addition fails if the edge to be added is not an outgoing edge from the
   * previously added node or of the target node of the previously added edge.  
   *  
   * $BUILDERREC
   * 
   * @define ADDELEM Tries to add `elem` to the tail of the path/walk.
   * @define ADDNODE Tries to add `node` to the tail of the path/walk.
   * @define ADDEDGE Tries to add `edge` to the tail of the path/walk.
   * @define ADDSUCCESS Whether the addition was successful. 
   */  
  trait WalkBuilder
      extends Builder[InnerElem, Walk] {
    
    /** The node this walk starts at. */
    def start: NodeT
      
    /** $ADDELEM
     *  @return $ADDSUCCESS */
    @inline final def add(elem: InnerElem): Boolean = elem match {
      case n: InnerNode => this add n.asNodeT[N,E,thisGraph.type](thisGraph)
      case e: InnerEdge => this add e.asEdgeT[N,E,thisGraph.type](thisGraph)
    }
    /** $ADDNODE
     *  @return $ADDSUCCESS */
    def add(node: NodeT): Boolean
    /** $ADDEDGE
     *  @return $ADDSUCCESS */
    def add(edge: EdgeT): Boolean

    /** $ADDELEM */
    def += (elem: InnerElem): this.type = { add(elem); this }
    /** $ADDNODE */
    @inline final def += (node: NodeT): this.type = { add(node); this }
    /** $ADDEDGE */
    @inline final def += (edge: EdgeT): this.type = { add(edge); this }
  }
  
  /** Instantiates a [[WalkBuilder]] for this graph.
   * 
   * @param start The node this walk starts at.
   * @param sizeHint Expected maximum number of nodes on this walk.
   * @param edgeSelector $EDGESELECTOR 
   */  
  def newWalkBuilder(
      start: NodeT)(
      implicit sizeHint: Int = defaultPathSize,
      edgeSelector:      (NodeT, NodeT) => Option[EdgeT] = anyEdgeSelector): WalkBuilder
  
  /** Represents a path in this graph where
   * 
   * `path` $PATHSYNTAX
   * 
   * Nodes and edges on the path are distinct. $WALKPATH
   */
  trait Path extends Walk
  {
    override def stringPrefix = "Path"

    /** Returns whether the nodes and edges on this path are valid with respect
     *  to this graph. $SANECHECK  
     */
    override def isValid: Boolean = super.isValid
    
    override protected def nodeValidator: NodeValidator =
      new NodeValidator { 
        private[this] val nodeSet = new EqHashMap[NodeT,Null](nodes.size)
        def apply(node: NodeT): Boolean = nodeSet.put(node, null).isEmpty
    }
  }
  object Path extends Serializable {
    /** A path of zero length that is a single node. */
    def zero(node: NodeT) =
      new Path with Walk.Zero {
        protected final def single = node
      }
  }
  
  /** A `Builder` for valid paths in this graph.
   * 
   * $BUILDERADDS path.
   *   
   * A node addition fails if either the node to be added is already contained or
   * the node is not a direct successor of the previously added node or
   * of the target node of the previously added edge.
   * An edge addition fails if either the edge to be added is is already contained or
   * the edge is not an outgoing edge from the previously added node or
   * of the target node of the previously added edge.  
   *  
   * $BUILDERREC
   */  
  trait PathBuilder
      extends WalkBuilder
         with Builder[InnerElem, Path]
  
  /** Instantiates a [[PathBuilder]] for this graph.
   * 
   * @param start The node this path starts at.
   * @param sizeHint Expected maximum number of nodes on this path.
   * @param edgeSelector $EDGESELECTOR 
   */  
  def newPathBuilder(
      start: NodeT)(
      implicit sizeHint: Int = defaultPathSize,
      edgeSelector:      (NodeT, NodeT) => Option[EdgeT] = anyEdgeSelector): PathBuilder

  /** Represents a cycle in this graph listing the nodes and connecting edges on it
   *  with the following syntax:
   * 
   * `cycle ::= ''start-end-node'' { ''edge'' ''node'' } ''edge'' ''start-end-node''`
   * 
   * All nodes and edges on the path are distinct except the start and end nodes that
   * are equal. A cycle contains at least a start node followed by any number of
   * consecutive pairs of an edge and a node and the end node equaling to the start node.
   * The first element is the start node, the second is an edge with its tail
   * being the start node and its head being the third element etc.   
   */
  trait Cycle extends Path {
    override def stringPrefix = "Cycle"

    /** Same as `sameAs` but also comparing this cycle with any `Traversable`.
     */
    final def sameElements(that: Traversable[_]): Boolean =
      this.size == that.size && {
        val thisList = to[List]
        // thisList.indexOf(that.head) may fail due to asymmetric equality
        val idx = thisList.indexWhere(_ == that.head)
        if (idx >= 0) {
          val thisDoubled = thisList ++ thisList.tail
          val thatList = that.to[List]
          (thisDoubled startsWith (thatList        , idx)) ||
          (thisDoubled startsWith (thatList.reverse, idx))
        }
        else false
      }

    /** Semantically compares `this` cycle with `that` cycle. While `==` returns `true`
     *  only if the cycles contain the same elements in the same order, this comparison
     *  returns also `true` if the elements of `that` cycle can be shifted and optionally
     *  reversed such that their elements have the same order. For instance, given
     * 
     * `c1 = Cycle(1-2-3-1)`, `c2 = Cycle(2-3-1-2)` and `c3 = Cycle(2-1-3-2)`
     * 
     * the following expressions hold:
     * 
     * `c1 != c2`, `c1 != c3` but `c1 sameAs c2` and `c1 sameAs c3`.  
     */
    final def sameAs(that: GraphTraversal[N,E]#Cycle): Boolean =
      this == that || ( that match {
        case that: GraphTraversal[N,E]#Cycle => sameElements(that)
        case _ => false
      })

  }

  /** Whether all nodes are pairwise adjacent.
   * 
   * @return `true` if this graph is complete, `false` if this graph contains any
   * independent nodes. 
   */
  def isComplete = {
    val orderLessOne = order - 1
    nodes forall (_.diSuccessors.size == orderLessOne)
  }
  
  /** An arbitrary edge between `from` and `to` that is available most efficiently. 
   */
  @inline final def anyEdgeSelector(from: NodeT, to: NodeT): Option[EdgeT] =
    from findOutgoingTo to

  /** Stores a value and an edge weight function
   *  for use in weight-based traversals that may be defined by `withMaxWeight`. */
  class Weight(val value: Double, val edgeWeight: EdgeT => Double) {
    val ordering = Edge weightOrdering edgeWeight
  }
  object Weight {
    /** Creates a new `Weight` with the given `value` and weight function. */
    def apply[W](value: W, edgeWeight: EdgeT => W)(implicit num: Numeric[W]): Weight = {
      import num._
      new Weight(toDouble(value), edgeWeight andThen toDouble)
    }
    
    /** Creates a new `Weight` with the given `value` and the default weight function returning `edge.weight`, */
    def apply(value: Long) = new Weight(value, Edge.defaultWeight _ andThen (_.toDouble))
  }
  
  /** Template for extended node visitors.
   *  While the default node visitor of the type `NodeT => U`
   *  passes solely the inner node being visited, extended node visitors
   *  pass the following traversal state information: 
   *  1. the inner node currently visited as with a standard node visitor
   *  1. the number of nodes visited so far and 
   *  1. the current depth in terms of the underlying algorithm and 
   *  1. a reference to a specific informer that may be pattern matched
   *     to collect even further data specific to the implementation.
   */
  trait ExtendedNodeVisitor[U]
      extends (NodeT => U)
      with ((NodeT, Int, Int, => NodeInformer) => U) {
    def apply(node: NodeT) = apply(node, 0, 0, NodeInformer.empty)
  }
  object ExtendedNodeVisitor {
    /** Instantiates an extended node visitor based on 'visitor'.
     */
    def apply[N, E[X] <: EdgeLikeIn[X], U](
        visitor: (NodeT, Int, Int, => NodeInformer) => U) =
      new ExtendedNodeVisitor[U] {
        def apply(n: NodeT, cnt: Int, depth: Int, inf: => NodeInformer) =
          visitor(n, cnt, depth, inf)
      } 
  }  

  type NodeT <: TraverserInnerNode
  trait TraverserInnerNode extends super.InnerNode
  { this: NodeT =>
 
    /** Instantiates an [[InnerNodeTraverser]] $EXTENDSTYPE `NodeT` $SETROOT. $TOSTART
     *  @param parameters $PARAMETERS
     */
    @inline final def innerNodeTraverser(implicit parameters: Parameters = Parameters()) =
      thisGraph.innerNodeTraverser(this, parameters)

    /** Instantiates an [[OuterNodeTraverser]] $EXTENDSTYPE `N` $SETROOT. $TOSTART
     *  @param parameters $PARAMETERS   
     */
    @inline final def outerNodeTraverser(implicit parameters: Parameters = Parameters()) =
      thisGraph.outerNodeTraverser(this, parameters)

    /** Instantiates an [[InnerEdgeTraverser]] $EXTENDSTYPE `EdgeT` $SETROOT. $TOSTART
     *  @param parameters $PARAMETERS
     */
    @inline final def innerEdgeTraverser(implicit parameters: Parameters = Parameters()) =
      thisGraph.innerEdgeTraverser(this, parameters)

    /** Instantiates an [[OuterEdgeTraverser]] $EXTENDSTYPE `E[N]` $SETROOT. $TOSTART
     *  @param parameters $PARAMETERS   
     */
    @inline final def outerEdgeTraverser(implicit parameters: Parameters = Parameters()) =
      thisGraph.outerEdgeTraverser(this, parameters)

    /** Instantiates an [[InnerElemTraverser]] $EXTENDSTYPE `InnerElem` $SETROOT. $TOSTART
     *  @param parameters $PARAMETERS
     */
    @inline final def innerElemTraverser(implicit parameters: Parameters = Parameters()) =
      thisGraph.innerElemTraverser(this, parameters)

    /** Instantiates an [[OuterElemTraverser]] $EXTENDSTYPE `OuterElem` $SETROOT. $TOSTART
     *  @param parameters $PARAMETERS   
     */
    @inline final def outerElemTraverser(implicit parameters: Parameters = Parameters()) =
      thisGraph.outerElemTraverser(this, parameters)
      
    /** Instantiates an [[InnerNodeDownUpTraverser]] $EXTENDSTYPE `(Boolean, NodeT)` $SETROOT.
     *  $TOSTART
     *  @param parameters $PARAMETERS   
     */
    @inline final def innerNodeDownUpTraverser(implicit parameters: Parameters = Parameters()) =
      thisGraph.innerNodeDownUpTraverser(this, parameters)

    /** Instantiates an [[OuterNodeDownUpTraverser]] $EXTENDSTYPE `(Boolean, N)` $SETROOT.
     *  $TOSTART
     *  @param parameters $PARAMETERS   
     */
    @inline final def outerNodeDownUpTraverser(implicit parameters: Parameters = Parameters()) =
      thisGraph.outerNodeDownUpTraverser(this, parameters)
  }
  @transient object TraverserInnerNode {
    /* The n parameter should be of type NodeT but then Scaladoc doesn't show implicit members
     * as expected. So TraverserInnerNode is given instead with the drawback of a cast:(.
     */
    implicit def toDefaultTraverser(n: TraverserInnerNode)
        : TraverserMethods[NodeT,InnerNodeTraverser] = innerNodeTraverser(n.asInstanceOf[NodeT])
  }
  
  /** Properties controlling the scope of traversals. */
  protected trait SubgraphProperties {
    /** $SUBGRAPHNODES */ def subgraphNodes: NodeFilter
    /** $SUBGRAPHEDGES */ def subgraphEdges: EdgeFilter
  }
  protected object SubgraphProperties {
    def apply[A](t: Traversable[A], nodeFilter: NodeFilter, edgeFilter: EdgeFilter) =
      new AbstractTraversable[A] with SubgraphProperties {
        def foreach[U](f: A => U): Unit = t foreach f
        def subgraphNodes = nodeFilter
        def subgraphEdges = edgeFilter
      } 
  }
  
  /** Properties controlling traversals. */
  protected trait Properties extends SubgraphProperties {
    /** $ROOT*/           def root: NodeT
    /** $PARAMETERS */    def parameters: Parameters
    /** $ORD */           def ordering: ElemOrdering
    /** $MAXWEIGHT */     def maxWeight: Option[Weight] = None
  }
  
  /** [[Properties]] and methods for creating modified properties in a fluent-interface manner.
   *  
   * @define UPDATED Creates a new [[FluentProperties]] based on `this` except for an updated
   */
  protected abstract class FluentProperties[+This <: FluentProperties[This]] {
    this: This with Properties =>

    protected def newTraverser:
        (NodeT, Parameters, NodeFilter, EdgeFilter, ElemOrdering, Option[Weight]) => This

    /** $UPDATED `parameters`. */
    final def withParameters(parameters: Parameters): This =
      if (this.parameters == parameters) this
      else newTraverser(root, parameters, subgraphNodes, subgraphEdges, ordering, maxWeight)
    
    /** $UPDATED `subgraphNodes` and/or `subgraphEdges`. */
    final def withSubgraph(nodes: NodeFilter = anyNode,
                           edges: EdgeFilter = anyEdge): This =
      if ((this.subgraphNodes eq nodes) &&
          (this.subgraphEdges eq edges)) this
      else newTraverser(root, parameters, nodes, edges, ordering, maxWeight)
      
    /** $UPDATED `ordering`. */
    final def withOrdering(ordering: ElemOrdering): This =
      if (this.ordering eq ordering) this
      else newTraverser(root, parameters, subgraphNodes, subgraphEdges, ordering, maxWeight)

    /** $UPDATED `kind`. */
    final def withKind(kind: Kind): This =
      withParameters(parameters.withKind(kind))

    /** $UPDATED `direction`. */
    final def withDirection(direction: Direction): This =
      withParameters(parameters.withDirection(direction))

    /** $UPDATED `maxDepth`. */
    final def withMaxDepth(maxDepth: Int): This =
      withParameters(parameters.withMaxDepth(maxDepth))

    /** $UPDATED `maxWeight`. */
    def withMaxWeight(maxWeight: Option[Weight]): This =
      if (this.maxWeight eq maxWeight) this
      else newTraverser(root, parameters, subgraphNodes, subgraphEdges, ordering, maxWeight)

    /** $UPDATED `maxWeight` having the given `max` value and the given weight function. */
    final def withMaxWeight[W: Numeric](max: W, edgeWeight: EdgeT => W): This =
      withMaxWeight(Some(Weight(max, edgeWeight)))

    /** $UPDATED `maxWeight` having the given `max` and the default weight function returning `edge.weight`. */
    final def withMaxWeight(max: Long): This =
      withMaxWeight(Some(Weight(max)))

    final def toInnerElemTraverser(root: NodeT): InnerElemTraverser =
      innerElemTraverser(root, parameters, subgraphNodes, subgraphEdges, ordering, maxWeight)
  }
  
  /** Represents a component of `this` graph with an underlying lazy implementation.
   *  Instances will be created by traversals based on [[componentTraverser]].  
   */
  protected abstract class Component extends Properties {
    def nodes: Set[NodeT]
    def edges: Set[EdgeT]
    def toGraph: Graph[N,E] =
      innerEdgeTraverser(root, parameters, subgraphNodes, subgraphEdges, ordering, maxWeight).toGraph
  }
  
  /** Controls the properties of graph traversals with no specific root.
   *  Provides methods to refine the properties and to invoke multiple traversals
   *  to span all graph components. 
   */
  protected abstract class ComponentTraverser
      extends FluentProperties[ComponentTraverser]
         with Properties 
         with Traversable[Component] {
    
    def findCycle[U](implicit visitor: InnerElem => U = empty): Option[Cycle]
    
    /** See [[GraphTraversal#topologicalSort]]. */
    def topologicalSort[U](implicit visitor: InnerElem => U = empty): CycleNodeOrTopologicalOrder
    
    /** See [[GraphTraversal#topologicalSortByComponent]]. */
    def topologicalSortByComponent[U](implicit visitor: InnerElem => U = empty): Traversable[CycleNodeOrTopologicalOrder]
  }
  
  /** Creates a [[ComponentTraverser]] responsible for invoking graph traversal methods
   *  that cover all components of this possibly disconnected graph.
   *    
   * @param parameters $PARAMETERS
   * @param subgraphNodes $SUBGRAPHNODES      
   * @param subgraphEdges $SUBGRAPHEDGES
   * @param ordering $ORD      
   * @param maxWeight $MAXWEIGHT      
   */
  def componentTraverser(
      parameters   : Parameters     = Parameters(),
      subgraphNodes: NodeFilter     = anyNode,
      subgraphEdges: EdgeFilter     = anyEdge,
      ordering     : ElemOrdering   = NoOrdering,
      maxWeight    : Option[Weight] = None): ComponentTraverser

  /** The `root`-related methods [[Traverser]] will inherit.
   *  
   * @define SHORTESTPATH Finds the shortest path from `root` to `potentialSuccessor`
   *         $CONSIDERING The calculation is based on the weight of the edges on the path.
   *         Edges have a default weight of `1L` that can be overridden by custom edges.
   *         A weight function yielding any numeric type may also be passed to `shortestPathTo`.
   * @define POTENTIALSUCC The node the shortest path is to be found to.
   * @define SHORTESTPATHRET The shortest path to `potentialSuccessor` or `None` if either
   *         a. there exists no path to `potentialSuccessor` or
   *         a. there exists a path to `potentialSuccessor` but $DUETOSUBG
   */
  protected abstract class TraverserMethods[A, +This <: TraverserMethods[A,This]]
      extends FluentProperties[This] {
    this: This with Properties =>

    def root: NodeT

    protected def nodeVisitor[U](f: A => U): (NodeT) => U
    protected def edgeVisitor[U](f: A => U): (EdgeT) => U

    /** $UPDATED `root`. */
    final def withRoot(root: NodeT): This =
      if (this.root eq root) this
      else newTraverser(root, parameters, subgraphNodes, subgraphEdges, ordering, maxWeight)
    
    protected def apply[U](pred: NodeFilter = noNode,
                           visitor: A => U  = empty): Option[NodeT]
    
    /** Finds a successor of `root` for which the predicate `pred` holds $CONSIDERING
     *  `root` itself does not count as a match. This is also true if it has a hook.
     *  If several successors holding `pred` exist any one of them may be returned.
     * 
     * @param pred The predicate which must hold for the resulting node.
     * @param visitor $OPTVISITOR
     * @return A node with the predicate `pred` or `None` if either
     *         a. there is no node with `pred` or
     *         a. there exists no path to such a node or
     *         a. there exists a path to such a node but $DUETOSUBG
     */
    final def findSuccessor[U](pred: NodeFilter)
                              (implicit visitor: A => U = empty): Option[NodeT] =
      apply(pred, visitor)

    /** Checks whether `potentialSuccessor` is a successor of this node $CONSIDERING
     *  Same as `isPredecessorOf`. 
     *
     * @param potentialSuccessor The node which is potentially a successor of this node. 
     * @param visitor $OPTVISITOR
     * @return `true` if a path exists from this node to `potentialSuccessor` and
     *         it had not to be excluded due to a `subgraph*` restriction.
     */
    @inline final def hasSuccessor[U](potentialSuccessor: NodeT)
                                     (implicit visitor: A => U = empty): Boolean =
      findSuccessor(_ eq potentialSuccessor)(visitor).isDefined

    /** Same as `hasSuccessor`. */
    @inline final def isPredecessorOf[U](potentialSuccessor: NodeT)
                                        (implicit visitor: A => U = empty): Boolean =
      hasSuccessor(potentialSuccessor)(visitor)

    /** Finds a predecessor of `root` for which the predicate `pred` holds $CONSIDERING
     * `root` itself does not count as a match. This is also true if it has a hook.
     * If several predecessors exist the algorithm selects the first of them found.
     * 
     * @param pred The predicate which must hold true for the resulting node.
     * @param visitor $OPTVISITOR
     * @return A node with the predicate `pred` or `None` if either
     *         a. there is no node with `pred` or
     *         a. there exists no path from such a node to this node or
     *         a. there exists a path from such a node to `root` but $DUETOSUBG
     */
    final def findPredecessor[U](pred: NodeFilter)
                                (implicit visitor: A => U = empty): Option[NodeT] =
      withParameters(parameters.withDirection(Predecessors))(pred, visitor)


    /** Checks whether `potentialPredecessor` is a predecessor of `root` $CONSIDERING
     *  Same as `isSuccessorOf`. 
     *
     * @param potentialPredecessor The node which is potentially a predecessor of `root`. 
     * @param visitor $OPTVISITOR
     * @return `true` if a path exists from `potentialPredecessor` to `root` and
     *         it had not to be excluded due to `subgraph` properties.
     */
    @inline final def hasPredecessor[U](potentialPredecessor: NodeT)
                                       (implicit visitor: A => U = empty): Boolean =
      findPredecessor(_ eq potentialPredecessor)(visitor).isDefined

    /** Same as `hasPredecessor`. */
    @inline final def isSuccessorOf[U](potentialPredecessor: NodeT)
                                      (implicit visitor: A => U = empty): Boolean =
      hasPredecessor(potentialPredecessor)(visitor)

    /** Finds a node connected with `root` by any number of edges with any direction
     *  for which the predicate `pred` holds $CONSIDERING
     *  For directed or mixed graphs the node to be found is weekly connected with this node.
     * `root` itself does not count as a match. This is also true if it has a hook.
     * If several connected nodes exist with `pred` the algorithm selects any one of these.
     * 
     * @param pred The predicate which must hold true for the resulting node.
     * @param visitor $OPTVISITOR
     * @return A node with the predicate `pred` or `None` if either
     *         a. there is no node with `pred` or
     *         a. there exists no connection to such a node or
     *         a. there exists a connection to such a node but $DUETOSUBG
     */
    final def findConnected[U](pred: NodeFilter)
                              (implicit visitor: A => U = empty): Option[NodeT] =
      withParameters(parameters.withDirection(AnyConnected))(pred, visitor)

    /** Checks whether `potentialConnected` is a node (not necessarily directly)
     *  connected with `root` by any number of edges with any direction $CONSIDERING
     *  For directed or mixed graphs it is satisfactory that `potentialConnected` is
     *  weekly connected with `root`.
     *
     * @param potentialConnected The node which is potentially connected with `root`. 
     * @param visitor $OPTVISITOR
     * @return `true` if a path exists from this node to `potentialConnected` and
     *         it had not to be excluded due to `subgraph` properties.
     */
    @inline final def isConnectedWith[U](potentialConnected: NodeT)
                                        (implicit visitor: A => U = empty): Boolean =
      findConnected(_ eq potentialConnected)(visitor).isDefined

    /** Finds a path from `root` to a successor of `root` for which `pred` holds $CONSIDERING
     * `root` itself does not count as a match. This is also true if it has a hook.
     * If several successors exist the algorithm selects any one of these.
     * 
     * @param pred The predicate which must hold true for the successor. 
     * @param visitor $OPTVISITOR
     * @return A path to a node with the predicate `pred` or `None` if either
     *         a. there is no node with `pred` or
     *         a. there exists no path to such a node or
     *         a. there exists a path to such a node but $DUETOSUBG
     */
    def pathUntil[U](pred: NodeFilter)
                    (implicit visitor: A => U = empty): Option[Path]

    /** Finds a path from `root` to `potentialSuccessor` $CONSIDERING
     *
     * @param potentialSuccessor The node a path is to be found to.
     * @param visitor $OPTVISITOR
     * @return A path to `potentialSuccessor` or `None` if either
     *         a. there is no node with `pred` or
     *         a. there exists no path to such a node
     */
    final def pathTo[U](potentialSuccessor: NodeT)
                       (implicit visitor: A => U = empty): Option[Path] =
      if (potentialSuccessor eq root) Some(Path.zero(root))
      else pathUntil(_ eq potentialSuccessor)(visitor)

    /** $SHORTESTPATH 
     *
     * @param potentialSuccessor $POTENTIALSUCC
     * @param visitor $OPTVISITOR
     * @return $SHORTESTPATHRET
     */
    @inline final
    def shortestPathTo[U](potentialSuccessor: NodeT)
                         (implicit visitor  : A => U = empty): Option[Path] =
      shortestPathTo(potentialSuccessor, Edge.defaultWeight, visitor)

    /** $SHORTESTPATH
     *  
     * @param potentialSuccessor $POTENTIALSUCC
     * @param weight Function to determine the weight of edges. If supplied, this function
     *        takes precedence over edge weights. 
     * @return $SHORTESTPATHRET 
     */
    @inline final
    def shortestPathTo[T: Numeric](potentialSuccessor: NodeT,
                                   weight            : EdgeT => T): Option[Path] =
      shortestPathTo(potentialSuccessor, weight, empty)

    /** $SHORTESTPATH
     *  
     * @param potentialSuccessor $POTENTIALSUCC
     * @param weight Function to determine the weight of edges. If supplied, this function
     *        takes precedence over edge weights. 
     * @param visitor $OPTVISITOR
     * @return $SHORTESTPATHRET 
     */
    def shortestPathTo[T:Numeric, U](potentialSuccessor: NodeT,
                                     weight            : EdgeT => T,
                                     visitor           : A => U): Option[Path]

    /** Finds a cycle starting the search at `root` $INTOACC, if any.
     *  The resulting cycle may start at any node connected with `this` node.
     * 
     * @param visitor $OPTVISITOR
     * @return A cycle or `None` if either
     *  a. there exists no cycle in the component depicting by `root` or
     *  a. there exists a cycle in the component but $DUETOSUBG
     */
    def findCycle[U](implicit visitor: A => U = empty): Option[Cycle]

    /** Sorts the component designated by the given node topologically.
     *  Only nodes connected with this node will be included in the resulting topological order. 
     *  If the graph is known to be connected choose [[GraphTraversal#topologicalSort]] instead. 
     *  $SEEFLUENT
     *  
      * @param ignorePredecessors If `true`, the topological sort will be partial in that it will only
      *                           include successors of `root`. `withSubgraph` restricts the successor nodes to
      *                           be included but not predecessors that will be excluded in total.
     *  @param visitor            Function to be called for each inner node or inner edge visited during the sort.
     */
    def topologicalSort[U](ignorePredecessors: Boolean = false)
                          (implicit visitor: InnerElem => U = empty): CycleNodeOrTopologicalOrder
  }

  /** Controls the properties of consecutive graph traversals starting at a root node.
   *  Provides methods to refine the properties and to invoke traversals.
   *  Instances will be created by [[innerNodeTraverser]] etc. 
   */
  trait Traverser[A, +This <: Traverser[A,This]]
      extends TraverserMethods[A,This]
         with Properties 
         with Traversable[A] {
    this: This =>
    
    def foreach[U](f: A => U): Unit =
      if (subgraphNodes(root))
        apply(noNode, f)

    /** Completes a traversal and creates a new connected graph populated with the
     *  elements visited.
     */
    final def toGraph: Graph[N,E] = thisGraph match {
      case g: Graph[N, E] =>
        val b = Graph.newBuilder(g.edgeT, Graph.defaultConfig)
        b += root
        val edgeTraverser = this match {
          case e: InnerEdgeTraverser => e
          case _ => innerEdgeTraverser(root, parameters, subgraphNodes, subgraphEdges, ordering) 
        }
        edgeTraverser foreach {
          e: EdgeT => b += e
        }
        b.result
    }    
  }

  /** Controls the properties of inner-node graph traversals. $TOSTART
   */
  abstract class InnerNodeTraverser extends Traverser[NodeT,InnerNodeTraverser]

  /** Creates a [[InnerNodeTraverser]] based on `scala.collection.Traversable[NodeT]`.
   *    
   * @param root $ROOT
   * @param parameters $PARAMETERS
   * @param subgraphNodes $SUBGRAPHNODES      
   * @param subgraphEdges $SUBGRAPHEDGES
   * @param ordering $ORD      
   * @param maxWeight $MAXWEIGHT      
   */
  def innerNodeTraverser(
      root         : NodeT,
      parameters   : Parameters     = Parameters(),
      subgraphNodes: NodeFilter     = anyNode,
      subgraphEdges: EdgeFilter     = anyEdge,
      ordering     : ElemOrdering   = NoOrdering,
      maxWeight    : Option[Weight] = None): InnerNodeTraverser

  /** Controls the properties of outer-node graph traversals. $TOSTART
   */
  abstract class OuterNodeTraverser extends Traverser[N,OuterNodeTraverser]

  /** Creates a [[OuterNodeTraverser]] based on `scala.collection.Traversable[N]`.
   *    
   * @param root $ROOT
   * @param parameters $PARAMETERS
   * @param subgraphNodes $SUBGRAPHNODES      
   * @param subgraphEdges $SUBGRAPHEDGES
   * @param ordering $ORD      
   * @param maxWeight $MAXWEIGHT      
   */
  def outerNodeTraverser(
      root         : NodeT,
      parameters   : Parameters     = Parameters(),
      subgraphNodes: NodeFilter     = anyNode,
      subgraphEdges: EdgeFilter     = anyEdge,
      ordering     : ElemOrdering   = NoOrdering,
      maxWeight    : Option[Weight] = None): OuterNodeTraverser
    
  /** Controls the properties of inner-edge graph traversals. $TOSTART
   */
  abstract class InnerEdgeTraverser extends Traverser[EdgeT,InnerEdgeTraverser]

  /** Creates a [[InnerEdgeTraverser]] based on `scala.collection.Traversable[EdgeT]`.
   *    
   * @param root $ROOT
   * @param parameters $PARAMETERS
   * @param subgraphNodes $SUBGRAPHNODES      
   * @param subgraphEdges $SUBGRAPHEDGES
   * @param ordering $ORD      
   */
  def innerEdgeTraverser(
      root         : NodeT,
      parameters   : Parameters     = Parameters(),
      subgraphNodes: NodeFilter     = anyNode,
      subgraphEdges: EdgeFilter     = anyEdge,
      ordering     : ElemOrdering   = NoOrdering,
      maxWeight    : Option[Weight] = None): InnerEdgeTraverser
      
  /** Controls the properties of outer-edge graph traversals. $TOSTART
   */
  abstract class OuterEdgeTraverser extends Traverser[E[N],OuterEdgeTraverser]

  /** Creates a [[OuterEdgeTraverser]] based on `scala.collection.Traversable[E[N]]`.
   *    
   * @param root $ROOT
   * @param parameters $PARAMETERS
   * @param subgraphNodes $SUBGRAPHNODES      
   * @param subgraphEdges $SUBGRAPHEDGES
   * @param ordering $ORD      
   * @param maxWeight $MAXWEIGHT      
   */
  def outerEdgeTraverser(
      root         : NodeT,
      parameters   : Parameters     = Parameters(),
      subgraphNodes: NodeFilter     = anyNode,
      subgraphEdges: EdgeFilter     = anyEdge,
      ordering     : ElemOrdering   = NoOrdering,
      maxWeight    : Option[Weight] = None): OuterEdgeTraverser 
 
  /** Controls the properties of inner-element graph traversals. $TOSTART
   */
  protected abstract class InnerElemTraverser extends Traverser[InnerElem,InnerElemTraverser]

  /** Creates a [[InnerElemTraverser]] based on `scala.collection.Traversable[InnerElem]`.
   *    
   * @param root $ROOT
   * @param parameters $PARAMETERS
   * @param subgraphNodes $SUBGRAPHNODES      
   * @param subgraphEdges $SUBGRAPHEDGES
   * @param ordering $ORD      
   * @param maxWeight $MAXWEIGHT      
   */
  def innerElemTraverser(
      root         : NodeT,
      parameters   : Parameters     = Parameters(),
      subgraphNodes: NodeFilter     = anyNode,
      subgraphEdges: EdgeFilter     = anyEdge,
      ordering     : ElemOrdering   = NoOrdering,
      maxWeight    : Option[Weight] = None): InnerElemTraverser
      
  /** Controls the properties of outer-element graph traversals. $TOSTART
   */
  trait OuterElemTraverser extends Traverser[OuterElem[N,E],OuterElemTraverser]

  /** Creates a [[OuterElemTraverser]] based on `scala.collection.Traversable[OuterElem]`.
   *    
   * @param root $ROOT
   * @param parameters $PARAMETERS
   * @param subgraphNodes $SUBGRAPHNODES      
   * @param subgraphEdges $SUBGRAPHEDGES
   * @param ordering $ORD      
   * @param maxWeight $MAXWEIGHT      
   */
  def outerElemTraverser(
      root         : NodeT,
      parameters   : Parameters     = Parameters(),
      subgraphNodes: NodeFilter     = anyNode,
      subgraphEdges: EdgeFilter     = anyEdge,
      ordering     : ElemOrdering   = NoOrdering,
      maxWeight    : Option[Weight] = None): OuterElemTraverser
      
  /** Controls the properties of inner-node down-up graph traversals. $TOSTART
   */
  abstract class InnerNodeDownUpTraverser
      extends Traverser[(Boolean, NodeT),InnerNodeDownUpTraverser]

  /** Creates a [[InnerNodeDownUpTraverser]] based on `scala.collection.Traversable[(Boolean, NodeT)]`
   *  $DOWNUPBOOLEAN
   *    
   * @param root $ROOT
   * @param parameters $PARAMETERS A `kind` different from `DepthFirst` will be ignored.
   * @param subgraphNodes $SUBGRAPHNODES      
   * @param subgraphEdges $SUBGRAPHEDGES
   * @param ordering $ORD      
   * @param maxWeight $MAXWEIGHT      
   */
  def innerNodeDownUpTraverser(
      root         : NodeT,
      parameters   : Parameters     = Parameters(),
      subgraphNodes: NodeFilter     = anyNode,
      subgraphEdges: EdgeFilter     = anyEdge,
      ordering     : ElemOrdering   = NoOrdering,
      maxWeight    : Option[Weight] = None): InnerNodeDownUpTraverser
      
  /** Controls the properties of outer-node down-up graph traversals. $TOSTART
   */
  abstract class OuterNodeDownUpTraverser
      extends Traverser[(Boolean, N),OuterNodeDownUpTraverser]

  /** Creates a [[OuterNodeDownUpTraverser]] based on `scala.collection.Traversable[(Boolean, N)]`
   *  $DOWNUPBOOLEAN
   *    
   * @param root $ROOT
   * @param parameters $PARAMETERS A `kind` different from `DepthFirst` will be ignored.
   * @param subgraphNodes $SUBGRAPHNODES      
   * @param subgraphEdges $SUBGRAPHEDGES
   * @param ordering $ORD      
   */
  def outerNodeDownUpTraverser(
      root         : NodeT,
      parameters   : Parameters     = Parameters(),
      subgraphNodes: NodeFilter     = anyNode,
      subgraphEdges: EdgeFilter     = anyEdge,
      ordering     : ElemOrdering   = NoOrdering,
      maxWeight    : Option[Weight] = None): OuterNodeDownUpTraverser      
}

/** Contains traversal parameter definitions such as direction constants.
 *  
 * @define KIND The kind of traversal including breadth-first and depth-fist search.
 * @define DIRECTION Determines which connected nodes the traversal has to follow.
 *         The default value is `Successors`.
 * @define MAXDEPTH A positive value to limit the number of layers for BFS respectively
 *         the number of consecutive child visits before siblings are visited for DFS.
 *         `0` - the default - indicates that the traversal should have
 *         an unlimited depth.
 * @author Peter Empen
 */
object GraphTraversal {
  /** Algebraic type to determine which connected nodes the traversal has to follow.
   *  The default value is `Successors`.
   */
  sealed trait Direction  
  /** Defines the traversal to follow successor nodes. */
  object Successors   extends Direction 
  /** Defines the traversal to follow predecessor nodes. */
  object Predecessors extends Direction
  /** Defines the traversal to follow successor and predecessor nodes alike. */
  object AnyConnected extends Direction

  /** Marker trait for informers aimed at passing algorithmic-specific state
   *  to [[scalax.collection.GraphTraversal.ExtendedNodeVisitor]].
   *  Before calling an informer please match against one of 
   *  1. [[scalax.collection.GraphTraversalImpl.BfsInformer]]
   *  1. [[scalax.collection.GraphTraversalImpl.DfsInformer]]   
   *  1. [[scalax.collection.GraphTraversalImpl.WgbInformer]]
   *  1. [[scalax.collection.GraphTraversalImpl.DijkstraInformer]]
   *  or any other implementation that is currently not known.   
   */
  trait NodeInformer
  object NodeInformer extends Serializable {
    def empty = new NodeInformer {}
  }

  /** Algebraic type for the kind of traversal. */
  trait Kind {
    /** Whether 'this' kind equals to BreadthFirst. */
    def isBsf = this eq BreadthFirst
  }
  /** Instructs the traverser to use a breadth first search
   *  (BSF, search layer-for-layer). */
  case object BreadthFirst extends Kind
  /** Instructs the traverser to use a depth first search (DFS). */
  case object DepthFirst extends Kind

  /** Parameters to control traversals.
   *
   * @param kind       $KIND
   * @param direction  $DIRECTION
   * @param maxDepth   $MAXDEPTH
   * @define UPDATED Creates a new `Parameters` based on `this` except for an updated 
   */
  case class Parameters(kind     : Kind = BreadthFirst,
                        direction: Direction = Successors,
                        maxDepth : Int = 0) {
    
    /** $UPDATED `kind`. */
    def withKind(kind: Kind): Parameters =
      if (this.kind eq kind) this else copy(kind = kind)

    /** $UPDATED `direction`. */
    def withDirection(direction: Direction): Parameters =
      if (this.direction == direction) this else copy(direction = direction)

    /** $UPDATED `maxDepth`. */
    def withMaxDepth(maxDepth: Int): Parameters =
      if (this.maxDepth == maxDepth) this else copy(maxDepth = maxDepth)
  }
  object Parameters {
    
    /** Default `Parameters`. */
    def apply = new Parameters()
    
    /** Creates `Parameters` of kind `BreadthFirst` with specific `direction` and `maxDepth`. */
    def Bfs(direction: Direction = Successors, maxDepth : Int = 0) =
      Parameters(direction = direction, maxDepth = maxDepth)
    
    /** Creates `Parameters` of kind `DepthFirst` with specific `direction` and `maxDepth`. */
    def Dfs(direction: Direction = Successors, maxDepth : Int = 0) =
      Parameters(kind = DepthFirst, direction = direction, maxDepth = maxDepth)
  }

  /** Implements an empty visitor based on a value.
   */
  object Visitor {
    private final val _empty: Any => Unit = null
    @inline final def   empty  [A,U]: A => U = _empty.asInstanceOf[A => U]
    @inline final def isEmpty  [A,U](visitor: A => U) = visitor eq _empty   
    @inline final def isDefined[A,U](visitor: A => U) = visitor ne _empty   
  }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy