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

scalax.collection.constrained.constraints.Connected.scala Maven / Gradle / Ivy

The newest version!
package scalax.collection.constrained
package constraints

import scala.annotation.unchecked.{uncheckedVariance => uV}
import scala.language.{higherKinds, postfixOps}
import scala.collection.Set

import scalax.collection.GraphPredef._
import scalax.collection.{Graph => SimpleGraph}
import scalax.collection.GraphTraversal.AnyConnected

import PreCheckFollowUp._

/** Ensures that the underlying `Graph` is connected if it is undirected
  * or weakly connected if it is directed.
  */
class Connected[N, E[X] <: EdgeLikeIn[X], G <: Graph[N, E]](override val self: G) extends Constraint[N, E, G](self) {

  /** Skips this pre-check to rely on the post-check `postAdd` except for trivial cases. */
  override def preCreate(nodes: Traversable[N], edges: Traversable[E[N]]) =
    PreCheckResult(
      if (edges.isEmpty && nodes.size <= 1 ||
          nodes.isEmpty && edges.size <= 1) Complete
      else PostCheck
    )

  /** `Complete` if `node` is contained even though no addition will be performed;
    *  otherwise `Abort` because `node` would become isolated. */
  override def preAdd(node: N): PreCheckResult = PreCheckResult.complete(self contains node)

  /** `Complete` if `edge` itself or at least one end of `edge` is already contained;
    *  otherwise `Abort`. */
  override def preAdd(edge: E[N]): PreCheckResult = PreCheckResult.complete(
    (self contains edge.asInstanceOf[OuterEdge[N, E]]) ||
      (edge exists (self contains _))
  )

  /** `Complete` if `elems` build a connected graph and at least one node of `elems`
    *  is already contained; otherwise `Abort`. */
  override def preAdd(elems: InParam[N, E]*): PreCheckResult = PreCheckResult.complete {
    val p        = Param.Partitions(elems)
    val graphAdd = SimpleGraph.from(p.toOuterNodes, p.toOuterEdges)(self.edgeT)
    graphAdd.isConnected &&
    (self.isEmpty ||
    (graphAdd.nodes exists (self find _ isDefined)))
  }

  /** Check the whole `newGraph`. */
  override def postAdd(newGraph: G @uV,
                       passedNodes: Traversable[N],
                       passedEdges: Traversable[E[N]],
                       preCheck: PreCheckResult): Either[PostCheckFailure, G] =
    if (newGraph.isConnected) Right(newGraph)
    else Left(PostCheckFailure(s"Unexpected isolated node found when adding $passedNodes, $passedEdges."))

  /** Checks within any `preSubtract` whether the neighborhood of the elements
    * to be subtracted remains connected after the subtraction thus preventing
    * a full traversal of the graph.
    *
    * @param include nodes in the neighborhood of the nodes/edges to be subtracted.
    * @param excludeNodes nodes to be subtracted.
    * @param excludeEdges edges to be subtracted.
    * @return `true`if all nodes in `include` are connected.
    */
  protected def isConnected(include: Set[self.NodeT],
                            excludeNodes: Set[self.NodeT],
                            excludeEdges: Set[self.EdgeT]): Boolean =
    include.headOption forall { head =>
      val cnt = head
        .withDirection(AnyConnected)
        .withSubgraph(
          nodes = n => (include contains n) && !(excludeNodes contains n),
          edges = e => !(excludeEdges contains e)
        )
        .size
      cnt == include.size
    }

  override def preSubtract(node: self.NodeT, forced: Boolean): PreCheckResult =
    PreCheckResult.complete(isConnected(node.neighbors, Set(node), node.edges.toSet))

  override def preSubtract(edge: self.EdgeT, simple: Boolean): PreCheckResult =
    PreCheckResult.complete(
      if (simple) isConnected(edge.nodes.toSet, Set.empty, Set(edge))
      else isConnected(edge.nodes.toSet -- edge.privateNodes, edge.privateNodes, Set(edge))
    )
  override def preSubtract(nodes: => Set[self.NodeT], edges: => Set[self.EdgeT], simple: Boolean): PreCheckResult =
    PreCheckResult.complete({
      def neighbors(nodes: Set[self.NodeT]) =
        (for (n <- nodes) yield n.neighbors).flatten
      val nodesToInspect = nodes ++ (for {
        e <- edges
        n <- e
      } yield n)
      if (simple)
        isConnected(neighbors(nodesToInspect) -- nodes, nodes, edges ++ (for (n <- nodes) yield n.edges).flatten)
      else
        isConnected(
          neighbors(nodesToInspect) -- nodes,
          nodes ++ (for (e <- edges) yield e.privateNodes).flatten,
          edges ++ (for (n <- nodes) yield n.edges).flatten)
    })
}

object Connected extends ConstraintCompanion[Connected] {
  def apply[N, E[X] <: EdgeLikeIn[X], G <: Graph[N, E]](self: G) = new Connected[N, E, G](self)
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy