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

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

The newest version!
package scalax.collection

import language.higherKinds
import scala.annotation.{tailrec, switch}

import GraphPredef.{InnerNodeParam, OuterEdge}
import edge.LBase.LEdge

/**
 * Container for basic edge types to be used in the context of `Graph`.
 * You will usually simply import all its members along with the members of Param:
 * {{{
 * import scalax.collection.GraphPredef._, scalax.collection.GraphEdge,_
 * }}}
 * @define SHORTCUT Allows to replace the edge object with it's shortcut like
 * @define ORDIHYPER or the source/target ends of a directed hyperedge
 * @define BAG bag that is an unordered collection of nodes with duplicates allowed
 * @author Peter Empen
 */
object GraphEdge {
  /**
   * Template for Edges in a Graph.
   * 
   * Implementation note: Irrespective of the containing `Graph` all library-provided Edges
   * are immutable.
   * 
   * @tparam N the user type of the nodes (ends) of this edge.
   * @define CalledByValidate This function is called on every edge-instantiation
   *         by `validate` that throws EdgeException if this method returns `false`.
   * @define ISAT In case this edge is undirected this method maps to `isAt`
   * @author Peter Empen
   */
  sealed trait EdgeLike[+N] extends Iterable[N] with Eq with Serializable
  {
    /** The end nodes joined by this edge.
     * 
     * Nodes will typically be represented by Tuples. Alternatively subclasses of `Iterable`
     * implementing Product, such as List, may also be used.
     * In the latter case be aware of higher memory footprint.
     */
    def nodes: Product
    /** Iterator for the nodes (end-points) of this edge.
     */
    def iterator: Iterator[N]
    /** Sequence of the end points of this edge.
     */
    def nodeSeq: Seq[N] = iterator.toSeq
    /**
     * The first node. Same as _n(0).
     */
    @inline final def _1: N = nodes.productElement(0).asInstanceOf[N] 
    /**
     * The second node. Same as _n(1).
     */
    def _2: N =  nodes match {
      case i: Iterable[N] => i.drop(1).head
      case p: Product     => nodes.productElement(1).asInstanceOf[N] 
    }  
    /**
     * The n'th node with 0 <= n < arity.
     */
    def _n(n: Int): N = (n: @scala.annotation.switch) match {
      case 0 => _1
      case 1 => _2
      case _ => {
        if (n < 0 || n >= arity) throw new IndexOutOfBoundsException
        nodes match {
          case i: Iterable[N] => i.drop(n).head
          case p: Product     => nodes.productElement(n).asInstanceOf[N] 
        }
      }  
    }
    /**
     * Number of nodes linked by this Edge. At least two nodes are linked. In case of
     * a hook, the two nodes are identical. Hyperedges may link more than two nodes.
     */
    final def arity = nodes match {
      case i: Iterable[N] => i.size
      case p: Product     => nodes.productArity 
    } 
    /**
     * A function to determine whether the `arity` of the passed `Product`
     * of nodes (that is the number of edge ends) is valid.
     * $CalledByValidate
     */
    protected def isValidArity(size: Int): Boolean
    /**
     * This method may be overridden to enforce additional validation at edge
     * creation time. Be careful to call `super.isValidCustom` when overriding.
     * $CalledByValidate
     */
    protected def isValidCustom = true
    protected def isValidCustomExceptionMessage = "Custom validation failed: " + toString
    protected class EdgeException(val msg: String) extends Exception
    /**
     * Performs basic, inevitable edge validation. Among others, ensures
     * that `nodes ne null` and no edge end `eq null`.
     * 
     * This validation method must be called in the constructor of any edge class
     * that directly extends or mixes in `EdgeLike`. To perform additional custom
     * validation `isValidCustom` is to be overridden.
     *  
     *  @throws EdgeException if any of the basic validations or of eventually
     *  supplied additional validations fails.
     */
    protected final def validate {
      nodes match {
        case r: AnyRef if r eq null =>
          throw new EdgeException(s"null node in: $toString")
        case _ =>
      }
      val ar = arity
      if (! (ar >= 2 && isValidArity(ar)))
        throw new EdgeException("Invalid arity: " + ar + ": " + toString)
      if (! isValidCustom)
          throw new EdgeException(isValidCustomExceptionMessage)
    }
    /** `true` if this edge is directed. */
    def directed = false
    /** Same as `directed`. */
    @inline final def isDirected = directed
    /** `true` if this edge is undirected. */
    @inline final def undirected = ! directed
    /** Same as `undirected`. */
    @inline final def isUndirected = undirected
    /** `true` if this is a hyperedge that is it may have more than two ends. */
    def isHyperEdge = true
    /** `true` if this edge has exactly two ends. */
    @inline final def nonHyperEdge = ! isHyperEdge
    /** `true` if this edge produces a self-loop.
     * In case of a non-hyperedge, a loop is given if the incident nodes are equal.
     * In case of a directed hyperedge, a loop is given if the source is equal to
     * any of the targets.
     * In case of an undirected hyperedge, a loop is given if any pair of incident
     * nodes has equal nodes.
     */
    def isLooping = if (arity == 2) _1 == _2
                    else if (directed) iterator.drop(1) exists (_ == _1)
                    else (MSet() ++= iterator).size < arity
    /** Same as `! looping`. */                
    final def nonLooping = ! isLooping 
    /**
     * The weight of this edge with a default of 1.
     * 
     * Note that `weight` is normally not part of the edge key (hashCode). As a result,
     * edges with different weights connecting the same nodes will be evaluated as equal
     * and thus added once and only once to the graph.
     * In case you need multi-edges based on different weights 
     * you should either make use of a predefined key-weighted edge type such as `WDiEdge` 
     * or define a custom edge class that mixes in `ExtendedKey` and adds `weight` to
     * `keyAttributes`. 
     * 
     * For weight types other than `Long` you may either convert then to `Long`
     * prior to edge creation or define a custom edge class that includes the
     * weight of the appropriate type and overrides `def weight` to provide the
     * required conversion to `Long`.  
     */
    def weight: Long = 1
    /**
     * The label of this edge. If `Graph`'s edge type parameter has been inferred or set
     * to a labeled edge type all contained edges are labeled. Otherwise you should
     * assert, for instance by calling `isLabeled`, that the edge instance is labeled
     * before calling this method.
     * 
     * Note that `label` is normally not part of the edge key (hashCode). As a result,
     * edges with different labels connecting the same nodes will be evaluated as equal
     * and thus added once and only once to the graph.
     * In case you need multi-edges based on different labels 
     * you should either make use of a predefined key-labeled edge type such as `LDiEdge` 
     * or define a custom edge class that mixes in `ExtendedKey` and adds `label` to
     * `keyAttributes`. 
     * 
     * @throws UnsupportedOperationException if the edge is non-labeled.
     */
    def label: Any =
      throw new UnsupportedOperationException("Call of label for a non-labeled edge.")
    /** `true` if this edge is labeled. See also `label`. */
    def isLabeled = this.isInstanceOf[LEdge[N]]

    /** Same as `isAt`. */
    @inline final def contains[M>:N](node: M): Boolean = isAt(node)

    /** `true` if `node` is incident with this edge. */
    def isAt[M>:N](node: M): Boolean
    /** `true` if any end of this edge fulfills `pred`. */
    def isAt(pred: N => Boolean): Boolean
    
    /** `true` if `node` is a source of this edge. $ISAT. */
    def hasSource[M>:N](node: M): Boolean
    /** `true` if any source end of this edge fulfills `pred`. */
    def hasSource(pred: N => Boolean): Boolean

    /** `true` if `node` is a target of this edge. $ISAT. */
    def hasTarget[M>:N](node: M): Boolean
    /** `true` if any target end of this edge fulfills `pred`. */
    def hasTarget(pred: N => Boolean): Boolean
    
    /** Applies `f` to all source ends of this edge without new memory allocation. */
    def withSources[U](f: N => U): Unit
    /** All source ends of this edge. */
    def sources: Traversable[N] = new Traversable[N] {
      def foreach[U](f: N => U): Unit = withSources(f)
    }
    
    /** Applies `f` to the target ends of this edge without new memory allocation. */
    def withTargets[U](f: N => U): Unit
    /** All target ends of this edge. */
    def targets: Traversable[N] = new Traversable[N] {
      def foreach[U](f: N => U): Unit = withTargets(f)
    }

    /** `true` if
* a) both `n1` and `n2` are at this edge for an undirected edge
* b) `n1` is a source and `n2` a target of this edge for a directed edge. */ def matches[M>:N](n1: M, n2: M): Boolean /** `true` if
* a) two distinct ends of this undirected edge exist * for which `p1` and `p2` hold or
* b) `p1` holds for a source and `p2` for a target of this directed edge. */ def matches(p1: N => Boolean, p2: N => Boolean): Boolean override def canEqual(that: Any): Boolean = that.isInstanceOf[EdgeLike[_]] override def equals(other: Any): Boolean = other match { case that: EdgeLike[_] => (this eq that) || (that canEqual this) && (this.directed == that.directed) && (this.isInstanceOf[Keyed] == that.isInstanceOf[Keyed]) && equals(that) case _ => false } /** Preconditions: * `this.directed == that.directed &&` * `this.isInstanceOf[Keyed] == that.isInstanceOf[Keyed]` */ protected def equals(other: EdgeLike[_]): Boolean = baseEquals(other) override def hashCode: Int = baseHashCode final protected def thisSimpleClassName = try { this.getClass.getSimpleName } catch { // Malformed class name case e: java.lang.InternalError => this.getClass.getName } override def stringPrefix = "Nodes" protected def nodesToStringWithParenthesis = false protected def nodesToStringSeparator = EdgeLike.nodeSeparator protected def nodesToString = if (nodesToStringWithParenthesis) nodes match { case it: Iterable[N] => it.toString.patch(0, stringPrefix, it.stringPrefix.length) case _=> stringPrefix + nodes.toString } else iterator mkString nodesToStringSeparator protected def attributesToString = "" protected def toStringWithParenthesis = false protected def brackets = EdgeLike.curlyBraces override def toString = { val attr = attributesToString val woParenthesis = nodesToString + ( if(attr.length > 0) attr else "" ) if (toStringWithParenthesis) thisSimpleClassName + brackets.left + woParenthesis + brackets.right else woParenthesis } } /** * This trait is to be mixed in by every class implementing EdgeLike. */ trait EdgeCopy[+CC[X] <: EdgeLike[_]] { /** * It is a prerequisite for edge-classes to implement this method. Otherwise * they cannot be passed to a `Graph`. * * `Graph` calls this method internally to obtain a new instance of the * edge passed to `Graph` with nodes of the type of the inner class `NodeT` * which itself contains the outer node. */ protected[collection] def copy[NN](newNodes: Product): CC[NN] } object EdgeLike { val nodeSeparator = "~" protected case class Brackets(left: Char, right: Char) protected val curlyBraces = Brackets('{', '}') def unapply[N](e: EdgeLike[N]) = Some(e) } /** * Helper object to convert edge-factory parameter-lists to tuple-n or list. * * @author Peter Empen */ object NodeProduct { @inline final def apply[N](node_1: N, node_2: N): Tuple2[N,N] = Tuple2(node_1, node_2) @inline def apply[N](node_1: N, node_2: N, nodes: N*): Product = { (nodes.size: @scala.annotation.switch) match { case 1 => Tuple3(node_1, node_2, nodes(0)) case 2 => Tuple4(node_1, node_2, nodes(0), nodes(1)) case 3 => Tuple5(node_1, node_2, nodes(0), nodes(1), nodes(2)) case _ => List[N](node_1, node_2) ::: List(nodes: _*) } } final def apply[N](nodes: Iterable[N]): Product = nodes match { case n1 :: n2 :: rest => if (rest eq Nil) apply(n1, n2) else apply(n1, n2, rest: _*) case _ => val it = nodes.iterator val n1 = it.next val n2 = it.next if (it.hasNext) apply(n1, n2, it.toList: _*) else apply(n1, n2) } } protected[collection] trait Keyed /** Defines how to handle the ends of hyperedges, or the source/target ends of directed hyperedges, * with respect to equality. */ sealed abstract class CollectionKind(val duplicatesAllowed: Boolean, val orderSignificant: Boolean) object CollectionKind { protected[collection] def from(duplicatesAllowed: Boolean, orderSignificant: Boolean): CollectionKind = if (duplicatesAllowed) if (orderSignificant) Sequence else Bag else throw new IllegalArgumentException("'duplicatesAllowed == false' is not supported for endpoints kind.") protected[collection] def from(s: String): CollectionKind = if (s == Bag.toString) Bag else if (s == Sequence.toString) Sequence else throw new IllegalArgumentException(s"Unexpected representation of '$s' for endpoints kind.") protected[collection] def from(edge: EdgeLike[_]): CollectionKind = CollectionKind.from(true, edge.isInstanceOf[OrderedEndpoints]) def unapply(kind: CollectionKind): Option[(Boolean, Boolean)] = Some((kind.duplicatesAllowed, kind.orderSignificant)) } /** Marks a hyperedge, $ORDIHYPER, to handle the endpoints * as an unordered collection of nodes with duplicates allowed. */ case object Bag extends CollectionKind(true, false) /** Marks a hyperedge, $ORDIHYPER, to handle the endpoints * as an ordered collection of nodes with duplicates allowed. */ case object Sequence extends CollectionKind(true, true) /** Marks (directed) hyperedge endpoints to have a significant order. */ protected[collection] trait OrderedEndpoints protected[collection] sealed trait Eq { protected def baseEquals(other: EdgeLike[_]): Boolean protected def baseHashCode: Int } protected[collection] object Eq { def nrEqualingNodes(itA: Iterator[_], itB: Iterable[_]): Int = { var nr = 0 val bLen = itB.size val used = new Array[Boolean](bLen) for (a <- itA) { val bs = itB.iterator var j = 0 while(j < bLen) { val b = bs.next if (! used(j) && a == b) { nr += 1 used(j) = true j = bLen } j += 1 } } nr } def equalTargets(left : EdgeLike[_], leftEnds : Traversable[_], right: EdgeLike[_], rightEnds: Traversable[_], arity: Int): Boolean = { val thisOrdered = left .isInstanceOf[OrderedEndpoints] val thatOrdered = right.isInstanceOf[OrderedEndpoints] thisOrdered == thatOrdered && ( if (thisOrdered) leftEnds.toSeq sameElements rightEnds.toSeq else Eq.nrEqualingNodes(leftEnds.toIterator, rightEnds.toIterable) == arity ) } } protected[collection] trait EqHyper extends Eq { this: EdgeLike[_] => override protected def baseEquals(other: EdgeLike[_]) = { val (thisArity, thatArity) = (arity, other.arity) if (thisArity == thatArity) Eq.equalTargets(this, this.sources, other, other.sources, thisArity) else false } override protected def baseHashCode: Int = (0 /: iterator)(_ ^ _.hashCode) } /** Equality for targets handled as a $BAG. * Targets are equal if they contain the same nodes irrespective of their position. */ protected[collection] trait EqDiHyper extends Eq { this: DiHyperEdgeLike[_] => override protected def baseEquals(other: EdgeLike[_]) = { val (thisArity, thatArity) = (arity, other.arity) if (thisArity == thatArity) if (thisArity == 2) this._1 == other._1 && this._2 == other._2 else other match { case diHyper: DiHyperEdgeLike[_] => this.source == diHyper.source && Eq.equalTargets(this, this.targets, other, other.targets, arity - 1) case _ => false } else false } override protected def baseHashCode = { var m = 4 def mul(i: Int): Int = { m += 3; m * i } (0 /: iterator)((s: Int, n: Any) => s ^ mul(n.hashCode)) } } protected[collection] trait EqUnDi extends Eq { this: EdgeLike[_] => @inline final protected def unDiBaseEquals(n1: Any, n2: Any) = this._1 == n1 && this._2 == n2 || this._1 == n2 && this._2 == n1 override protected def baseEquals(other: EdgeLike[_]) = other.arity == 2 && unDiBaseEquals(other._1, other._2) override protected def baseHashCode = (_1.##) ^ (_2.##) } protected[collection] trait EqDi extends Eq { this: DiEdgeLike[_] => @inline final protected def diBaseEquals(n1: Any, n2: Any) = this._1 == n1 && this._2 == n2 final protected override def baseEquals(other: EdgeLike[_]) = other.arity == 2 && diBaseEquals(other._1, other._2) override protected def baseHashCode = (23 * (_1.##)) ^ (_2.##) } /** * Template trait for directed edges. * * Any class representing directed edges must inherit from this trait. * * @author Peter Empen */ trait DiHyperEdgeLike[+N] extends EdgeLike[N] with EqDiHyper { @inline final override def directed = true /** Synonym for `source`. */ @inline final def from = source /** The single source node of this directed edge. */ @inline final def source = _1 /** Synonym for `target`. */ def to = target /** The target node for a directed edge; * one of the target nodes for a directed hyperedge. */ @inline final def target = _2 override def hasSource[M>:N](node: M) = this._1 == node override def hasSource(pred: N => Boolean) = pred(this._1) override def hasTarget[M>:N](node: M) = targets exists (_ == node) override def hasTarget(pred: N => Boolean) = targets exists pred override def withSources[U](f: N => U) = f(this._1) override def withTargets[U](f: N => U) = (iterator drop 1) foreach f override def matches[M >: N](n1: M, n2: M): Boolean = source == n1 && (targets exists (_ == n2)) override def matches(p1: N => Boolean, p2: N => Boolean): Boolean = p1(source) && (targets exists p2) override protected def nodesToStringSeparator = DiEdgeLike.nodeSeparator } trait DiEdgeLike[+N] extends DiHyperEdgeLike[N] with EqDi { @inline final override def to = _2 final override def hasSource[M>:N](node: M) = this._1 == node final override def hasSource(pred: N => Boolean) = pred(this._1) final override def hasTarget[M>:N](node: M) = this._2 == node final override def hasTarget(pred: N => Boolean) = pred(this._2) final override def withTargets[U](f: N => U) = f(this._2) final override def withSources[U](f: N => U) = f(this._1) override def matches[M >: N](n1: M, n2: M): Boolean = diBaseEquals(n1, n2) override def matches(p1: N => Boolean, p2: N => Boolean): Boolean = p1(this._1) && p2(this._2) } object DiEdgeLike { val nodeSeparator = "~>" def unapply[N](e: DiEdgeLike[N]) = Some(e) } /** * This trait supports extending the default key of an edge with additional attributes. * * As a default, the key - represented by `hashCode` - of an edge is made up of the * participating nodes. * Custom edges may need to expand this default key with additional attributes * thus enabling the definition of several edges between the same nodes (multi-edges). * Edges representing flight connections between airports are a typical example * for this requirement because their key must also include something like a flight number. * When defining a custom edge for a multi-graph, this trait must be mixed in. * * @tparam N type of the nodes * @author Peter Empen */ trait ExtendedKey[+N] extends EdgeLike[N] { /** * Each element in this sequence references an attribute of the custom * edge which composes the key of this edge. All attributes added to this sequence * will be considered when calculating `equals` and `hashCode`. * * Neither an empty sequence, nor null elements are permitted. */ def keyAttributes: Seq[Any] override def equals(other: Any) = super.equals(other) && (other match { case that: ExtendedKey[_] => this.keyAttributes == that.keyAttributes case _ => false }) override def hashCode = super.hashCode + keyAttributes.map(_.## * 41).sum override protected def attributesToString: String } object ExtendedKey { def unapply[N](e: ExtendedKey[N]) = Some(e) } trait LoopFreeEdge[+N] extends EdgeLike[N] { override protected def isValidCustom = { super.isValidCustom if (arity == 2) _1 != _2 else nonLooping } override protected def isValidCustomExceptionMessage = "No loop is allowed: " + toString } object LoopFreeEdge { def unapply[N](e: LoopFreeEdge[N]) = Some(e) } /** Marker trait for companion objects of any kind of edge. */ trait EdgeCompanionBase[+E[N] <: EdgeLike[N]] extends Serializable /** * The abstract methods of this trait must be implemented by companion objects * of simple (non-weighted, non-labeled) hyperedges. * * @author Peter Empen */ trait HyperEdgeCompanion[+E[N] <: EdgeLike[N]] extends EdgeCompanionBase[E] { def apply[N](node_1: N, node_2: N, nodes: N*)(implicit endpointsKind: CollectionKind = Bag): E[N] /** @param nodes must be of arity >= 2 */ protected[collection] def from[N](nodes: Product)(implicit endpointsKind: CollectionKind): E[N] } /** * The abstract methods of this trait must be implemented by companion objects * of simple (non-weighted, non-labeled) edges. * * @author Peter Empen */ trait EdgeCompanion[+E[N] <: EdgeLike[N]] extends EdgeCompanionBase[E] { def apply[N](node_1: N, node_2: N): E[N] /** @param nodes must be of arity == 2 */ protected[collection] def from[N](nodes: Product): E[N] } // ------------------------------------------------------------------------ * /** Represents an undirected hyperedge (hyperlink) in a hypergraph * with unlimited number of nodes. * * @author Peter Empen */ @SerialVersionUID(50L) class HyperEdge[+N] (override val nodes: Product) extends EdgeLike [N] with EdgeCopy [HyperEdge] with OuterEdge[N,HyperEdge] with EqHyper { validate protected def isValidArity(size: Int) = size >= 2 override protected[collection] def copy[NN](newNodes: Product) = if (this.isInstanceOf[OrderedEndpoints]) new HyperEdge[NN](newNodes) with OrderedEndpoints else new HyperEdge[NN](newNodes) /** Iterator for the nodes (end-points) of this edge. */ def iterator: Iterator[N] = nodes match { case i: Iterable[N] => i.iterator case p: Product => p.productIterator.asInstanceOf[Iterator[N]] } override def isAt[M>:N](node: M) = iterator contains node override def isAt(pred: N => Boolean) = iterator exists pred override def hasSource[M>:N](node: M) = isAt(node) override def hasSource(pred: N => Boolean) = isAt(pred) override def hasTarget[M>:N](node: M) = isAt(node) override def hasTarget(pred: N => Boolean) = isAt(pred) override def withSources[U](f: N => U) = iterator foreach f override def withTargets[U](f: N => U) = withSources(f) final protected def matches(fList: List[N => Boolean]): Boolean = { val it = iterator @tailrec def loop(checks: List[N => Boolean]): Boolean = { if (checks.isEmpty) true else if (! it.hasNext) false else { val n = it.next val f = checks find (f => f(n)) if (f.isDefined) loop(checks diff List(f.get)) else loop(checks) } } loop(fList) } override def matches[M >: N](n1: M, n2: M): Boolean = matches(List((n: M) => n == n1, (n: M) => n == n2)) override def matches(p1: N => Boolean, p2: N => Boolean): Boolean = matches(List(p1, p2)) } /** * Factory for undirected hyper-edges. * `GraphPredef` also supports implicit conversion from `node_1 ~ node_2 ~ node_3` * to `HyperEdge`. */ object HyperEdge extends HyperEdgeCompanion[HyperEdge] { def apply[N](node_1: N, node_2: N, nodes: N*)(implicit endpointsKind: CollectionKind = Bag): HyperEdge[N] = from(NodeProduct(node_1, node_2, nodes: _*)) def apply[N](nodes: Iterable[N])(implicit endpointsKind: CollectionKind): HyperEdge[N] = from(nodes.toList) protected[collection] def from [N](nodes: Product)(implicit endpointsKind: CollectionKind): HyperEdge[N] = if (endpointsKind.orderSignificant) new HyperEdge[N](nodes) with OrderedEndpoints else new HyperEdge[N](nodes) def unapplySeq[N](e: HyperEdge[N]) = if (e eq null) None else Some(e._1, e.nodeSeq drop 1) } /** $SHORTCUT `hyperedge match {case n1 ~~ (n2, n3) => f(n1, n2, n3)}`. */ val ~~ = HyperEdge /** Represents a directed edge in a hypergraph with a single source and an unlimited number * of taget nodes. Target nodes are handled as a $BAG. */ @SerialVersionUID(51L) class DiHyperEdge[+N] (nodes: Product) extends HyperEdge [N](nodes) with DiHyperEdgeLike[N] with EdgeCopy [DiHyperEdge] with OuterEdge [N,DiHyperEdge] { override protected[collection] def copy[NN](newNodes: Product): DiHyperEdge[NN] = if (this.isInstanceOf[OrderedEndpoints]) new DiHyperEdge[NN](newNodes) with OrderedEndpoints else new DiHyperEdge[NN](newNodes) } /** * Factory for directed hyper-edges. * `GraphPredef` also supports implicit conversion from `node_1 ~> node_2 ~> node_3` * to `DirectedHyperEdge`. * * @author Peter Empen */ object DiHyperEdge extends HyperEdgeCompanion[DiHyperEdge] { def apply[N] (from: N, to_1: N, to_n: N*)(implicit targetsKind: CollectionKind = Bag): DiHyperEdge[N] = DiHyperEdge.from(NodeProduct(from, to_1, to_n: _*)) def apply[N] (nodes: Iterable[N])(implicit targetsKind: CollectionKind): DiHyperEdge[N] = DiHyperEdge.from(nodes.toList) protected[collection] def from [N](nodes: Product)(implicit targetsKind: CollectionKind): DiHyperEdge[N] = if (targetsKind.orderSignificant) new DiHyperEdge[N](nodes) with OrderedEndpoints else new DiHyperEdge[N](nodes) def unapplySeq[N](e: DiHyperEdge[N]) = if (e eq null) None else Some(e.from, e.nodeSeq drop 1) } /** $SHORTCUT `diHyperedge match {case source ~~> (t1, t2) => f(source, t1, t2)}`. */ val ~~> = DiHyperEdge /** * Represents an undirected edge. * * @author Peter Empen */ @SerialVersionUID(52L) class UnDiEdge[+N] (nodes: Product) extends HyperEdge[N](nodes) with EdgeCopy [UnDiEdge] with OuterEdge[N,UnDiEdge] with EqUnDi { @inline final override protected def isValidArity(size: Int) = size == 2 @inline final override def isHyperEdge = false override protected[collection] def copy[NN](newNodes: Product) = new UnDiEdge[NN](newNodes) @inline final override def size = 2 override def iterator: Iterator[N] = new AbstractIterator[N] { private var count = 0 def hasNext = count < 2 def next: N = { count += 1 (count: @switch) match { case 1 => _1 case 2 => _2 case _ => Iterator.empty.next } } } override def isAt[M>:N](node: M) = this._1 == node || this._2 == node override def isAt(pred: N => Boolean) = pred(this._1) || pred(this._2) override def withSources[U](f: N => U) = { f(this._1); f(this._2) } override def withTargets[U](f: N => U) = withSources(f) override def matches[M >: N](n1: M, n2: M): Boolean = unDiBaseEquals(n1, n2) override def matches(p1: N => Boolean, p2: N => Boolean): Boolean = (p1(this._1) && p2(this._2) || p1(this._2) && p2(this._1) ) } /** * Factory for undirected edges. * `GraphPredef` also supports implicit conversion from `node_1 ~ node_2` to `UnDiEdge`. * * @author Peter Empen */ object UnDiEdge extends EdgeCompanion[UnDiEdge] { def apply[N] (node_1: N, node_2: N) = new UnDiEdge[N](NodeProduct(node_1, node_2)) def apply[N] (nodes: Tuple2[N,N]) = new UnDiEdge[N](nodes) protected[collection] def from [N](nodes: Product) = new UnDiEdge[N](nodes) def unapply[N](e: UnDiEdge[N]) = if (e eq null) None else Some(e._1, e._2) } /** $SHORTCUT `edge match {case n1 ~ n2 => f(n1, n2)}`. */ val ~ = UnDiEdge /** * Represents a directed edge (arc / arrow) connecting two nodes. * * @author Peter Empen */ @SerialVersionUID(53L) class DiEdge[+N] (nodes: Product) extends UnDiEdge [N](nodes) with DiEdgeLike[N] with EdgeCopy [DiEdge] with OuterEdge [N,DiEdge] { override protected[collection] def copy[NN](newNodes: Product) = new DiEdge[NN](newNodes) } /** * Factory for directed edges. * `GraphPredef` also supports implicit conversion from `node_1 ~> node_2` to `DiEdge`. * * @author Peter Empen */ object DiEdge extends EdgeCompanion[DiEdge] { def apply[N](from: N, to: N) = new DiEdge[N](NodeProduct(from, to)) def apply[N](nodes: Tuple2[N,N]) = new DiEdge[N](nodes) protected[collection] def from [N](nodes: Product) = new DiEdge[N](nodes) def unapply[N](e: DiEdge[N]) = if (e eq null) None else Some(e.source, e.target) } /** $SHORTCUT `edge match {case source ~> target => f(source, target)}`. */ val ~> = DiEdge }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy