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

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

The newest version!
package scalax.collection

import language.postfixOps

import scalax.collection.generic.Edge
import scalax.collection.mutable.ExtBitSet

/** Adds bit fields to the graph and its nodes facilitating fast storage and retrieval of
  *  traversal-specific flags as a decoupled implementation trait. These flags are often used
  *  to keep track of visited nodes.
  *  Traversals (algorithms) acquire handles by calling `withHandle`. Then,
  *  in `withHandle`'s code block, `node.visited` or `node.bit` is called with the supplied
  *  handle as an implicit parameter to set or get a node's flag.
  *
  *  @author Peter Empen
  */
protected trait State[N, E <: Edge[N]] {
  this: GraphTraversalImpl[N, E] =>

  import State._

  /** Flags in `inUse` refer to required but unclosed handles. */
  private val inUse = new FlagStore

  /** Flags in `dirty` refer to released handles with dirty flags at the nodes. */
  private val dirty   = new FlagStore
  private val monitor = new Object with Serializable

  protected def dump(store: FlagStore): ExtBitSet = {
    val words = store.flagsExt.cloneWords
    val dump  = new Array[Long](words.length + 1)
    dump(0) = store.flags
    Array.copy(words, 0, dump, 1, words.length)
    new ExtBitSet(dump)
  }

  /** Returns a copy of the current `inUse`-flags for dump purposes. */
  def dumpInUse: ExtBitSet = dump(inUse)

  /** Returns a copy of the current `dirty`-flags for dump purposes. */
  def dumpDirty: ExtBitSet = dump(dirty)

  /** Avoid calling this directly, prefer `withHandle` instead. */
  protected def nextHandle: Handle = monitor.synchronized {
    def clearNodes(hasDirtyExt: Boolean): Unit = {
      clearNodeStates(dirty.flags, if (hasDirtyExt) dirty.flagsExt else null)
      dirty.flags = 0L
      if (hasDirtyExt) dirty.flagsExt.clear()
    }
    val free         = ~(inUse.flags | dirty.flags)
    val nrDirtyFlags = java.lang.Long.bitCount(dirty.flags)
    val newHandle: Handle =
      if (free != 0) {
        val nextFree = java.lang.Long.lowestOneBit(free)
        new Handle(singleWord, nextFree)
      } else if (nrDirtyFlags >= minBitsForClear) {
        clearNodes(dirty.flagsExt.headOption.nonEmpty)
        val nextFree = java.lang.Long.lowestOneBit(~inUse.flags)
        new Handle(singleWord, nextFree)
      } else
        inUse.flagsExt.onOrFindUnset(dirty.flagsExt) map { handle =>
          handle
        } getOrElse { // no free flag in flagsExt
          if (nrDirtyFlags + dirty.flagsExt.size >= minBitsForClearExt) {
            val handle = dirty.flagsExt.lowestOneBit.get
            clearNodes(true)
            handle
          } else { // we expand flagsExt
            val handle = new Handle(inUse.flagsExt.nrWords, 1L)
            handle
          }
        }
    inUse(newHandle) = true
    newHandle
  }

  /** Avoid calling this directly, prefer `withHandle` instead. */
  protected def releaseHandle(handle: Handle) = monitor.synchronized {
    inUse(handle) = false
    dirty(handle) = true
  }

  /** Executes a code block in the context of a new or reused state handler.
    *  @return The result of the code block executed.
    */
  protected def withHandle[T](reuse: Option[Handle] = None)(block: Handle => T): T = {
    val thisHandler = reuse getOrElse nextHandle
    val res         = block(thisHandler)
    if (reuse isEmpty) releaseHandle(thisHandler)
    res
  }

  /** Executes a code block in the context `nr` new state handlers
    *  or alternatively in the context of the state handlers `reuse`.
    *  @return The result of the code block executed.
    */
  protected def withHandles[T](nr: Int, reuse: Array[Handle] = Array.empty[Handle])(block: Array[Handle] => T): T = {
    val newHandlers = reuse isEmpty
    val theseHandles =
      if (newHandlers) Array.fill(nr)(nextHandle)
      else reuse
    val res = block(theseHandles)
    if (newHandlers) theseHandles foreach releaseHandle
    res
  }

  trait InnerNodeState {
    protected[State] var flags: FlagWord     = 0L
    protected[State] var flagsExt: FlagWords = null
    @inline final protected def withFlagsExt[T](block: (ExtBitSet) => T): T =
      block {
        if (flagsExt eq null) flagsExt = initFlagSet
        flagsExt
      }

    def dumpState: Array[Long] = {
      val dump = new Array[Long](1 + (if (flagsExt eq null) 0 else flagsExt.nrWords))
      dump(0) = flags
      if (flagsExt ne null)
        Array.copy(flagsExt.cloneWords, 0, dump, 1, flagsExt.nrWords)
      dump
    }

    @inline final protected[collection] def bit[T](implicit handle: Handle): Boolean =
      if (handle.index == singleWord) (flags & handle.mask) != 0L
      else withFlagsExt(_.apply(handle.index, handle.mask))

    /** Whether this node is marked as visited with respect to `handle`. */
    @inline final protected[collection] def visited(implicit handle: Handle): Boolean =
      bit(handle)

    @inline final protected[collection] def bit_=[T](isSet: Boolean)(implicit handle: Handle): Unit =
      monitor.synchronized {
        if (handle.index == singleWord)
          flags =
            if (isSet) flags | handle.mask
            else flags & ~handle.mask
        else withFlagsExt(_.update(handle.index, handle.mask, isSet))
      }

    /** Sets this node to `visited` with respect to to `handle`. */
    @inline final protected[collection] def visited_=(visited: Boolean)(implicit handle: Handle): Unit =
      bit_=(visited)(handle)
  }

  protected def clearNodeStates(flags: FlagWord, flagsExt: ExtBitSet): Unit = {
    val clear      = ~flags
    val doClearExt = flagsExt != null
    val clearExt   = if (doClearExt) ~flagsExt else null
    nodes foreach { n =>
      n.flags &= clear
      if (doClearExt && (n.flagsExt ne null)) n.flagsExt &= clearExt
    }
  }
}

object State {

  /** Word of flags, that is unit of bitwise boolean state information.
    *  These flags are mainly used to store whether a node counts as visited
    *  with respect to a given traversal where each traversal is represented by a `Handle`.
    */
  type FlagWord = Long
  protected val nrOfFlagWordBits = 64
  protected val minBitsForClear  = nrOfFlagWordBits >> 2

  /** Growable collection for storing bitwise boolean state information
    *  as an extension of `FlagsWord`.
    */
  type FlagWords = ExtBitSet
  protected val minBitsForClearExt = nrOfFlagWordBits
  require(minBitsForClear < minBitsForClearExt)

  /** state accessor with respect to a given traversal. */
  class Handle(val index: Int, val mask: FlagWord)
  val singleWord  = -1
  def emptyHandle = new Handle(singleWord, 0L)
  def initFlagSet = new ExtBitSet

  final protected class FlagStore(var flags: FlagWord = 0L, val flagsExt: ExtBitSet = initFlagSet)
      extends Serializable {

    /** Whether `store` is set with respect to `handle`. */
    def apply(handle: Handle): Boolean =
      if (handle.index == singleWord)
        (flags & handle.mask) != 0
      else
        flagsExt(handle.index, handle.mask)

    /** Sets `store` to `isSet` with respect to `handle`. */
    def update(handle: Handle, isSet: Boolean): Unit =
      if (handle.index == singleWord)
        flags =
          if (isSet) flags | handle.mask
          else flags & ~handle.mask
      else
        flagsExt(handle.index, handle.mask) = isSet
  }

  /** Dumps the state flags of a `node`. */
  def dump[N, E <: Edge[N]](node: AnyGraph[N, E]#NodeT): ExtBitSet =
    node.containingGraph match {
      case g: State[_, _] =>
        node match {
          case n: g.GraphInnerNode with g.InnerNodeState => new ExtBitSet(n.dumpState)
        }
    }

  /** Represents state flags of a graph for dump purposes. */
  class GraphDump(val inUse: ExtBitSet, val dirty: ExtBitSet) {
    override def toString = "inUse = %s%ndirty = %s%n".format(inUse.toString, dirty.toString)
  }

  /** Dumps the state flags of a `graph`. */
  def dump[N, E <: Edge[N]](graph: AnyGraph[N, E]): GraphDump = graph match {
    case g: State[_, _] => new GraphDump(g.dumpInUse, g.dumpDirty)
  }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy