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

scalaz.PrioritySearchQueue.scala Maven / Gradle / Ivy

The newest version!
package scalaz

import scala.annotation.tailrec
import scalaz.syntax.order._
import scalaz.syntax.show._

import PrioritySearchQueue.Summary

/** Container whose elements have two different orders: by priority and by lookup key.
  *
  * Supports efficient implementation of the following operations:
  * - insertion (O(log n)),
  * - lookup by key (O(log n)),
  * - deletion by key (O(log n)),
  * - access to minimal-by-priority element (O(1)),
  * - deletion of minimal-by-priority element (O(log n)).
  *
  * Implemented using [[FingerTree]].
  *
  * @tparam A element type
  * @tparam P priority of an element. Multiple elements can have the same priority.
  * @tparam K lookup key. Unique—the queue holds at most one element with any given key.
  */
class PrioritySearchQueue[A, P, K] private(
    private val tree: FingerTree[Summary[A, P, K], A],
    priorityOf: A => P,
    keyOf: A => K
) {
  def isEmpty: Boolean = tree.isEmpty
  def nonEmpty: Boolean = !tree.isEmpty
  def size: Int = tree.measure.map(_.size).getOrElse(0)

  /** Returns an element with minimum priority, or [[Maybe.Empty]] if this queue is empty.
    *
    * If there are multiple elements with the same minimum priority,
    * it is unspecified which of them is returned.
    */
  def minimum: Maybe[A] = tree.measure.map(_.minByPrio)

  def minimumPriority: Maybe[P] = tree.measure.map(_.minPrio)

  /** Removes an element with minimum priority.
    *
    * If there are multiple elements with the same minimum priority,
    * it is guaranteed to remove the one returned by [[minimum]].
    *
    * ''O(log(n))''.
    *
    * If this queue is empty, returns `this`.
    */
  def deleteMin(implicit P: Order[P]): PrioritySearchQueue[A, P, K] =
    tree.measure match {
      case Maybe.Empty() =>
        this
      case Maybe.Just(summary) =>
        val (tree1, tree2) = tree.split(_.minPrio === summary.minPrio)
        assert(!tree2.isEmpty) // the element with the minimal priority must be present in the tree
        new PrioritySearchQueue(tree1 <++> tree2.tail, priorityOf, keyOf)
    }

  // hack to get an empty tree without calling FingerTree.empty, which requires a Reducer
  private def emptyTree = tree.split(_ => false)._2

  /** Removes elements with priority less than `p`.
    *
    * ''O(min(k*log(n), (n-k)*log(n), n))'', where ''k'' is the number of actually removed elements.
    * For large ''k'', this method is more efficient than iterated [[deleteMin]].
    *
    * To also return the removed elements, use [[splitBeforePrio]].
    */
  def deleteLt(p: P)(implicit P: Order[P]): PrioritySearchQueue[A, P, K] = {
    @tailrec def go(
        accGte: FingerTree[Summary[A, P, K], A],
        tree: FingerTree[Summary[A, P, K], A]
    ): PrioritySearchQueue[A, P, K] =
      if (tree.isEmpty) {
        new PrioritySearchQueue(accGte, priorityOf, keyOf)
      } else {
        val (_, tree1) = tree.split(_.maxPrio >= p)
        val (gte, tree2) = tree1.split(_.minPrio < p)
        go(accGte <++> gte, tree2)
      }

    go(emptyTree, tree)
  }

  /** Splits this queue into elements with priority less than `p`
    * and elements with priority greater than or equal to `p`.
    */
  def splitBeforePrio(p: P)(implicit P: Order[P]): (PrioritySearchQueue[A, P, K], PrioritySearchQueue[A, P, K]) = {
    @tailrec def go(
        accLt: FingerTree[Summary[A, P, K], A],
        accGte: FingerTree[Summary[A, P, K], A],
        tree: FingerTree[Summary[A, P, K], A]
    ): (PrioritySearchQueue[A, P, K], PrioritySearchQueue[A, P, K]) =
      if (tree.isEmpty) {
        (new PrioritySearchQueue(accLt, priorityOf, keyOf), new PrioritySearchQueue(accGte, priorityOf, keyOf))
      } else {
        val (lt, tree1) = tree.split(_.maxPrio >= p)
        val (gte, tree2) = tree1.split(_.minPrio < p)
        go(accLt <++> lt, accGte <++> gte, tree2)
      }

    go(emptyTree, emptyTree, tree)
  }

  /** Removes elements with priority less than or equal to `p`.
    *
    * ''O(min(k*log(n), (n-k)*log(n), n))'', where ''k'' is the number of actually removed elements.
    * For large ''k'', this method is more efficient than iterated [[deleteMin]].
    *
    * To also return the removed elements, use [[splitAfterPrio]].
    */
  def deleteLte(p: P)(implicit P: Order[P]): PrioritySearchQueue[A, P, K] = {
    @tailrec def go(
        accGt: FingerTree[Summary[A, P, K], A],
        tree: FingerTree[Summary[A, P, K], A]
    ): PrioritySearchQueue[A, P, K] =
      if (tree.isEmpty) {
        new PrioritySearchQueue(accGt, priorityOf, keyOf)
      } else {
        val (_, tree1) = tree.split(_.maxPrio > p)
        val (gt, tree2) = tree1.split(_.minPrio <= p)
        go(accGt <++> gt, tree2)
      }

    go(emptyTree, tree)
  }

  /** Splits this queue into elements with priority less than or equal to `p`
    * and elements with priority greater than `p`.
    */
  def splitAfterPrio(p: P)(implicit P: Order[P]): (PrioritySearchQueue[A, P, K], PrioritySearchQueue[A, P, K]) = {
    @tailrec def go(
        accLte: FingerTree[Summary[A, P, K], A],
        accGt: FingerTree[Summary[A, P, K], A],
        tree: FingerTree[Summary[A, P, K], A]
    ): (PrioritySearchQueue[A, P, K], PrioritySearchQueue[A, P, K]) =
      if (tree.isEmpty) {
        (new PrioritySearchQueue(accLte, priorityOf, keyOf), new PrioritySearchQueue(accGt, priorityOf, keyOf))
      } else {
        val (lte, tree1) = tree.split(_.maxPrio > p)
        val (gt, tree2) = tree1.split(_.minPrio <= p)
        go(accLte <++> lte, accGt <++> gt, tree2)
      }

    go(emptyTree, emptyTree, tree)
  }

  /** Inserts the given element into this queue.
    *
    * If an element with the same key is already present in this queue, it is replaced by `elem`
    * and the replaced element is returned in the second part of the returned pair.
    */
  def insert(elem: A)(implicit K: Order[K]): (PrioritySearchQueue[A, P, K], Maybe[A]) = {
    val (tree1, tree2) = tree.split(_.maxKey >= keyOf(elem))
    val (newTree, replacedElem) =
      if (tree2.isEmpty) {
        (tree1 :+ elem, Maybe.empty[A])
      } else if (keyOf(tree2.head) === keyOf(elem)) {
        (tree1 <++> (elem |-: tree2), Maybe.just(tree2.head))
      } else {
        (tree1.add1(elem, tree2), Maybe.empty[A])
      }
    (new PrioritySearchQueue(newTree, priorityOf, keyOf), replacedElem)
  }

  /** Looks up an element by key. */
  def get(key: K)(implicit K: Order[K]): Maybe[A] = {
    val (tree1, tree2) = tree.split(_.maxKey >= key)
    if (tree2.isEmpty) {
      Maybe.empty
    } else if (keyOf(tree2.head) === key) {
      Maybe.just(tree2.head)
    } else {
      Maybe.empty
    }
  }

  /** Removes an element by key.
    *
    * Returns the removed element, if any, in the second part of the returned pair.
    */
  def removeByKey(key: K)(implicit K: Order[K]): (PrioritySearchQueue[A, P, K], Maybe[A]) = {
    val (tree1, tree2) = tree.split(_.maxKey >= key)
    if (tree2.isEmpty) {
      (this, Maybe.empty)
    } else if (keyOf(tree2.head) === key) {
      (new PrioritySearchQueue(tree1 <++> tree2.tail, priorityOf, keyOf), Maybe.just(tree2.head))
    } else {
      (this, Maybe.empty)
    }
  }

  def containsKey(key: K)(implicit K: Order[K]): Boolean = {
    val (tree1, tree2) = tree.split(_.maxKey >= key)
    !tree2.isEmpty && (keyOf(tree2.head) === key)
  }

  def toUnsortedList: List[A] = tree.toList
  def toUnsortedIList: IList[A] = tree.toIList
}

object PrioritySearchQueue {
  def empty[A, P: Order, K: Order](priorityOf: A => P, keyOf: A => K): PrioritySearchQueue[A, P, K] = {
    def summaryOf(a: A): Summary[A, P, K] =
      Summary(1, priorityOf(a), priorityOf(a), a, a, keyOf(a), keyOf(a))

    implicit val reducer: Reducer[A, Summary[A, P, K]] =
      Reducer.unitReducer(summaryOf)

    new PrioritySearchQueue[A, P, K](FingerTree.empty, priorityOf, keyOf)
  }

  private[PrioritySearchQueue] case class Summary[A, P, K](
      size: Int,
      minPrio: P,
      maxPrio: P,
      minByPrio: A,
      maxByPrio: A,
      minKey: K,
      maxKey: K
  )

  private[PrioritySearchQueue] object Summary {
    implicit def summarySemigroup[A, P, K](implicit P: Order[P], K: Order[K]): Semigroup[Summary[A, P, K]] =
      new Semigroup[Summary[A, P, K]] {
        override def append(s1: Summary[A, P, K], s2: => Summary[A, P, K]): Summary[A, P, K] = {
          val (minPrio, minByPrio) =
            if (s1.minPrio <= s2.minPrio) (s1.minPrio, s1.minByPrio)
            else (s2.minPrio, s2.minByPrio)
          val (maxPrio, maxByPrio) =
            if (s1.maxPrio >= s2.maxPrio) (s1.maxPrio, s1.maxByPrio)
            else (s2.maxPrio, s2.maxByPrio)
          Summary(
            s1.size + s2.size,
            minPrio,
            maxPrio,
            minByPrio,
            maxByPrio,
            s1.minKey min s2.minKey,
            s1.maxKey max s2.maxKey
          )
        }
      }
  }

  implicit def showInstance[A: Show, P, K]: Show[PrioritySearchQueue[A, P, K]] = new Show[PrioritySearchQueue[A, P, K]] {
    override def show(q: PrioritySearchQueue[A, P, K]): Cord =
      Cord("PrioritySearchQueue(") :: (q.tree.foldRight(Cord(")")) { (a, acc) => a.show :: Cord(", ") :: acc })
  }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy