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

scalax.collection.generic.edgeBase.scala Maven / Gradle / Ivy

The newest version!
package scalax.collection
package generic

import scala.annotation.unchecked.{uncheckedVariance => uV}
import scala.annotation.{switch, tailrec}
import scala.collection.immutable.Iterable

/** Base template for all edges in a `Graph`.
  *
  * Library-provided edges are immutable for both mutable and immutable `Graph`.
  * However, when using mutable graphs with labeled edges where labels are mutable with respect to your use case,
  * you might want to make your label mutable accordingly. Otherwise a much less efficient edge replacement
  * would be necessary.
  *
  * @tparam N the 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 Edge[+N] extends Equals {

  /** The endpoints of this edge, in other words the nodes this edge connects. */
  def ends: Several[N]

  /** The first node of this edge. */
  def node1: N

  /** The second node of this edge. */
  def node2: N

  /** The n'th node of this edge.
    * @throws IllegalArgumentException if `n` does not meet `0 <= n < arity`.
    */
  def node(n: Int): N

  /** Number of the endpoints of this edge. At least two nodes are joined.
    * In case of a hook, the two nodes are identical.
    * Hyperedges may link more than two nodes.
    */
  def arity: Int

  protected def customMsgPrefix: String = "Custom validation failed"

  /** Whether this edge is directed. */
  def isDirected: Boolean

  /** Whether this edge is undirected. */
  @inline final def isUndirected: Boolean = !isDirected

  /** Whether this edge's type is hyperedge meaning that it may have more than two ends. */
  def isHyperEdge: Boolean

  /** Whether this edge has exactly two ends. */
  @inline final def nonHyperEdge: Boolean = !isHyperEdge

  /** Whether 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 any  of the targets is included in the sources.
    *  In case of an undirected hyperedge, a loop is given if any of the ends is a duplicate.
    */
  def isLooping: Boolean

  /** Same as `! looping`. */
  @inline final def nonLooping: Boolean = !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 define a custom edge class
    * that mixes in `ExtendedKey` and adds `weight` to `keyAttributes`.
    */
  def weight: Double = 1

  def isLabeled: Boolean = false

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

  /** All source ends of this edge. */
  def sources: OneOrMore[N]

  /** All target ends of this edge. */
  def targets: OneOrMore[N]

  /** `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 any memory allocation. */
  def withSources[U](f: N => U): Unit

  /** Applies `f` to the target ends of this edge without any memory allocation. */
  def withTargets[U](f: N => U): Unit

  /** `true` if
    *  a. being an undirected edge, both `n1` and `n2` are at this edge
    *  a. being a directed edge, `n1` is a source and `n2` a target of this 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[Edge[_]] } object Edge { def unapply[N](e: Edge[N]): Some[Edge[Any]] = Some(e) } private[collection] trait InnerEdgeLike[+N] extends Edge[N] /** Marker trait for companion objects of any non-labeled edge. */ sealed trait EdgeCompanionBase extends Serializable /** Template for companion objects of generic edges. */ trait EdgeCompanion[+E[N] <: Edge[N]] extends EdgeCompanionBase { def apply[N](node_1: N, node_2: N): E[N] implicit def thisCompanion: this.type = this } trait AnyHyperEdge[+N] extends Edge[N] with EqHyper { def node1: N = ends.head def node2: N = ends(1) def node(n: Int): N = ends(n) def arity: Int = ends.size def isDirected = false def isHyperEdge = true def isLooping: Boolean = ends.iterator.toSet.size < arity override def sources: OneOrMore[N] = OneOrMore.fromUnsafe(ends.iterator) override def targets: OneOrMore[N] = sources override def isAt[M >: N](node: M): Boolean = ends contains node override def isAt(pred: N => Boolean): Boolean = ends exists pred override def hasSource[M >: N](node: M): Boolean = isAt(node) override def hasSource(pred: N => Boolean): Boolean = isAt(pred) override def hasTarget[M >: N](node: M): Boolean = isAt(node) override def hasTarget(pred: N => Boolean): Boolean = isAt(pred) def withSources[U](f: N => U): Unit = sources.iterator foreach f def withTargets[U](f: N => U): Unit = targets.iterator foreach f final protected def matches(fList: List[N => Boolean]): Boolean = { val it = ends.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)) } object AnyHyperEdge { def unapply[N](hE: AnyHyperEdge[N]): Some[Several[N]] = Some(hE.ends) } abstract class AbstractHyperEdge[+N](val ends: Several[N]) extends AnyHyperEdge[N] object AbstractHyperEdge { def unapply[N](e: AbstractHyperEdge[N]): Some[Several[N]] = Some(e.ends) } abstract class AbstractGenericHyperEdge[+N, +CC[X] <: AbstractGenericHyperEdge[X, CC]](ends: Several[N]) extends AbstractHyperEdge[N](ends) with GenericHyperEdgeMapper[CC] abstract class AbstractUnlabeledGenericHyperEdge[+N, +CC[X] <: AbstractUnlabeledGenericHyperEdge[X, CC]]( ends: Several[N] ) extends AbstractHyperEdge[N](ends) with GenericUnlabeledHyperEdgeMapper[CC] /** The abstract methods of this trait must be implemented by companion objects of non-labeled hyperedges. */ trait HyperEdgeCompanion[+E[N] <: AbstractHyperEdge[N]] extends EdgeCompanionBase { def apply[N](ends: Several[N]): E[N] def apply[N](node_1: N, node_2: N, moreNodes: N*): E[N] = apply(new Several(node_1, node_2, moreNodes)) final def unapply[N](edge: E[N] @uV): Some[Several[N]] = Some(edge.ends) /** `Some` hyperedge if `ends` contains at least two elements, otherwise `None`. */ final def from[N](iterable: Iterable[N]): Option[E[N]] = Several.from(iterable) map (ends => apply(ends)) /** A hyperedge with these `ends`. * @throws IllegalArgumentException if `ends` has not at least two elements. */ final def fromUnsafe[N](iterable: Iterable[N]): E[N] = apply(Several.fromUnsafe(iterable)) implicit def thisCompanion: this.type = this } trait AnyDiHyperEdge[+N] extends AnyHyperEdge[N] with EqDiHyper { override def node1: N = sources.head override def ends: Several[N] = Several.fromUnsafe(sources.iterator ++ targets.iterator) override def arity: Int = sources.size + targets.size @inline final override def isDirected = true override def isLooping: Boolean = targets exists sources.contains override def hasSource[M >: N](node: M): Boolean = sources exists (_ == node) override def hasSource(pred: N => Boolean): Boolean = sources exists pred override def hasTarget[M >: N](node: M): Boolean = targets exists (_ == node) override def hasTarget(pred: N => Boolean): Boolean = targets exists pred override def matches[M >: N](n1: M, n2: M): Boolean = sources.exists(_ == n1) && targets.exists(_ == n2) override def matches(p1: N => Boolean, p2: N => Boolean): Boolean = (sources exists p1) && (targets exists p2) } object AnyDiHyperEdge { def unapply[N](diH: AnyDiHyperEdge[N]): Some[(OneOrMore[N], OneOrMore[N])] = Some(diH.sources, diH.targets) } abstract class AbstractDiHyperEdge[+N](override val sources: OneOrMore[N], override val targets: OneOrMore[N]) extends AnyDiHyperEdge[N] object AbstractDiHyperEdge { def unapply[N](e: AbstractDiHyperEdge[N]): Some[(OneOrMore[N], OneOrMore[N])] = Some(e.sources, e.targets) } abstract class AbstractGenericDiHyperEdge[+N, +CC[X] <: AbstractGenericDiHyperEdge[X, CC]]( sources: OneOrMore[N], targets: OneOrMore[N] ) extends AbstractDiHyperEdge[N](sources, targets) with GenericDiHyperEdgeMapper[CC] abstract class AbstractGenericUnlabeledDiHyperEdge[+N, +CC[X] <: AbstractGenericUnlabeledDiHyperEdge[X, CC]]( sources: OneOrMore[N], targets: OneOrMore[N] ) extends AbstractDiHyperEdge[N](sources, targets) with GenericUnlabeledDiHyperEdgeMapper[CC] /** The abstract methods of this trait must be implemented by companion objects of directed, non-labeled hyperedges. */ trait DiHyperEdgeCompanion[+E[N] <: AbstractDiHyperEdge[N]] extends EdgeCompanionBase { def apply[N](sources: OneOrMore[N], targets: OneOrMore[N]): E[N] def apply[N](source_1: N, moreSources: N*)(target_1: N, moreTargets: N*): E[N] = apply(OneOrMore(source_1, moreSources: _*), OneOrMore(target_1, moreTargets: _*)) def unapply[N](edge: E[N] @uV): Some[(OneOrMore[N], OneOrMore[N])] = Some(edge.sources, edge.targets) /** `Some` directed hyperedge if `sources` and `targets` have at least one element each, otherwise `None`. */ final def from[N](sources: Iterable[N], targets: Iterable[N]): Option[E[N]] = (OneOrMore.from(sources), OneOrMore.from(targets)) match { case (Some(s), Some(t)) => Some(apply(s, t)) case _ => None } /** A directed hyperedge with the supplied `sources` and `targets`. * @throws IllegalArgumentException if `sources` or `targets` is empty. */ final def fromUnsafe[N](sources: Iterable[N], targets: Iterable[N]): E[N] = apply(OneOrMore.fromUnsafe(sources), OneOrMore.fromUnsafe(targets)) } trait AnyEdge[+N] extends Edge[N] { final override def node(n: Int): N = (n: @switch) match { case 0 => node1 case 1 => node2 case _ => throw new NoSuchElementException } @inline final override def arity: Int = 2 @inline final override def isHyperEdge = false def isLooping: Boolean = node1 == node2 def ends: Several[N] = Several(node1, node2) @inline final override def isAt[M >: N](node: M): Boolean = this.node1 == node || this.node2 == node @inline final override def isAt(pred: N => Boolean): Boolean = pred(this.node1) || pred(this.node2) } object AnyEdge { def unapply[N](e: AnyEdge[N]): Some[(N, N)] = Some(e.node1, e.node2) } trait AnyUnDiEdge[+N] extends AnyHyperEdge[N] with AnyEdge[N] with EqUnDi[N] { def source: N def target: N @inline final override def sources: OneOrMore[N] = OneOrMore(source, target) @inline final override def targets: OneOrMore[N] = sources @inline final override def node1: N = source @inline final override def node2: N = target final override def isLooping: Boolean = super[AnyEdge].isLooping final override def hasSource[M >: N](node: M): Boolean = source == node || target == node final override def hasSource(pred: N => Boolean): Boolean = pred(source) || pred(target) final override def hasTarget[M >: N](node: M): Boolean = hasSource(node) final override def hasTarget(pred: N => Boolean): Boolean = hasSource(pred) final override def withSources[U](f: N => U): Unit = { f(source); f(target) } final override def withTargets[U](f: N => U): Unit = withSources(f) override def isDirected = false 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.node1) && p2(this.node2) || p1(this.node2) && p2(this.node1) } object AnyUnDiEdge { def unapply[N](e: AnyUnDiEdge[N]) = Some(e) } abstract class AbstractUnDiEdge[+N](val source: N, val target: N) extends AnyUnDiEdge[N] trait AbstractGenericUnDiEdge[+N, +CC[X] <: AbstractGenericUnDiEdge[X, CC]] extends AnyUnDiEdge[N] with GenericEdgeMapper[CC] trait AbstractGenericUnlabeledUnDiEdge[+N, +CC[X] <: AbstractGenericUnlabeledUnDiEdge[X, CC]] extends AnyUnDiEdge[N] with GenericUnlabeledEdgeMapper[CC] trait AnyDiEdge[+N] extends AnyDiHyperEdge[N] with AnyEdge[N] with EqDi[N] { final override def ends: Several[N] = super[AnyEdge].ends def source: N def target: N @inline final override def node1: N = source @inline final override def node2: N = target final override def isLooping: Boolean = super[AnyEdge].isLooping final override def sources: OneOrMore[N] = OneOrMore(source) final override def targets: OneOrMore[N] = OneOrMore(target) final override def hasSource[M >: N](node: M): Boolean = source == node final override def hasSource(pred: N => Boolean): Boolean = pred(source) final override def hasTarget[M >: N](node: M): Boolean = target == node final override def hasTarget(pred: N => Boolean): Boolean = pred(target) final override def withSources[U](f: N => U): Unit = f(source) final override def withTargets[U](f: N => U): Unit = f(target) final override def matches[M >: N](n1: M, n2: M): Boolean = diBaseEquals(n1, n2) final override def matches(p1: N => Boolean, p2: N => Boolean): Boolean = p1(source) && p2(target) } abstract class AbstractDiEdge[+N](val source: N, val target: N) extends AnyDiEdge[N] trait AbstractGenericDiEdge[+N, +CC[X] <: AbstractGenericDiEdge[X, CC]] extends AnyDiEdge[N] with GenericEdgeMapper[CC] trait AbstractGenericUnlabeledDiEdge[+N, +CC[X] <: AbstractGenericUnlabeledDiEdge[X, CC]] extends AnyDiEdge[N] with GenericUnlabeledEdgeMapper[CC] private[collection] object Abstract { abstract class HyperEdge[+N](ends: Several[N]) extends AnyHyperEdge[N] abstract class OrderedHyperEdge[+N](ends: Several[N]) extends AnyHyperEdge[N] with OrderedEndpoints abstract class DiHyperEdge[+N](sources: OneOrMore[N], targets: OneOrMore[N]) extends AnyDiHyperEdge[N] abstract class OrderedDiHyperEdge[+N](sources: OneOrMore[N], targets: OneOrMore[N]) extends AnyDiHyperEdge[N] with OrderedEndpoints abstract class UnDiEdge[+N](node_1: N, node_2: N) extends AnyUnDiEdge[N] abstract class DiEdge[+N](source: N, target: N) extends AnyDiEdge[N] }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy