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

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

The newest version!
package scalax.collection

import scala.annotation.unchecked.{uncheckedVariance => uV}
import scala.collection.immutable.Iterable
import scala.reflect.ClassTag

import scalax.collection.generic._
import scalax.collection.generic.Factory
import scalax.collection.config.GraphConfig
import scalax.collection.edges.{DiEdge, UnDiEdge}
import scalax.collection.hyperedges.{ordered, DiHyperEdge, HyperEdge}
import scalax.collection.mutable.{Builder, EqHashMap}

/** A template trait for graphs.
  *
  * This trait provides the common structure and operations of immutable graphs independently
  * of their representation.
  *
  * If `E` inherits `DiHyperEdgeLike` the graph is directed, otherwise it is undirected or mixed.
  *
  * @tparam N    the user type of the nodes (vertices) in this graph.
  * @tparam E    the type of the edges in this graph.
  * @tparam This the higher kinded type of the graph itself.
  *
  * @define REIMPLFACTORY Note that this method must be reimplemented in each module
  *         having its own factory methods such as `constrained` does.
  * @define CONTGRAPH The `Graph` instance that contains `this`
  * @author Peter Empen
  */
trait GraphLike[N, E <: Edge[N], +CC[X, Y <: Edge[X]] <: GraphLike[X, Y, CC] with AnyGraph[X, Y]]
    extends GraphBase[N, E, CC]
    with GraphTraversal[N, E]
    with GraphDegree[N, E, CC]
    with ToString[N, E, CC] {
  thisGraph: CC[N, E] =>

  protected type ThisGraph = thisGraph.type

  def empty: CC[N, E]
  protected[this] def newBuilder: mutable.Builder[N, E, CC]

  def isDirected: Boolean = edges.nonEmpty && edges.hasOnlyDiEdges
  def isHyper: Boolean    = edges.nonEmpty && edges.hasAnyHyperEdge
  def isMixed: Boolean    = edges.nonEmpty && edges.hasMixedEdges
  def isMulti: Boolean    = edges.nonEmpty && edges.hasAnyMultiEdge

  /** The companion object of `CC`. */
  val companion: Factory[CC]
  implicit def config: GraphConfig

  /** `Graph` instances are equal if their nodes and edges turned
    * to outer nodes and outer edges are equal. Any `TraversableOnce`
    * instance may also be equal to this graph if its set representation
    * contains equalling outer nodes and outer edges. Thus the following
    * expressions hold:
    * {{{
    * Graph(1~2, 3) == List(1~2, 3)
    * Graph(1~2, 3) == List(1, 2, 2, 3, 2~1)
    * }}}
    * The first test is `false` because of the failing nodes `1` and `2`.
    * The second is true because of duplicate elimination and undirected edge equivalence.
    */
  override def equals(that: Any): Boolean = that match {
    case that: AnyGraph[N, E] @unchecked =>
      (this eq that) ||
      this.order == that.order &&
      this.size == that.size && {
        val thatNodes = that.nodes.toOuter
        try this.nodes forall (thisN => thatNodes(thisN.outer))
        catch { case _: ClassCastException => false }
      } && {
        val thatEdges = that.edges.toOuter
        try this.edges forall (thisE => thatEdges(thisE.outer))
        catch { case _: ClassCastException => false }
      }
    case that: IterableOnce[_] =>
      val thatSet = that.iterator.to(Set)
      this.elementCount == thatSet.size && {
        val thatNodes = thatSet.asInstanceOf[Set[N]]
        try this.nodes forall (thisN => thatNodes(thisN.outer))
        catch { case _: ClassCastException => false }
      } && {
        val thatEdges = thatSet.asInstanceOf[Set[E]]
        try this.edges forall (thisE => thatEdges(thisE.outer))
        catch { case _: ClassCastException => false }
      }
    case _ =>
      false
  }

  type NodeT <: GraphInnerNode
  trait GraphInnerNode extends BaseInnerNode with TraverserInnerNode { this: NodeT =>

    /** $CONTGRAPH inner edge. */
    final def containingGraph: ThisGraph = thisGraph
  }

  protected trait NodeBase extends BaseNodeBase with GraphInnerNode {
    this: NodeT =>
    final def isContaining[N, E <: Edge[N]](g: GraphBase[N, E, CC] @uV): Boolean =
      g eq containingGraph
  }

  type NodeSetT <: GraphNodeSet
  trait GraphNodeSet extends NodeSet with NodeSetToString {
    protected def copy: NodeSetT

    final override def -(node: NodeT): NodeSetT =
      if (this contains node) { val c = copy; c minus node; c }
      else this.asInstanceOf[NodeSetT]

    /** removes `node` from this node set leaving the edge set unchanged.
      *
      * @param node the node to be removed from the node set.
      */
    protected def minus(node: NodeT): Unit

    /** removes `node` either rippling or gently.
      *
      * @param node the node to be subtracted
      * @param rippleDelete if `true`, `node` will be deleted with its incident edges;
      *        otherwise `node` will be only deleted if it has no incident edges or
      *        all its incident edges are hooks.
      * @param minusNode implementation of node removal without considering incident edges.
      * @param minusEdges implementation of removal of all incident edges.
      * @return `true` if `node` has been removed.
      */
    final protected[collection] def subtract(
        node: NodeT,
        rippleDelete: Boolean,
        minusNode: (NodeT) => Unit,
        minusEdges: (NodeT) => Unit
    ): Boolean = {
      def minusNodeTrue = { minusNode(node); true }
      def minusAllTrue  = { minusEdges(node); minusNodeTrue }
      if (this contains node)
        if (node.edges.isEmpty) minusNodeTrue
        else if (rippleDelete) minusAllTrue
        else if (node.hasOnlyHooks) minusAllTrue
        else handleNotGentlyRemovable
      else false
    }
    protected def handleNotGentlyRemovable = false
  }

  trait GraphInnerEdge extends BaseInnerEdge {
    this: EdgeT =>

    /** $CONTGRAPH inner edge. */
    final def containingGraph: ThisGraph = thisGraph

    final protected[collection] def asEdgeT: EdgeT = this
  }

  type EdgeT = GraphInnerEdge

  import scala.{SerialVersionUID => S}
  // format: off
  @S(-901) class InnerHyperEdge       (val ends: Several[NodeT], val outer: E) extends Abstract.HyperEdge       (ends) with EdgeT
  @S(-902) class InnerOrderedHyperEdge(val ends: Several[NodeT], val outer: E) extends Abstract.OrderedHyperEdge(ends) with EdgeT

  @S(-903) class InnerDiHyperEdge       (override val sources: OneOrMore[NodeT], override val targets: OneOrMore[NodeT], val outer: E) extends Abstract.DiHyperEdge       (sources, targets) with EdgeT
  @S(-904) class InnerOrderedDiHyperEdge(override val sources: OneOrMore[NodeT], override val targets: OneOrMore[NodeT], val outer: E) extends Abstract.OrderedDiHyperEdge(sources, targets) with EdgeT

  @S(-905) class InnerUnDiEdge(val source: NodeT, val target: NodeT, val outer: E) extends Abstract.UnDiEdge(source, target) with EdgeT
  @S(-906) class InnerDiEdge  (val source: NodeT, val target: NodeT, val outer: E) extends Abstract.DiEdge  (source, target) with EdgeT

  @transient object InnerHyperEdge        { def unapply(edge:        InnerHyperEdge): Option[Several[NodeT]] = Some(edge.ends) }
  @transient object InnerOrderedHyperEdge { def unapply(edge: InnerOrderedHyperEdge): Option[Several[NodeT]] = Some(edge.ends) }

  @transient object InnerDiHyperEdge        { def unapply(edge:        InnerDiHyperEdge): Option[(OneOrMore[NodeT], OneOrMore[NodeT])] = Some(edge.sources, edge.targets) }
  @transient object InnerOrderedDiHyperEdge { def unapply(edge: InnerOrderedDiHyperEdge): Option[(OneOrMore[NodeT], OneOrMore[NodeT])] = Some(edge.sources, edge.targets) }

  @transient object InnerUnDiEdge { def unapply(edge: InnerUnDiEdge): Option[(NodeT, NodeT)] = Some(edge.source, edge.target) }
  @transient object InnerDiEdge   { def unapply(edge:   InnerDiEdge): Option[(NodeT, NodeT)] = Some(edge.source, edge.target) }
  // format: on

  final override protected def newHyperEdge(outer: E, nodes: Several[NodeT]): EdgeT = outer match {
    case _: AnyHyperEdge[N] with OrderedEndpoints => new InnerOrderedHyperEdge(nodes, outer)
    case _: AnyHyperEdge[N]                       => new InnerHyperEdge(nodes, outer)
    case e                                        => throw new MatchError(s"Unexpected HyperEdge $e")
  }
  protected def newDiHyperEdge(outer: E, sources: OneOrMore[NodeT], targets: OneOrMore[NodeT]): EdgeT = outer match {
    case _: AnyDiHyperEdge[N] with OrderedEndpoints => new InnerOrderedDiHyperEdge(sources, targets, outer)
    case _: AnyDiHyperEdge[N]                       => new InnerDiHyperEdge(sources, targets, outer)
    case e                                          => throw new MatchError(s"Unexpected DiHyperEdge $e")
  }
  protected def newEdge(outer: E, node_1: NodeT, node_2: NodeT): EdgeT = outer match {
    case _: AnyDiEdge[N]   => new InnerDiEdge(node_1, node_2, outer)
    case _: AnyUnDiEdge[N] => new InnerUnDiEdge(node_1, node_2, outer)
    case e                 => throw new MatchError(s"Unexpected Edge $e")
  }

  type EdgeSetT <: GraphEdgeSet
  trait GraphEdgeSet extends EdgeSet with EdgeSetToString {
    def hasOnlyDiEdges: Boolean
    def hasOnlyUnDiEdges: Boolean
    def hasMixedEdges: Boolean
    def hasAnyHyperEdge: Boolean
    def hasAnyMultiEdge: Boolean
  }

  final def contains(node: N): Boolean = nodes contains newNode(node)
  final def contains(edge: E): Boolean = edges contains newHyperEdge(edge, edge.ends map newNode)

  def iterator: Iterator[InnerElem] = nodes.iterator ++ edges.iterator

  def toIterable: Iterable[InnerElem] = new Iterable[InnerElem] {
    def iterator: Iterator[InnerElem] = thisGraph.iterator
  }

  def outerIterator: Iterator[OuterElem] =
    nodes.iterator.map[OuterElem](n => OuterNode(n.outer)) ++
      edges.iterator.map[OuterElem](e => OuterEdge(e.outer))

  def toOuterIterable: Iterable[OuterElem] = new Iterable[OuterElem] {
    def iterator: Iterator[OuterElem] = outerIterator
  }

  @inline final def find(node: N): Option[NodeT] = nodes find node
  @inline final def find(edge: E): Option[EdgeT] = edges find edge

  @inline final def get(node: N): NodeT = nodes get node
  @inline final def get(edge: E): EdgeT = edges.find(edge).get

  final def filter(nodeP: NodePredicate = anyNode, edgeP: EdgePredicate = anyEdge): CC[N, E] = {
    import scala.collection.Set

    def build(nodes: Set[NodeT]): CC[N, E] = {
      val b = companion.newBuilder[N, E]
      nodes foreach { case InnerNode(innerN, outerN) =>
        b addOne outerN
        innerN.edges foreach { case InnerEdge(innerE, outerE) =>
          if (edgeP(innerE) && (innerE.ends forall nodes.contains)) b += outerE
        }
      }
      b.result
    }
    (nodeP, edgeP) match {
      case (`anyNode`, `anyEdge`) => this
      case (`anyNode`, _)         => build(nodes)
      case (fN, _)                => build(nodes filter fN)
    }
  }

  final def map[NN, EC[X] <: Edge[X]](
      fNode: NodeT => NN
  )(implicit w1: E <:< GenericMapper, w2: EC[N] =:= E, t: ClassTag[EC[NN]]): CC[NN, EC[NN]] =
    mapNodes[NN, EC[NN]](fNode) match {
      case (nMap, builder) => map(nMap, builder)
    }

  private def map[NN, EC[X] <: Edge[X]](
      nMap: EqHashMap[NodeT, NN],
      builder: Builder[NN, EC[NN], CC @uV]
  )(implicit w1: E <:< GenericMapper, w2: EC[N] =:= E, t: ClassTag[EC[NN]]): CC[NN, EC[NN]] = {
    def validate[E <: Edge[NN]](e: E): Option[EC[NN]] = e match {
      case e if t.runtimeClass.isInstance(e) => Some(e.asInstanceOf[EC[NN]])
      case _                                 => None
    }
    edges foreach {
      case InnerEdge(AnyEdge(n1: NodeT @unchecked, n2: NodeT @unchecked), outer) =>
        def fallback[A]: (A, A) => AnyEdge[A] = outer match {
          case _: AnyDiEdge[N] => DiEdge.apply
          case _               => UnDiEdge.apply
        }

        (nMap get n1, nMap get n2) match {
          case (Some(nn1), Some(nn2)) =>
            outer match {
              case gM: GenericEdgeMapper[EC @unchecked] => builder += gM.map(nn1, nn2)
              case pM: PartialEdgeMapper[EC[NN] @unchecked] =>
                pM.map[NN].lift(nn1, nn2).fold(validate(fallback(nn1, nn2)))(Some(_)).map(builder += _)
            }
          case _ =>
        }
      case InnerEdge(
            AnyDiHyperEdge(sources: OneOrMore[NodeT] @unchecked, targets: OneOrMore[NodeT] @unchecked),
            outer
          ) =>
        def fallback[A]: (OneOrMore[A], OneOrMore[A]) => AnyDiHyperEdge[A] = outer match {
          case _: OrderedEndpoints => ordered.DiHyperEdge.apply
          case _                   => DiHyperEdge.apply
        }

        (sources.flatMapEither(nMap.get), targets.flatMapEither(nMap.get)) match {
          case (Right(newSources), Right(newTargets)) =>
            outer match {
              case gM: GenericDiHyperEdgeMapper[EC @unchecked] => builder += gM.map(newSources, newTargets)
              case pM: PartialDiHyperEdgeMapper[EC[NN] @unchecked] =>
                pM.map[NN]
                  .lift(newSources, newTargets)
                  .fold(validate(fallback(newSources, newTargets)))(Some(_))
                  .map(builder += _)
            }
          case _ =>
        }
      case InnerEdge(AnyHyperEdge(ends: Several[NodeT] @unchecked), outer) =>
        def fallback[A]: Several[A] => AnyHyperEdge[A] = outer match {
          case _: OrderedEndpoints => ordered.HyperEdge.apply
          case _                   => HyperEdge.apply
        }

        ends.flatMapEither(nMap.get) match {
          case Right(newEnds) =>
            outer match {
              case gM: GenericHyperEdgeMapper[EC @unchecked] => builder += gM.map(newEnds)
              case pM: PartialHyperEdgeMapper[EC[NN] @unchecked] =>
                pM.map[NN].lift(newEnds).fold(validate(fallback(newEnds)))(Some(_)).map(builder += _)
            }
          case _ =>
        }
    }
    builder.result
  }

  final def mapBound(fNode: NodeT => N)(implicit w1: E <:< PartialMapper): CC[N, E] =
    mapNodes[N, E](fNode) match {
      case (nMap, builder) => mapBound(nMap, builder)
    }

  private def mapBound(nMap: EqHashMap[NodeT, N], builder: Builder[N, E, CC @uV]): CC[N, E] = {
    edges foreach {
      case InnerEdge(AnyEdge(n1: NodeT @unchecked, n2: NodeT @unchecked), outer) =>
        (nMap get n1, nMap get n2) match {
          case (Some(nn1), Some(nn2)) =>
            outer match {
              case pM: PartialEdgeMapper[E @unchecked] => pM.map[N].lift(nn1, nn2).map(builder += _)
              case _                                   =>
            }
          case _ =>
        }
      case InnerEdge(
            AnyDiHyperEdge(sources: OneOrMore[NodeT] @unchecked, targets: OneOrMore[NodeT] @unchecked),
            outer
          ) =>
        (sources.flatMapEither(nMap.get), targets.flatMapEither(nMap.get)) match {
          case (Right(newSources), Right(newTargets)) =>
            outer match {
              case pM: PartialDiHyperEdgeMapper[E @unchecked] =>
                pM.map[N].lift(newSources, newTargets).map(builder += _)
              case _ =>
            }
          case _ =>
        }
      case InnerEdge(AnyHyperEdge(ends: Several[NodeT] @unchecked), outer) =>
        ends.flatMapEither(nMap.get) match {
          case Right(newEnds) =>
            outer match {
              case pM: PartialHyperEdgeMapper[E @unchecked] => pM.map[N].lift(newEnds).map(builder += _)
              case _                                        =>
            }
          case _ =>
        }
    }
    builder.result
  }

  private def mapNodes[NN, EE <: Edge[NN]](fNode: NodeT => NN): (EqHashMap[NodeT, NN], Builder[NN, EE, CC]) = {
    val nMap = EqHashMap.empty[NodeT, NN](order)
    val b    = companion.newBuilder[NN, EE](config)
    nodes foreach { n =>
      val nn = fNode(n)
      nMap put (n, nn)
      b += nn
    }
    (nMap, b)
  }

  private def flatMapNodesBiased[NN, EE <: Edge[NN]](
      fNode: NodeT => Seq[NN]
  ): (EqHashMap[NodeT, NN], Builder[NN, EE, CC]) = {
    val nMap = EqHashMap.empty[NodeT, NN](order)
    val b    = companion.newBuilder[NN, EE](config)
    nodes foreach { n =>
      val newNodes = fNode(n)
      newNodes.headOption.map { head =>
        var preserved: Option[NN] = None
        newNodes foreach { nn =>
          b += nn
          if (n.outer == nn) preserved = Some(nn)
        }
        nMap put (n, preserved.fold(head)(identity))
      }
    }
    (nMap, b)
  }

  private def flatMapNodes[NN, EE <: Edge[NN]](
      fNode: NodeT => Seq[NN]
  ): (EqHashMap[NodeT, Seq[NN]], Builder[NN, EE, CC]) = {
    val nMap = EqHashMap.empty[NodeT, Seq[NN]](order)
    val b    = companion.newBuilder[NN, EE](config)
    nodes foreach { n =>
      val newNodes = fNode(n)
      nMap put (n, newNodes)
      b ++= newNodes
    }
    (nMap, b)
  }

  final def map[NN, EC[X] <: Edge[X]](
      fNode: NodeT => NN,
      fEdge: (EdgeT, NN, NN) => EC[NN]
  )(implicit
      w: E <:< AnyEdge[N]
  ): CC[NN, EC[NN]] =
    mapBound(fNode, fEdge)

  final def mapBound[NN, EC <: Edge[NN]](
      fNode: NodeT => NN,
      fEdge: (EdgeT, NN, NN) => EC
  )(implicit w: E <:< AnyEdge[N]): CC[NN, EC] =
    mapNodes[NN, EC](fNode) match {
      case (nMap, builder) =>
        edges foreach { case e @ InnerEdge(AnyEdge(n1: NodeT @unchecked, n2: NodeT @unchecked), _) =>
          builder += fEdge(e, nMap(n1), nMap(n2))
        }
        builder.result
    }

  final def mapHyper[NN, EC[X] <: Edge[X]](
      fNode: NodeT => NN,
      fHyperEdge: (EdgeT, Several[NN]) => EC[NN],
      fDiHyperEdge: Option[(EdgeT, OneOrMore[NN], OneOrMore[NN]) => EC[NN]],
      fEdge: Option[(EdgeT, NN, NN) => EC[NN]]
  )(implicit w: E <:< AnyHyperEdge[N]): CC[NN, EC[NN]] =
    mapHyperBound(fNode, fHyperEdge, fDiHyperEdge, fEdge)

  final def mapHyperBound[NN, EC <: Edge[NN]](
      fNode: NodeT => NN,
      fHyperEdge: (EdgeT, Several[NN]) => EC,
      fDiHyperEdge: Option[(EdgeT, OneOrMore[NN], OneOrMore[NN]) => EC],
      fEdge: Option[(EdgeT, NN, NN) => EC]
  )(implicit w: E <:< AnyHyperEdge[N]): CC[NN, EC] =
    mapNodes[NN, EC](fNode) match {
      case (nMap, builder) =>
        edges foreach {
          case e @ InnerEdge(AnyEdge(n1: NodeT @unchecked, n2: NodeT @unchecked), _) =>
            fEdge.fold {
              builder += fHyperEdge(e, Several(nMap(n1), nMap(n2)))
            } { f =>
              builder += f(e, nMap(n1), nMap(n2))
            }
          case e @ InnerEdge(
                AnyDiHyperEdge(sources: OneOrMore[NodeT @unchecked], targets: OneOrMore[NodeT @unchecked]),
                _
              ) =>
            fDiHyperEdge.fold {
              builder += fHyperEdge(e, Several.fromUnsafe(sources.map(nMap).iterator ++ targets.map(nMap).iterator))
            } { f =>
              builder += f(e, sources.map(nMap), targets.map(nMap))
            }
          case e @ InnerEdge(AnyHyperEdge(ends: Several[NodeT @unchecked]), _) =>
            builder += fHyperEdge(e, ends.map(nMap))
        }
        builder.result
    }

  def mapDiHyper[NN, EC[X] <: Edge[X]](
      fNode: NodeT => NN,
      fDiHyperEdge: (EdgeT, OneOrMore[NN], OneOrMore[NN]) => EC[NN],
      fEdge: Option[(EdgeT, NN, NN) => EC[NN]]
  )(implicit w: E <:< AnyDiHyperEdge[N]): CC[NN, EC[NN]] =
    mapDiHyperBound(fNode, fDiHyperEdge, fEdge)

  def mapDiHyperBound[NN, EC <: Edge[NN]](
      fNode: NodeT => NN,
      fDiHyperEdge: (EdgeT, OneOrMore[NN], OneOrMore[NN]) => EC,
      fEdge: Option[(EdgeT, NN, NN) => EC]
  )(implicit w: E <:< AnyDiHyperEdge[N]): CC[NN, EC] =
    mapNodes[NN, EC](fNode) match {
      case (nMap, builder) =>
        edges foreach {
          case e @ InnerEdge(AnyEdge(n1: NodeT @unchecked, n2: NodeT @unchecked), _) =>
            import OneOrMore.one
            fEdge.fold {
              builder += fDiHyperEdge(e, one(nMap(n1)), one(nMap(n2)))
            } { f =>
              builder += f(e, nMap(n1), nMap(n2))
            }
          case e @ InnerEdge(
                AnyDiHyperEdge(sources: OneOrMore[NodeT @unchecked], targets: OneOrMore[NodeT @unchecked]),
                _
              ) =>
            builder += fDiHyperEdge(e, sources.map(nMap), targets.map(nMap))
        }
        builder.result
    }

  final def flatMap[NN, EC[X] <: Edge[X]](
      fNode: NodeT => Seq[NN]
  )(implicit w1: E <:< GenericMapper, w2: EC[N] =:= E, t: ClassTag[EC[NN]]): CC[NN, EC[NN]] =
    flatMapNodesBiased[NN, EC[NN]](fNode) match {
      case (nMap, builder) => map(nMap, builder)
    }

  final def flatMapBound(fNode: NodeT => Seq[N])(implicit w1: E <:< PartialMapper): CC[N, E] =
    flatMapNodesBiased[N, E](fNode) match {
      case (nMap, builder) => mapBound(nMap, builder)
    }

  final def flatMap[NN, EC[X] <: Edge[X]](
      fNode: NodeT => Seq[NN],
      fEdge: (EdgeT, Seq[NN], Seq[NN]) => Seq[EC[NN]]
  )(implicit w: E <:< AnyEdge[N]): CC[NN, EC[NN]] =
    flatMapBound(fNode, fEdge)

  final def flatMapBound[NN, EC <: Edge[NN]](
      fNode: NodeT => Seq[NN],
      fEdge: (EdgeT, Seq[NN], Seq[NN]) => Seq[EC]
  )(implicit w: E <:< AnyEdge[N]): CC[NN, EC] =
    flatMapNodes[NN, EC](fNode) match {
      case (nMap, builder) =>
        edges foreach { case e @ InnerEdge(AnyEdge(n1: NodeT @unchecked, n2: NodeT @unchecked), _) =>
          builder ++= (edges = fEdge(e, nMap(n1), nMap(n2)))
        }
        builder.result
    }

  final def flatMapHyper[NN, EC[X] <: Edge[X]](
      fNode: NodeT => Seq[NN],
      fHyperEdge: (EdgeT, Seq[NN]) => Seq[EC[NN]],
      fDiHyperEdge: Option[(EdgeT, Seq[NN], Seq[NN]) => Seq[EC[NN]]],
      fEdge: Option[(EdgeT, Seq[NN], Seq[NN]) => Seq[EC[NN]]]
  )(implicit w: E <:< AnyHyperEdge[N]): CC[NN, EC[NN]] =
    flatMapHyperBound(fNode, fHyperEdge, fDiHyperEdge, fEdge)

  final def flatMapHyperBound[NN, EC <: Edge[NN]](
      fNode: NodeT => Seq[NN],
      fHyperEdge: (EdgeT, Seq[NN]) => Seq[EC],
      fDiHyperEdge: Option[(EdgeT, Seq[NN], Seq[NN]) => Seq[EC]],
      fEdge: Option[(EdgeT, Seq[NN], Seq[NN]) => Seq[EC]]
  )(implicit w: E <:< AnyHyperEdge[N]): CC[NN, EC] =
    flatMapNodes[NN, EC](fNode) match {
      case (nMap, builder) =>
        edges foreach {
          case e @ InnerEdge(AnyEdge(n1: NodeT @unchecked, n2: NodeT @unchecked), _) =>
            val nn1s = nMap(n1)
            val nn2s = nMap(n2)
            builder ++= (edges = fEdge.fold(fHyperEdge(e, nn1s ++ nn2s))(_(e, nn1s, nn2s)))

          case e @ InnerEdge(
                AnyDiHyperEdge(sources: OneOrMore[NodeT @unchecked], targets: OneOrMore[NodeT @unchecked]),
                _
              ) =>
            val newSources = sources.flatMap(nMap)
            val newTargets = targets.flatMap(nMap)
            builder ++= (edges =
              fDiHyperEdge.fold(fHyperEdge(e, newSources ++ newTargets))(_(e, newSources, newTargets))
            )

          case e @ InnerEdge(AnyHyperEdge(ends: Several[NodeT @unchecked]), _) =>
            builder ++= (edges = fHyperEdge(e, ends.flatMap(nMap)))
        }
        builder.result
    }

  final def flatMapDiHyper[NN, EC[X] <: Edge[X]](
      fNode: NodeT => Seq[NN],
      fDiHyperEdge: (EdgeT, Seq[NN], Seq[NN]) => Seq[EC[NN]],
      fEdge: Option[(EdgeT, Seq[NN], Seq[NN]) => Seq[EC[NN]]]
  )(implicit w: E <:< AnyDiHyperEdge[N]): CC[NN, EC[NN]] =
    flatMapDiHyperBound(fNode, fDiHyperEdge, fEdge)

  final def flatMapDiHyperBound[NN, EC <: Edge[NN]](
      fNode: NodeT => Seq[NN],
      fDiHyperEdge: (EdgeT, Seq[NN], Seq[NN]) => Seq[EC],
      fEdge: Option[(EdgeT, Seq[NN], Seq[NN]) => Seq[EC]]
  )(implicit w: E <:< AnyDiHyperEdge[N]): CC[NN, EC] =
    flatMapNodes[NN, EC](fNode) match {
      case (nMap, builder) =>
        edges foreach {
          case e @ InnerEdge(AnyEdge(n1: NodeT @unchecked, n2: NodeT @unchecked), _) =>
            val nn1s = nMap(n1)
            val nn2s = nMap(n2)
            builder ++= (edges = fEdge.fold(fDiHyperEdge(e, nn1s, nn2s))(_(e, nn1s, nn2s)))

          case e @ InnerEdge(
                AnyDiHyperEdge(sources: OneOrMore[NodeT @unchecked], targets: OneOrMore[NodeT @unchecked]),
                _
              ) =>
            val newSources = sources.flatMap(nMap)
            val newTargets = targets.flatMap(nMap)
            builder ++= (edges = fDiHyperEdge(e, newSources, newTargets))
        }
        builder.result
    }

  def foldLeft[B](z: B)(opNode: (B, NodeT) => B, opEdge: (B, EdgeT) => B): B =
    edges.foldLeft(nodes.foldLeft(z)(opNode))(opEdge)
}

/** Bundled functionality for mutable and immutable graphs alike.
  *
  * @tparam N the type of the nodes (vertices) in this graph.
  * @tparam E the type of the edges in this graph.
  */
trait AnyGraph[N, E <: Edge[N]] extends GraphLike[N, E, AnyGraph]




© 2015 - 2024 Weber Informatics LLC | Privacy Policy