git.fiddlerwoaroof.com
Raw Blame History
(ns tailrecursion.priority-map
  (:require [cljs.core :as core])
  (:use [cljs.reader :only [register-tag-parser!
                            reader-error]])
  (:require-macros [cljs.core :as coreclj]))

(deftype PersistentPriorityMap [priority->set-of-items item->priority meta ^:mutable __hash]
  IPrintWithWriter
  (-pr-writer [coll writer opts]
    (let [pr-pair (fn [keyval] (pr-sequential-writer writer pr-writer "" " " "" opts keyval))]
      (pr-sequential-writer writer pr-pair "#tailrecursion.priority-map {" ", " "}" opts coll)))

  IWithMeta
  (-with-meta [this meta]
    (PersistentPriorityMap. priority->set-of-items item->priority meta __hash))

  IMeta
  (-meta [this] meta)

  ICollection
  (-conj [this entry]
    (if (vector? entry)
      (-assoc this (-nth entry 0) (-nth entry 1))
      (reduce -conj this entry)))

  IEmptyableCollection
  (-empty [this] (with-meta
                   tailrecursion.priority-map.PersistentPriorityMap.EMPTY
                   meta))

  IEquiv
  (-equiv [this other]
    (-equiv item->priority other))

  IHash
  (-hash [this]
    (coreclj/caching-hash this core/hash-imap __hash))

  ISeqable
  (-seq [this]
    (seq (for [[priority item-set] priority->set-of-items, item item-set]
           [item priority])))

  IReversible
  (-rseq [coll]
    (seq (for [[priority item-set] (rseq priority->set-of-items), item item-set]
           [item priority])))

  ICounted
  (-count [this]
    (count item->priority))

  ILookup
  (-lookup [this item]
    (get item->priority item))
  (-lookup [coll item not-found]
    (get item->priority item not-found))

  IStack
  (-peek [this]
    (when-not (zero? (count item->priority))
      (let [f (first priority->set-of-items)]
        [(first (val f)) (key f)])))
  (-pop [this]
    (if (zero? (count item->priority))
      (throw (js/Error. "Can't pop empty priority map"))
      (let [f (first priority->set-of-items)
            item-set (val f)
            item (first item-set)
            priority (key f)]
        (if (= (count item-set) 1)
          (PersistentPriorityMap.
           (dissoc priority->set-of-items priority)
           (dissoc item->priority item)
           meta
           nil)
          (PersistentPriorityMap.
           (assoc priority->set-of-items priority (disj item-set item)),
           (dissoc item->priority item)
           meta
           nil)))))

  IAssociative
  (-assoc [this item priority]
    (if-let [current-priority (get item->priority item nil)]
      (if (= current-priority priority)
        this
        (let [item-set (get priority->set-of-items current-priority)]
          (if (= (count item-set) 1)
            (PersistentPriorityMap.
             (assoc (dissoc priority->set-of-items current-priority)
               priority (conj (get priority->set-of-items priority #{}) item))
             (assoc item->priority item priority)
             meta
             nil)
            (PersistentPriorityMap.
             (assoc priority->set-of-items
               current-priority (disj (get priority->set-of-items current-priority) item)
               priority (conj (get priority->set-of-items priority #{}) item))
             (assoc item->priority item priority)
             meta
             nil))))
      (PersistentPriorityMap.
       (assoc priority->set-of-items
         priority (conj (get priority->set-of-items priority #{}) item))
       (assoc item->priority item priority)
       meta
       nil)))

  (-contains-key? [this item]
    (contains? item->priority item))

  IMap
  (-dissoc [this item]
    (let [priority (item->priority item ::not-found)]
      (if (= priority ::not-found)
        this
        (let [item-set (priority->set-of-items priority)]
          (if (= (count item-set) 1)
            (PersistentPriorityMap.
             (dissoc priority->set-of-items priority)
             (dissoc item->priority item)
             meta
             nil)
            (PersistentPriorityMap.
             (assoc priority->set-of-items priority (disj item-set item)),
             (dissoc item->priority item)
             meta
             nil))))))

  ISorted
  (-sorted-seq [this ascending?]
    ((if ascending? seq rseq) this))
  (-sorted-seq-from [this k ascending?]
    (let [sets (if ascending?
                 (subseq priority->set-of-items >= k)
                 (rsubseq priority->set-of-items <= k))]
      (seq (for [[priority item-set] sets, item item-set]
             [item priority]))))
  (-entry-key [this entry]
    (val entry))
  (-comparator [this] compare)

  IFn
  (-invoke [this item]
    (-lookup this item))
  (-invoke [this item not-found]
    (-lookup this item not-found)))

(set! tailrecursion.priority-map.PersistentPriorityMap.EMPTY
      (PersistentPriorityMap. (sorted-map) {} {} nil))

(defn- pm-empty-by [comparator]
  (PersistentPriorityMap. (sorted-map-by comparator) {} {} nil))

(defn- read-priority-map [elems]
  (if (map? elems)
    (into tailrecursion.priority-map.PersistentPriorityMap.EMPTY elems)
    (reader-error nil "Priority map literal expects a map for its elements.")))

(register-tag-parser! "tailrecursion.priority-map" read-priority-map)

(defn priority-map
  "keyval => key val
  Returns a new priority map with supplied mappings."
  ([& keyvals]
     (loop [in (seq keyvals) out tailrecursion.priority-map.PersistentPriorityMap.EMPTY]
       (if in
         (recur (nnext in) (assoc out (first in) (second in)))
         out))))

(defn priority-map-by
  "keyval => key val
  Returns a new priority map with supplied
  mappings, using the supplied comparator."
  ([comparator & keyvals]
     (loop [in (seq keyvals) out (pm-empty-by comparator)]
       (if in
         (recur (nnext in) (assoc out (first in) (second in)))
         out))))