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

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

The newest version!
package scalax.collection

import scala.reflect.ClassTag

import scalax.collection.generic._

/** Operations common to mutable and immutable graphs.
  *
  * @define mapGeneric You can call this flavor only if this graph's edge type is generic.
  * @define mapTyped   You can call this flavor only if this graph's edge type is typed.
  *
  * @define mapNodes Creates a new graph with nodes mapped by `fNode` and with an untouched edge structure otherwise.
  * @define mapEdges Creates a new graph with nodes and edges that are computed by the supplied mapping functions.
  *
  * @define flatMapNodes Creates a new graph with nodes returned by `fNode` and an edge structure that remains intact where possible.
  * @define flatMapEdges Creates a new graph with nodes and edges returned by `fNode` respectively `fEdge`.
  *
  * @define seeOverload See overload except the parameter
  *
  * @define fNode To apply to all nodes of this graph.
  *               Since the inner node is passed you can also examine the node context.
  *               Call `outer` to get the value of type `N` of the node.
  * @define fNodeFlat If `fNode` returns several new nodes with none equaling to the original node,
  *                   the first new node is accepted to be the result of the node transformation.
  *                   For more flexibility pass your own edge mapper to the overload.
  * @define fEdge To apply to all edges of this graph.
  *               This function is passed the current inner edge and its ends after being mapped by `fNode`.
  *               Since the inner edge is passed you can also examine its context.
  *               Call `outer` to get the outer edge of type E.
  * @define simplifiedFEdge has a simplified signature in this overload leaving out the inner edge.
  *                         This comes in handy whenever you don't need to inspect inner edges.
  * @define fEdgeOption To apply to any directed or undirected edge in this possibly mixed graph.
  *                     If not present simple edges will be mapped by the mandatory edge mapper you supply.
  *                     You are recommended supplying `Some` unless you know that the graph does not contain any simple edge.
  * @define fHyperEdge To apply to all hyperedges in this graph.
  *                    This function is passed the current inner hyperedge and its ends after being mapped by `fNode`.
  *                    Since the inner hyperedge is passed you can also examine its context.
  *                    Call `outer` to get the outer hyperedge of type E.
  * @define fDiHyperEdge To apply to all directed hyperedges in this graph.
  *                      This function is passed the existing inner directed hyperedge and its sources and targets after being mapped by `fNode`.
  *                      Since the inner directed hyperedge is passed you can also examine the edge context.
  *                      Call `outer` to get the outer directed hyperedge of type E.
  * @define fDiHyperEdgeOption To apply to any directed hyperedge in this possibly mixed graph.
  *                            If not present directed hyperedges will be mapped by the mandatory `fDiHyperEdge`.
  *                            You are recommended supplying `Some` unless you know that the graph does not contain any directed hyperedge.
  *
  * @define mapNN The node type of the resulting graph which may be unchanged or different from this graph's node type.
  * @define mapEC The higher kind of the generic edge type parameter of this graph.
  * @define mapECTyped The edge type parameter of this graph.
  *
  * @define mapReturn The mapped graph with possibly changed node and edge type parameters.
  * @define flatMapReturn A new graph of possibly changed node and edge types and of any new structure depending on your edge mapper(s).
  */
trait GraphOps[N, E <: Edge[N], +CC[X, Y <: Edge[X]]] extends OuterElems[N, E] {

  /** Whether this graph contains any node or any edge. */
  @inline final def isEmpty: Boolean = iterator.isEmpty

  /** Whether all edges of this graph are directed. */
  def isDirected: Boolean

  /** Whether this graph contains at least one hyperedge. */
  def isHyper: Boolean

  /** Whether this graph contains at least one directed and one undirected edge. */
  def isMixed: Boolean

  /** Whether this graph contains at least one multi-edge. We defnie multi-edges by
    *    a. two or more directed edges having the same source and target
    *    a. two or more undirected edges connecting the same nodes
    *    a. two or more (directed) hyperedges that, after being decomposed into (directed) edges,
    * yield any multy-edge as stipulated above.
    */
  def isMulti: Boolean

  /** `true` if this graph has at most 1 node. */
  @inline final def isTrivial: Boolean = order <= 1

  /** `true` if this graph has at least 2 nodes. */
  @inline final def nonTrivial: Boolean = !isTrivial

  /** The order, commonly referred to as |G|, of this graph equaling to the number of nodes. */
  def order: Int

  /** The size, commonly referred to as ||G||, of this graph equaling to the number of edges. */
  def size: Int

  /** The number of nodes and edges. */
  @inline final def elementCount: Int = order + size

  /** The Sum of the weight of all edges. */
  def totalWeight: Double

  sealed trait InnerElem

  type NodeT <: InnerNode
  trait InnerNode extends InnerElem {

    /** The outer node as supplied by instantiation or addition. */
    def outer: N

    protected[collection] def asNodeT: NodeT
  }
  object InnerNode {
    def unapply(node: InnerNode): Some[(NodeT, N)] = Some(node.asNodeT, node.outer)
  }

  type EdgeT <: InnerEdge
  trait InnerEdge extends InnerElem {

    /** The outer edge as supplied by instantiation or addition. */
    def outer: E

    protected[collection] def asEdgeT: EdgeT
  }
  object InnerEdge {
    def unapply(edge: InnerEdge): Some[(EdgeT, E)] = Some(edge.asEdgeT, edge.outer)
  }

  /** Iterator over all inner nodes and edges. */
  def iterator: Iterator[InnerElem]

  /** Iterable over all nodes and edges. */
  def toIterable: Iterable[InnerElem]

  /** Iterator over all inner nodes and edges. */
  def outerIterator: Iterator[OuterElem]

  /** Iterable over all nodes and edges. */
  def toOuterIterable: Iterable[OuterElem]

  /** Creates a new graph by adding all `edges` and `isolatedNodes` omitting duplicates.
    * The new graph is upcasted if any of the arguments is an upcast of `N` respectively `E`.
    * Use `union` to concatenate all nodes and edges of another graph.
    *
    * @param isolatedNodes to be concatenated. Nodes that are implicitly defined by any edge in `edges` will be ignored.
    * @param edges to be concatenated.
    */
  def concat[N2 >: N, E2 >: E <: Edge[N2]](isolatedNodes: IterableOnce[N2], edges: IterableOnce[E2])(implicit
      e: E2 <:< Edge[N2]
  ): CC[N2, E2]

  /** Same as `concat(isolatedNodes, edges)` but with empty `isolatedNodes`.
    * This method is useful if you don't need to pass any isolated node.
    */
  def concat[N2 >: N, E2 >: E <: Edge[N2]](edges: IterableOnce[E2])(implicit e: E2 <:< Edge[N2]): CC[N2, E2] =
    concat[N2, E2](Nil, edges)(e)

  /** Alias for `concat(isolatedNodes, edges)`. */
  @inline final def ++[N2 >: N, E2 >: E <: Edge[N2]](isolatedNodes: IterableOnce[N2], edges: IterableOnce[E2])(implicit
      e: E2 <:< Edge[N2]
  ): CC[N2, E2] =
    concat(isolatedNodes, edges)(e)

  /** Alias for `concat(edges)`. */
  @inline final def ++[N2 >: N, E2 >: E <: Edge[N2]](edges: IterableOnce[E2])(implicit
      e: E2 <:< Edge[N2]
  ): CC[N2, E2] =
    concat[N2, E2](edges)(e)

  /** Computes the union between this graph and `that` graph. */
  @inline final def union[N2 >: N, E2 >: E <: Edge[N2]](that: AnyGraph[N2, E2]): CC[N2, E2] =
    concat(that.nodes.outerIterator, that.edges.outerIterator)

  /** Whether the given outer node is contained in this graph. */
  def contains(node: N): Boolean

  /** Whether the given outer edge is contained in this graph. */
  def contains(edge: E): Boolean

  type NodePredicate = NodeT => Boolean
  type EdgePredicate = EdgeT => Boolean

  /** Node predicate with constant `true`. */
  def anyNode: NodePredicate

  /** Node predicate with constant `false`. */
  def noNode: NodePredicate

  /** Edge predicate with constant `true`. */
  def anyEdge: EdgePredicate

  /** Edge predicate with constant `false`. */
  def noEdge: EdgePredicate

  /** Whether the given node is contained in this graph. */
  // TODO make implementation allocation-free
  @inline final def apply(node: N): Boolean = find(node).isDefined

  /** Whether the given edge is contained in this graph. */
  @inline final def apply(edge: E): Boolean = find(edge).isDefined

  /** Computes a new graph with nodes satisfying `nodeP` and edges satisfying `edgeP`.
    * If both `nodeP` and `edgeP` have default values the original graph is retained.
    */
  def filter(nodeP: NodePredicate = anyNode, edgeP: EdgePredicate = anyEdge): CC[N, E]

  /** Computes a new graph without nodes satisfying `nodeP` and without edges satisfying `ePred`.
    * If both `nodeP` and `ePred` have default values the original graph is retained.
    */
  def filterNot(nodeP: NodePredicate = noNode, edgeP: EdgePredicate = noEdge): CC[N, E] =
    filter(n => !nodeP(n), e => !edgeP(e))

  /** Searches this graph for an inner node that wraps an outer node equalling to the given outer node. */
  def find(node: N): Option[NodeT]

  /** Searches this graph for an inner edge that wraps an outer edge equalling to the given outer edge. */
  def find(edge: E): Option[EdgeT]

  /** Short for `find(node).get`.
    *
    * @throws NoSuchElementException if the node is not found.
    */
  def get(node: N): NodeT

  /** Short for `find(edge).get`.
    *
    * @throws NoSuchElementException if the edge is not found.
    */
  def get(edge: E): EdgeT

  protected def removedAll(isolatedNodes: IterableOnce[N], edges: IterableOnce[E]): CC[N, E]

  /** Computes a new graph that is the difference of this graph and `that` graph. */
  final def diff(that: AnyGraph[N, E]): CC[N, E] = removedAll(that.nodes.outerIterator, that.edges.outerIterator)

  /** Alias for `diff`. */
  @inline final def &~(that: AnyGraph[N, E]): CC[N, E] = this diff that

  /** Computes the intersection between this graph and `that` graph. */
  final def intersect(that: AnyGraph[N, E]): CC[N, E] = this filter (n => that(n.outer), e => that(e.outer))

  /** Alias for `intersect`. */
  @inline final def &(that: AnyGraph[N, E]): CC[N, E] = this intersect that

  /** Alias for `union`. */
  @inline final def |(that: AnyGraph[N, E]): CC[N, E] = this union that

  /** $mapNodes
    *
    * $mapGeneric Otherwise see `mapBound`.
    *
    * If this graph also contains typed edges, the typed edge's partial `map` function will be called to replace the ends.
    * If the partial function is not defined, there will be an attempt to fall back to a generic edge.
    * If that attempt also fails the edge will be dropped.
    * So, if you have a mixed graph with generic and typed edges, prefer mapping edges directly to avoid leaving edges out.
    *
    * @tparam NN   $mapNN
    * @tparam EC   $mapEC
    * @param fNode $fNode
    * @return      $mapReturn
    */
  /* @param w1    Ensures that the type parameter `E` of this graph is of type `GenericMapper`.
   * @param w2    Captures the higher kind of current `E` to determine the return type.
   */
  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
    *
    * $mapTyped Otherwise see `map`.
    *
    * @param fNode $fNode
    * @return      $mapReturn
    */
  /* @param w1 Ensures that the type parameter `E` of this graph is of type `PartialMapper`.
   */
  def mapBound(fNode: NodeT => N)(implicit w1: E <:< PartialMapper): CC[N, E]

  /** $mapEdges
    *
    * $mapGeneric Otherwise see `mapBound`.
    *
    * @tparam NN   $mapNN
    * @tparam EC   $mapEC
    * @param fNode $fNode
    * @param fEdge $fEdge
    * @return      $mapReturn
    */
  /* @param w witnesses that this graph is defined as a non-hypergraph by its `E` type parameter.
   */
  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]]

  /** $mapEdges
    *
    * $seeOverload
    *
    * @param fEdge $simplifiedFEdge
    */
  final def map[NN, EC[X] <: Edge[X]](
      fNode: NodeT => NN,
      fEdge: (NN, NN) => EC[NN]
  )(implicit w: E <:< AnyEdge[N]): CC[NN, EC[NN]] =
    map(fNode, (_, n1: NN, n2: NN) => fEdge(n1, n2))

  /** $mapEdges
    *
    * $mapTyped Otherwise see `map`.
    *
    * @tparam NN $mapNN
    * @tparam EC $mapECTyped
    * @param fNode $fNode
    * @param fEdge $fEdge
    * @return $mapReturn
    */
  /* @param w witnesses that this graph is defined as a non-hypergraph by its `E` type parameter.
   */
  def mapBound[NN, EC <: Edge[NN]](
      fNode: NodeT => NN,
      fEdge: (EdgeT, NN, NN) => EC
  )(implicit w: E <:< AnyEdge[N]): CC[NN, EC]

  /** $mapEdges
    *
    * $seeOverload
    *
    * @param fEdge $simplifiedFEdge
    */
  final def mapBound[NN, EC <: Edge[NN]](
      fNode: NodeT => NN,
      fEdge: (NN, NN) => EC
  )(implicit w: E <:< AnyEdge[N]): CC[NN, EC] =
    mapBound(fNode, (_, n1: NN, n2: NN) => fEdge(n1, n2))

  /** $mapEdges
    *
    * $mapGeneric Otherwise see `mapHyperBound`.
    *
    * @tparam NN $mapNN
    * @tparam EC $mapEC
    * @param fNode $fNode
    * @param fHyperEdge $fHyperEdge
    * @param fDiHyperEdge $fDiHyperEdgeOption
    * @param fEdge $fEdgeOption
    * @return $mapReturn
    */
  /* @param w witnesses that this graph is defined as a hypergraph by its `E` type parameter.
   */
  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]]

  /** $mapEdges
    *
    * $mapGeneric Otherwise see `mapHyperBound`.
    *
    * $seeOverload
    *
    * @param fHyperEdge $simplifiedFEdge
    */
  final def mapHyper[NN, EC[X] <: Edge[X]](
      fNode: NodeT => NN,
      fHyperEdge: Several[NN] => EC[NN],
      fDiHyperEdge: Option[(OneOrMore[NN], OneOrMore[NN]) => EC[NN]] = None,
      fEdge: Option[(NN, NN) => EC[NN]] = None
  )(implicit w: E <:< AnyHyperEdge[N]): CC[NN, EC[NN]] =
    mapHyper(
      fNode,
      (_, several: Several[NN]) => fHyperEdge(several),
      fDiHyperEdge.map(f => (_, sources: OneOrMore[NN], targets: OneOrMore[NN]) => f(sources, targets)),
      fEdge.map(f => (_, n1: NN, n2: NN) => f(n1, n2))
    )

  /** $mapEdges
    *
    * $mapTyped Otherwise see `mapHyper`.
    *
    * @tparam NN $mapNN
    * @tparam EC $mapECTyped
    * @param fNode $fNode
    * @param fHyperEdge $fHyperEdge
    * @param fDiHyperEdge $fDiHyperEdgeOption
    * @param fEdge $fEdgeOption
    * @return $mapReturn
    */
  /* @param w witnesses that this graph is defined as a hypergraph by its `E` type parameter.
   */
  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]

  /** $mapEdges
    *
    * $mapTyped Otherwise see `mapHyper`.
    *
    * $seeOverload
    *
    * @param fHyperEdge $simplifiedFEdge
    */
  final def mapHyperBound[NN, EC <: Edge[NN]](
      fNode: NodeT => NN,
      fHyperEdge: Several[NN] => EC,
      fDiHyperEdge: Option[(OneOrMore[NN], OneOrMore[NN]) => EC] = None,
      fEdge: Option[(NN, NN) => EC] = None
  )(implicit w: E <:< AnyHyperEdge[N]): CC[NN, EC] =
    mapHyperBound(
      fNode,
      (_, several: Several[NN]) => fHyperEdge(several),
      fDiHyperEdge.map(f => (_, sources: OneOrMore[NN], targets: OneOrMore[NN]) => f(sources, targets)),
      fEdge.map(f => (_, n1: NN, n2: NN) => f(n1, n2))
    )

  /** $mapEdges
    *
    * $mapGeneric Otherwise see `mapDiHyperBound`.
    *
    * @tparam NN $mapNN
    * @tparam EC $mapEC
    * @param fNode $fNode
    * @param fDiHyperEdge $fDiHyperEdge
    * @param fEdge $fEdgeOption
    * @return $mapReturn
    */
  /* @param w witnesses that this graph is defined as a directed hypergraph by its `E` type parameter.
   */
  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]]

  /** $mapEdges
    *
    * $mapGeneric Otherwise see `mapDiHyperBound`.
    *
    * $seeOverload
    *
    * @param fDiHyperEdge $simplifiedFEdge
    */
  final def mapDiHyper[NN, EC[X] <: Edge[X]](
      fNode: NodeT => NN,
      fDiHyperEdge: (OneOrMore[NN], OneOrMore[NN]) => EC[NN],
      fEdge: Option[(NN, NN) => EC[NN]] = None
  )(implicit w: E <:< AnyDiHyperEdge[N]): CC[NN, EC[NN]] =
    mapDiHyper(
      fNode,
      (_, sources: OneOrMore[NN], targets: OneOrMore[NN]) => fDiHyperEdge(sources, targets),
      fEdge.map(f => (_, n1: NN, n2: NN) => f(n1, n2))
    )

  /** $mapEdges
    *
    * $mapTyped Otherwise see `mapDiHyper`.
    *
    * @tparam NN $mapNN
    * @tparam EC $mapECTyped
    * @param fNode $fNode
    * @param fDiHyperEdge $fDiHyperEdge
    * @param fEdge $fEdgeOption
    * @return $mapReturn
    */
  /* @param w witnesses that this graph is defined as a directed hypergraph by its `E` type parameter.
   */
  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]

  /** $mapEdges
    *
    * $mapTyped Otherwise see `mapDiHyper`.
    *
    * $seeOverload
    *
    * @param fDiHyperEdge $simplifiedFEdge
    */
  final def mapDiHyperBound[NN, EC <: Edge[NN]](
      fNode: NodeT => NN,
      fDiHyperEdge: (OneOrMore[NN], OneOrMore[NN]) => EC,
      fEdge: Option[(NN, NN) => EC] = None
  )(implicit w: E <:< AnyDiHyperEdge[N]): CC[NN, EC] =
    mapDiHyperBound(
      fNode,
      (_, sources: OneOrMore[NN], targets: OneOrMore[NN]) => fDiHyperEdge(sources, targets),
      fEdge.map(f => (_, n1: NN, n2: NN) => f(n1, n2))
    )

  /** $flatMapNodes
    *
    * $mapGeneric Otherwise see `flatMapBound`.
    *
    * If this graph also contains typed edges, the typed edge's partial `map` function will be called to replace the ends.
    * If the partial function is not defined, there will be an attempt to fall back to a generic edge.
    * If that attempt also fails the edge will be dropped.
    * So, if you have a mixed graph with generic and typed edges, prefer mapping edges directly to avoid leaving edges out.
    *
    * @tparam NN $mapNN
    * @tparam EC $mapEC
    * @param fNode $fNode $fNodeFlat
    * @return $flatMapReturn
    */
  /* @param w1    Ensures that the type parameter `E` of this graph is of type `GenericMapper`.
   * @param w2    Captures the higher kind of current `E` to determine the return type.
   */
  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]]

  /** $flatMapNodes
    *
    * $mapTyped Otherwise see `flatMap`.
    *
    * @param fNode $fNode $fNodeFlat
    */
  /* @param w1 Ensures that the type parameter `E` of this graph is of type `PartialMapper`.
   */
  def flatMapBound(fNode: NodeT => Seq[N])(implicit w1: E <:< PartialMapper): CC[N, E]

  /** $flatMapEdges
    *
    * $mapGeneric Otherwise see `flatMapBound`.
    *
    * @tparam NN $mapNN
    * @tparam EC $mapEC
    * @param fNode $fNode
    * @param fEdge $fEdge
    * @return $flatMapReturn
    */
  /* @param w witnesses that this graph is defined as a non-hypergraph by its `E` type parameter.
   */
  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]]

  /** $flatMapEdges
    *
    * $seeOverload
    *
    * @param fEdge $simplifiedFEdge
    */
  final def flatMap[NN, EC[X] <: Edge[X]](
      fNode: NodeT => Seq[NN],
      fEdge: (Seq[NN], Seq[NN]) => Seq[EC[NN]]
  )(implicit w: E <:< AnyEdge[N]): CC[NN, EC[NN]] =
    flatMap(fNode, (_, n1: Seq[NN], n2: Seq[NN]) => fEdge(n1, n2))

  /** $flatMapEdges
    *
    * $mapTyped Otherwise see `flatMap`.
    *
    * @tparam NN $mapNN
    * @tparam EC $mapECTyped
    * @param fNode $fNode
    * @param fEdge $fEdge
    * @return $flatMapReturn
    */
  /* @param w witnesses that this graph is defined as a non-hypergraph by its `E` type parameter.
   */
  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]

  /** $flatMapEdges
    *
    * $mapTyped Otherwise see `flatMap`.
    *
    * $seeOverload
    *
    * @param fEdge $simplifiedFEdge
    */
  final def flatMapBound[NN, EC <: Edge[NN]](
      fNode: NodeT => Seq[NN],
      fEdge: (Seq[NN], Seq[NN]) => Seq[EC]
  )(implicit w: E <:< AnyEdge[N]): CC[NN, EC] =
    flatMapBound(fNode, (_, n1: Seq[NN], n2: Seq[NN]) => fEdge(n1, n2))

  /** $flatMapEdges
    *
    * $mapGeneric Otherwise see `flatMapHyperBound`.
    *
    * @tparam NN $mapNN
    * @tparam EC $mapEC
    * @param fNode        $fNode
    * @param fHyperEdge   $fHyperEdge
    * @param fDiHyperEdge $fDiHyperEdgeOption
    * @param fEdge        $fEdgeOption
    * @return $flatMapReturn
    */
  /* @param w witnesses that this graph is defined as a hypergraph by its `E` type parameter.
   */
  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]]

  /** $flatMapEdges
    *
    * $mapGeneric Otherwise see `flatMapHyperBound`.
    *
    * $seeOverload
    *
    * @param fHyperEdge $simplifiedFEdge
    */
  final def flatMapHyper[NN, EC[X] <: Edge[X]](
      fNode: NodeT => Seq[NN],
      fHyperEdge: Seq[NN] => Seq[EC[NN]],
      fDiHyperEdge: Option[(Seq[NN], Seq[NN]) => Seq[EC[NN]]] = None,
      fEdge: Option[(Seq[NN], Seq[NN]) => Seq[EC[NN]]] = None
  )(implicit w: E <:< AnyHyperEdge[N]): CC[NN, EC[NN]] =
    flatMapHyper(
      fNode,
      (_, ends: Seq[NN]) => fHyperEdge(ends),
      fDiHyperEdge.map(f => (_, sources: Seq[NN], targets: Seq[NN]) => f(sources, targets)),
      fEdge.map(f => (_, n1s: Seq[NN], n2s: Seq[NN]) => f(n1s, n2s))
    )

  /** $flatMapEdges
    *
    * $mapTyped Otherwise see `flatMapHyper`.
    *
    * @tparam NN $mapNN
    * @tparam EC $mapECTyped
    * @param fNode        $fNode
    * @param fHyperEdge   $fHyperEdge
    * @param fDiHyperEdge $fDiHyperEdgeOption
    * @param fEdge        $fEdgeOption
    * @return $flatMapReturn
    */
  /* @param w witnesses that this graph is defined as a hypergraph by its `E` type parameter.
   */
  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]

  /** $flatMapEdges
    *
    * $mapTyped Otherwise see `flatMapHyper`.
    *
    * $seeOverload
    *
    * @param fHyperEdge $simplifiedFEdge
    */
  final def flatMapHyperBound[NN, EC <: Edge[NN]](
      fNode: NodeT => Seq[NN],
      fHyperEdge: Seq[NN] => Seq[EC],
      fDiHyperEdge: Option[(Seq[NN], Seq[NN]) => Seq[EC]] = None,
      fEdge: Option[(Seq[NN], Seq[NN]) => Seq[EC]] = None
  )(implicit w: E <:< AnyHyperEdge[N]): CC[NN, EC] =
    flatMapHyperBound(
      fNode,
      (_, ends: Seq[NN]) => fHyperEdge(ends),
      fDiHyperEdge.map(f => (_, sources: Seq[NN], targets: Seq[NN]) => f(sources, targets)),
      fEdge.map(f => (_, n1s: Seq[NN], n2s: Seq[NN]) => f(n1s, n2s))
    )

  /** $flatMapEdges
    *
    * $mapGeneric Otherwise see `flatMapDiHyperBound`.
    *
    * @tparam NN $mapNN
    * @tparam EC $mapEC
    * @param fNode        $fNode
    * @param fDiHyperEdge $fDiHyperEdge
    * @param fEdge        $fEdgeOption
    * @return $flatMapReturn
    */
  /* @param w witnesses that this graph is defined as a directed hypergraph by its `E` type parameter.
   */
  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]]

  /** $flatMapEdges
    *
    * $mapGeneric Otherwise see `flatMapDiHyperBound`.
    *
    * $seeOverload
    *
    * @param fDiHyperEdge $simplifiedFEdge
    */
  final def flatMapDiHyper[NN, EC[X] <: Edge[X]](
      fNode: NodeT => Seq[NN],
      fDiHyperEdge: (Seq[NN], Seq[NN]) => Seq[EC[NN]],
      fEdge: Option[(Seq[NN], Seq[NN]) => Seq[EC[NN]]] = None
  )(implicit w: E <:< AnyDiHyperEdge[N]): CC[NN, EC[NN]] =
    flatMapDiHyper(
      fNode,
      (_, sources: Seq[NN], targets: Seq[NN]) => fDiHyperEdge(sources, targets),
      fEdge.map(f => (_, n1s: Seq[NN], n2s: Seq[NN]) => f(n1s, n2s))
    )

  /** $flatMapEdges
    *
    * $mapTyped Otherwise see `flatMapDiHyper`.
    *
    * @tparam NN $mapNN
    * @tparam EC $mapECTyped
    * @param fNode        $fNode
    * @param fDiHyperEdge $fDiHyperEdge
    * @param fEdge        $fEdgeOption
    * @return $flatMapReturn
    */
  /* @param w witnesses that this graph is defined as a directed hypergraph by its `E` type parameter.
   */
  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]

  /** $flatMapEdges
    *
    * $mapTyped Otherwise see `flatMapDiHyper`.
    *
    * $seeOverload
    *
    * @param fDiHyperEdge $simplifiedFEdge
    */
  final def flatMapDiHyperBound[NN, EC <: Edge[NN]](
      fNode: NodeT => Seq[NN],
      fDiHyperEdge: (Seq[NN], Seq[NN]) => Seq[EC],
      fEdge: Option[(Seq[NN], Seq[NN]) => Seq[EC]] = None
  )(implicit w: E <:< AnyDiHyperEdge[N]): CC[NN, EC] =
    flatMapDiHyperBound(
      fNode,
      (_, sources: Seq[NN], targets: Seq[NN]) => fDiHyperEdge(sources, targets),
      fEdge.map(f => (_, n1s: Seq[NN], n2s: Seq[NN]) => f(n1s, n2s))
    )

  /** Applies a node-specific and an edge-specific binary operator to a cumulated value.
    * First `opNode` is called for all nodes than `opEdge` for all edges.
    *
    * @param z  the start value that is passed to `opNode` the first time.
    * @param opNode the binary operator that is passed the cumulated value and an inner node.
    * @param opEdge the binary operator that is passed the cumulated value and an inner edge.
    * @tparam B the result type of the binary operator.
    * @return the cumulated value.
    */
  def foldLeft[B](z: B)(opNode: (B, NodeT) => B, opEdge: (B, EdgeT) => B): B

  /** Same as `foldLeft` except the second parameter of the binary operators.
    *
    * @param opNode the binary operator that is passed the cumulated value and an outer node.
    * @param opEdge the binary operator that is passed the cumulated value and an outer edge.
    */
  final def foldLeftOuter[B](z: B)(opNode: (B, N) => B, opEdge: (B, E) => B): B =
    foldLeft(z)((cum: B, n: NodeT) => opNode(cum, n.outer), (cum: B, e: EdgeT) => opEdge(cum, e.outer))
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy