diff --git a/damn-fast-priority-queue/test.lisp b/damn-fast-priority-queue/test.lisp index 46595d5..e92c3a6 100644 --- a/damn-fast-priority-queue/test.lisp +++ b/damn-fast-priority-queue/test.lisp @@ -12,15 +12,16 @@ ;;;; Utilities (defun verify-heap-property (vector) + (declare (type q::prio-vector-type vector)) (loop with length = (length vector) for parent from 0 below (truncate length 2) for left = (+ (* parent 2) 1) for right = (+ (* parent 2) 2) - do (assert (< (aref vector parent) (aref vector left)) () + do (assert (<= (aref vector parent) (aref vector left)) () "VERIFY-HEAP-PROPERTY: Invalid left child: ~D -> ~D" (aref vector parent) (aref vector left)) when (oddp length) - do (assert (< (aref vector parent) (aref vector right)) () + do (assert (<= (aref vector parent) (aref vector right)) () "VERIFY-HEAP-PROPERTY: Invalid right child: ~D -> ~D" (aref vector parent) (aref vector right)))) @@ -39,6 +40,10 @@ (perform-test queue (nreverse (a:iota length))) (dotimes (i 100) (perform-test queue (a:shuffle (a:iota length)))))) + (let ((queue (q:make-queue 64))) + (perform-test queue (alexandria:shuffle (append (a:iota 64) + (a:iota 32) + (a:iota 16))))) (perform-error-test) (perform-copy-test)) @@ -122,7 +127,7 @@ (defun test-dequeue-and-peek (queue list) (let ((counter (q:size queue))) - (dotimes (i (length list)) + (dolist (i (sort list '<)) (test-peek queue (stringify i) t) (assert (= counter (q:size queue))) (test-dequeue queue (stringify i) t) diff --git a/damn-fast-updatable-priority-queue/damn-fast-updatable-priority-queue.asd b/damn-fast-updatable-priority-queue/damn-fast-updatable-priority-queue.asd new file mode 100644 index 0000000..0fa84bf --- /dev/null +++ b/damn-fast-updatable-priority-queue/damn-fast-updatable-priority-queue.asd @@ -0,0 +1,21 @@ +;;;; damn-fast-updatable-priority-queue.asd + +(asdf:defsystem #:damn-fast-updatable-priority-queue + :description "A heap-based priority queue with delete and adjust-priority whose first and foremost priority is speed." + :author "Michał \"phoe\" Herda " + :license "MIT" + :version "0.0.2" + :serial t + :depends-on (#:alexandria) + :components ((:file "src")) + :in-order-to ((test-op (load-op #:damn-fast-updatable-priority-queue/test))) + :perform (test-op (o c) (symbol-call "DAMN-FAST-UPDATABLE-PRIORITY-QUEUE/TEST" "RUN"))) + +(asdf:defsystem #:damn-fast-updatable-priority-queue/test + :description "Tests for Damn Fast Priority Queue" + :author "Michał \"phoe\" Herda " + :license "MIT" + :version "0.0.2" + :serial t + :depends-on (#:alexandria #:damn-fast-updatable-priority-queue) + :components ((:file "test"))) diff --git a/damn-fast-updatable-priority-queue/src.lisp b/damn-fast-updatable-priority-queue/src.lisp new file mode 100644 index 0000000..828e4cf --- /dev/null +++ b/damn-fast-updatable-priority-queue/src.lisp @@ -0,0 +1,354 @@ +;;;; damn-fast-updatable-priority-queue.lisp + +(defpackage #:damn-fast-updatable-priority-queue + (:use #:cl) + (:shadow #:map #:delete) + (:local-nicknames (#:a #:alexandria)) + (:export #:queue #:make-queue #:copy-queue + #:enqueue #:dequeue #:peek #:size #:trim #:map #:do-queue + #:delete #:adjust-priority + #:handle-priority #:handle-data + #:queue-size-limit-reached + #:queue-size-limit-reached-queue #:queue-size-limit-reached-object)) + +(in-package #:damn-fast-updatable-priority-queue) + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;;;; Read-time variables + +(eval-when (:compile-toplevel :load-toplevel :execute) + (defparameter *optimize-qualities* + #+real-damn-fast-priority-queue + ;; Good luck. + `(optimize (speed 3) (debug 0) (safety 0) (space 0) (compilation-speed 0)) + #-real-damn-fast-priority-queue + `(optimize (speed 3)))) + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;;;; Type definitions + +(deftype data-type () '(cons fixnum t)) + +(deftype data-vector-type () '(simple-array data-type (*))) + +(deftype prio-type () '(unsigned-byte 32)) + +(deftype prio-vector-type () '(simple-array prio-type (*))) + +(deftype extension-factor-type () '(integer 2 256)) + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;;;; Structure definition + +(declaim (inline %make %data-vector %prio-vector %size %extension-factor)) + +(defstruct (queue (:conc-name #:%) (:constructor %make) + (:predicate nil) (:copier nil)) + (data-vector (make-array 256 :element-type 'data-type + :initial-element (cons 0 nil)) + :type data-vector-type) + (prio-vector (make-array 256 :element-type 'prio-type) :type prio-vector-type) + (size 0 :type a:array-length) + (extension-factor 2 :type extension-factor-type) + (extend-queue-p t :type boolean)) + +(declaim (inline make-queue copy-queue)) + +(declaim (ftype (function + (&optional a:array-index extension-factor-type boolean) + (values queue &optional)) + make-queue)) +(defun make-queue (&optional + (initial-storage-size 256) + (extension-factor 2) + (extend-queue-p t)) + (declare (type extension-factor-type extension-factor)) + (declare #.*optimize-qualities*) + (%make :extension-factor extension-factor + :data-vector (make-array initial-storage-size + :element-type 'data-type + :initial-element (cons 0 nil)) + :prio-vector (make-array initial-storage-size + :element-type 'prio-type) + :extend-queue-p extend-queue-p)) + +(defmethod print-object ((object queue) stream) + (print-unreadable-object (object stream :type t :identity t) + (format stream "(~D)" (%size object)))) + +(declaim (ftype (function (queue) (values queue &optional)) copy-queue)) +(defun copy-queue (queue) + (declare (type queue queue)) + (declare #.*optimize-qualities*) + (%make :extension-factor (%extension-factor queue) + :size (%size queue) + :extend-queue-p (%extend-queue-p queue) + :data-vector (cl:map 'data-vector-type + (lambda (a) (cons (car a) (cdr a))) + (%data-vector queue)) + :prio-vector (copy-seq (%prio-vector queue)))) + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;;;; Enqueueing + +(declaim (inline heapify-upwards enqueue)) + +(declaim (ftype (function (data-vector-type prio-vector-type a:array-length) + (values null &optional)) + heapify-upwards)) +(defun heapify-upwards (data-vector prio-vector index) + (declare (type data-vector-type data-vector)) + (declare (type prio-vector-type prio-vector)) + (declare (type a:array-length index)) + (declare #.*optimize-qualities*) + (do ((child-index index parent-index) + (parent-index (ash (1- index) -1) (ash (1- parent-index) -1))) + ((= child-index 0)) + (let ((child-priority (aref prio-vector child-index)) + (parent-priority (aref prio-vector parent-index))) + (cond ((< child-priority parent-priority) + (rotatef (aref prio-vector parent-index) + (aref prio-vector child-index)) + (rotatef (car (aref data-vector parent-index)) + (car (aref data-vector child-index))) + (rotatef (aref data-vector parent-index) + (aref data-vector child-index))) + (t (return)))))) + +(declaim (ftype (function (queue t prio-type) (values cons &optional)) enqueue)) +(defun enqueue (queue object priority) + (declare (type queue queue)) + (declare (type prio-type priority)) + (declare #.*optimize-qualities*) + (symbol-macrolet ((data-vector (%data-vector queue)) + (prio-vector (%prio-vector queue))) + (let* ((size (%size queue)) + (extension-factor (%extension-factor queue)) + (length (array-total-size data-vector)) + (datum (cons size object))) + (when (>= size length) + (unless (%extend-queue-p queue) + (error 'queue-size-limit-reached :queue queue :element object)) + (let ((new-length (max 1 (mod (* length extension-factor) + (ash 1 64))))) + (declare (type a:array-length new-length)) + (when (<= new-length length) + (error "Integer overflow while resizing array: new-length ~D is ~ + smaller than old length ~D" new-length length)) + (setf data-vector (adjust-array data-vector new-length + :initial-element (cons 0 nil)) + prio-vector (adjust-array prio-vector new-length)))) + (setf (aref data-vector size) datum + (aref prio-vector size) priority) + (heapify-upwards data-vector prio-vector (%size queue)) + (incf (%size queue)) + datum))) + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;;;; Dequeueing + +(declaim (inline heapify-downwards dequeue)) + +(declaim (ftype (function (data-vector-type prio-vector-type a:array-index fixnum) + (values null &optional)) + heapify-downwards)) +(defun heapify-downwards (data-vector prio-vector size parent-index) + (declare (type data-vector-type data-vector)) + (declare (type prio-vector-type prio-vector)) + (declare (type fixnum parent-index)) + (declare #.*optimize-qualities*) + (loop + (assert (<= 0 parent-index size)) + (let* ((left-index (+ (* parent-index 2) 1)) + (left-index-validp (< left-index size)) + (right-index (+ (* parent-index 2) 2)) + (right-index-validp (< right-index size))) + (flet ((swap-left () + (rotatef (aref prio-vector parent-index) + (aref prio-vector left-index)) + (rotatef (car (aref data-vector parent-index)) + (car (aref data-vector left-index))) + (rotatef (aref data-vector parent-index) + (aref data-vector left-index)) + (setf parent-index left-index)) + (swap-right () + (rotatef (aref prio-vector parent-index) + (aref prio-vector right-index)) + (rotatef (car (aref data-vector parent-index)) + (car (aref data-vector right-index))) + (rotatef (aref data-vector parent-index) + (aref data-vector right-index)) + (setf parent-index right-index))) + (declare (inline swap-left swap-right)) + (when (and (not left-index-validp) + (not right-index-validp)) + (return)) + (when (and left-index-validp + (<= (aref prio-vector parent-index) + (aref prio-vector left-index)) + (or (not right-index-validp) + (<= (aref prio-vector parent-index) + (aref prio-vector right-index)))) + (return)) + (if (and right-index-validp + (<= (aref prio-vector right-index) + (aref prio-vector left-index))) + (swap-right) + (swap-left)))))) + +(declaim (ftype (function (queue) (values t boolean (or null data-type) + &optional)) + dequeue)) +(defun dequeue (queue) + (declare (type queue queue)) + (declare #.*optimize-qualities*) + (if (= 0 (%size queue)) + (values nil nil nil) + (let ((data-vector (%data-vector queue)) + (prio-vector (%prio-vector queue))) + (multiple-value-prog1 (values (cdr (aref data-vector 0)) + t + (aref data-vector 0)) + (decf (%size queue)) + (let ((old-data (aref data-vector (%size queue))) + (old-prio (aref prio-vector (%size queue)))) + (setf (aref data-vector 0) old-data + (aref prio-vector 0) old-prio + (car (aref data-vector 0)) 0)) + (heapify-downwards data-vector prio-vector (%size queue) 0))))) + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;;;; Introspection and maintenance + +(declaim (inline peek size trim map)) + +(declaim (ftype (function (queue) (values t boolean &optional)) peek)) +(defun peek (queue) + (declare (type queue queue)) + (declare #.*optimize-qualities*) + (if (= 0 (%size queue)) + (values nil nil) + (values (cdr (aref (%data-vector queue) 0)) t))) + +(declaim (ftype (function (queue) (values a:array-length &optional)) size)) +(defun size (queue) + (declare (type queue queue)) + (declare #.*optimize-qualities*) + (%size queue)) + +(declaim (ftype (function (queue) (values null &optional)) trim)) +(defun trim (queue) + (declare (type queue queue)) + (declare #.*optimize-qualities*) + (let ((size (%size queue))) + (setf (%data-vector queue) (adjust-array (%data-vector queue) size) + (%prio-vector queue) (adjust-array (%prio-vector queue) size)) + nil)) + +(declaim (ftype (function (queue (function (t) t)) (values null &optional)) + map)) +(defun map (queue function) + (dotimes (i (%size queue)) + (funcall function (cdr (aref (%data-vector queue) i))))) + +(defmacro do-queue ((object queue &optional result) &body body) + (multiple-value-bind (forms declarations) (a:parse-body body) + (a:with-gensyms (i) + (a:once-only (queue) + `(dotimes (,i (%size ,queue) ,result) + (let ((,object (cdr (aref (%data-vector ,queue) ,i)))) + ,@declarations + (tagbody ,@forms))))))) + +(declaim (inline valid-handle handle-priority handle-data)) + +(declaim (ftype (function (queue data-type) (values t &optional)) + valid-handle)) +(defun valid-handle (queue handle) + (declare (type queue queue) + (type data-type handle)) + (let ((index (car handle))) + (when (and (<= 0 index (1- (%size queue))) + (eq handle (aref (%data-vector queue) index))) + index))) + +(declaim (ftype (function (queue data-type) + (values (or null prio-type) &optional)) + handle-priority)) +(defun handle-priority (queue handle) + (let ((index (valid-handle queue handle)) + (prio-vector (%prio-vector queue))) + (when index + (aref prio-vector index)))) + +(declaim (ftype (function (queue data-type) + (values t boolean &optional)) + handle-data)) +(defun handle-data (queue handle) + (let ((index (valid-handle queue handle))) + (values (cdr handle) (if index t nil)))) + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;;;; Modification + +(declaim (inline delete adjust-priority)) + +(declaim (ftype (function (queue data-type) (values boolean &optional)) delete)) +(defun delete (queue object) + (declare (type queue queue) + (type data-type object)) + (if (= 0 (%size queue)) + nil + (let ((index (valid-handle queue object)) + (data-vector (%data-vector queue)) + (prio-vector (%prio-vector queue))) + (when index + (let ((deleted-prio (aref prio-vector index))) + (decf (%size queue)) + (let ((old-data (aref data-vector (%size queue))) + (old-prio (aref prio-vector (%size queue)))) + (setf (aref data-vector index) old-data + (aref prio-vector index) old-prio) + (setf (car (aref data-vector index)) index) + (cond + ((< old-prio deleted-prio) + (heapify-upwards data-vector prio-vector index)) + ((> old-prio deleted-prio) + (heapify-downwards data-vector prio-vector (%size queue) index))))) + t)))) + +(declaim (ftype (function (queue data-type prio-type) (values boolean &optional)) adjust-priority)) +(defun adjust-priority (queue object new-priority) + (declare (type queue queue) + (type data-type object) + (type prio-type new-priority)) + (let ((index (valid-handle queue object)) + (data-vector (%data-vector queue)) + (prio-vector (%prio-vector queue))) + (when index + (let ((old-priority (aref prio-vector index))) + (setf (aref prio-vector index) new-priority) + (cond + ((< new-priority old-priority) + (heapify-upwards data-vector prio-vector index)) + ((> new-priority old-priority) + (heapify-downwards data-vector prio-vector (%size queue) index)))) + t))) + + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;;;; Conditions + +(defun report-queue-size-limit-reached (condition stream) + (let ((queue (queue-size-limit-reached-queue condition)) + (element (queue-size-limit-reached-object condition))) + (format stream "Size limit (~D) reached for non-extensible ~ + queue ~S while trying to enqueue element ~S onto it." + (length (%data-vector queue)) queue element))) + +(define-condition queue-size-limit-reached (error) + ((%queue :reader queue-size-limit-reached-queue :initarg :queue) + (%object :reader queue-size-limit-reached-object :initarg :element)) + (:default-initargs :queue (a:required-argument :queue) + :object (a:required-argument :object)) + (:report report-queue-size-limit-reached)) diff --git a/damn-fast-updatable-priority-queue/test.lisp b/damn-fast-updatable-priority-queue/test.lisp new file mode 100644 index 0000000..7c47005 --- /dev/null +++ b/damn-fast-updatable-priority-queue/test.lisp @@ -0,0 +1,246 @@ +;;;; damn-fast-updatable-priority-queue-test.lisp + +(defpackage #:damn-fast-updatable-priority-queue/test + (:use #:cl) + (:local-nicknames (#:a #:alexandria) + (#:q #:damn-fast-updatable-priority-queue)) + (:export #:run)) + +(in-package #:damn-fast-updatable-priority-queue/test) + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;;;; Utilities + +(defun verify-heap-property (vector) + (declare (type q::prio-vector-type vector)) + (loop with length = (length vector) + for parent from 0 below (truncate length 2) + for left = (+ (* parent 2) 1) + for right = (+ (* parent 2) 2) + do (assert (<= (aref vector parent) (aref vector left)) () + "VERIFY-HEAP-PROPERTY: Invalid left child: ~D -> ~D" + (aref vector parent) (aref vector left)) + when (oddp length) + do (assert (<= (aref vector parent) (aref vector right)) () + "VERIFY-HEAP-PROPERTY: Invalid right child: ~D -> ~D" + (aref vector parent) (aref vector right)))) + +(defun verify-heap-property-for-queue (queue) + (verify-heap-property (subseq (q::%prio-vector queue) + 0 (q:size queue))) + (loop with data = (q::%data-vector queue) + for i below (q:size queue) + for d = (aref data i) + do (assert (eq d (aref data (car d)))))) + +(defun stringify (i) (format nil "~D" i)) + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;;;; Main interface + +(defvar *verbose* nil) + +(defun run (&optional *verbose*) + (dolist (length (nconc (a:iota 64 :start 1) '(256 1024 4096))) + (when *verbose* (format t "~&Testing with ~4,' D elements" length)) + (let ((queue (q:make-queue (max 1 (ash length -4))))) + (perform-test queue (a:iota length)) + (perform-test queue (nreverse (a:iota length))) + (dotimes (i 100) + (perform-test queue (a:shuffle (a:iota length)))))) + (perform-error-test) + (perform-copy-test) + (when *verbose* (format t "~&done testing.~%"))) + +(defun perform-test (queue list) + (when *verbose* (princ ".")) + (test-enqueue queue list) + (test-map queue list) + (test-do-queue queue list) + (test-dequeue-and-peek queue list) + (test-dequeue-and-peek-empty queue) + (test-trim queue list) + (let ((handles (test-enqueue queue list))) + (test-handles queue handles) + (test-delete queue handles) + (test-adjust queue handles) + (test-handles2 queue handles) + (test-modified-contents queue handles))) + +(defun perform-error-test () + (let ((queue (q:make-queue 4 2 nil))) + (dotimes (i 4) (q:enqueue queue (princ-to-string i) i)) + (flet ((perform () + (multiple-value-bind (value error) + (ignore-errors (q:enqueue queue "4" 4)) + (assert (null value)) + (assert (typep error 'q:queue-size-limit-reached)) + (assert (eq queue (q:queue-size-limit-reached-queue error))) + (assert (string= "4" + (q:queue-size-limit-reached-object error)))))) + (dotimes (i 4) (perform))))) + +(defun perform-copy-test () + (let ((queue-1 (q:make-queue))) + (q:enqueue queue-1 42 1) + (let ((queue-2 (q:copy-queue queue-1))) + (q:enqueue queue-2 24 0) + ;; Check QUEUE-1 + (multiple-value-bind (value foundp) (q:dequeue queue-1) + (assert (= 42 value)) + (assert (eq t foundp))) + (multiple-value-bind (value foundp) (q:dequeue queue-1) + (assert (null value)) + (assert (null foundp))) + ;; Check QUEUE-2 + (multiple-value-bind (value foundp) (q:dequeue queue-2) + (assert (= 24 value)) + (assert (eq t foundp))) + (multiple-value-bind (value foundp) (q:dequeue queue-2) + (assert (= 42 value)) + (assert (eq t foundp))) + (multiple-value-bind (value foundp) (q:dequeue queue-2) + (assert (null value)) + (assert (null foundp)))))) + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;;;; Subtests + +(defun test-enqueue (queue list) + (let ((counter 0) + (handles (make-array (length list)))) + (loop for i in list + for n from 0 + do (setf (aref handles n) + (q:enqueue queue (stringify i) i)) + (assert (= (incf counter) (q:size queue))) + (verify-heap-property-for-queue queue)) + handles)) + +(defun test-map (queue list) + (let ((expected (reduce #'+ list)) + (actual 0)) + (q:map queue (lambda (x) (incf actual (parse-integer x)))) + (assert (= expected actual)))) + +(defun test-do-queue (queue list) + (let ((expected (reduce #'+ list)) + (actual 0)) + (q:do-queue (x queue) (incf actual (parse-integer x))) + (assert (= expected actual)))) + +(defun test-dequeue (queue expected-value expected-foundp) + (multiple-value-bind (value foundp) (q:dequeue queue) + (assert (equal expected-value value)) + (assert (eql expected-foundp foundp)))) + +(defun test-peek (queue expected-value expected-foundp) + (multiple-value-bind (value foundp) (q:peek queue) + (assert (equal expected-value value)) + (assert (eql expected-foundp foundp)))) + +(defun test-dequeue-and-peek (queue list) + (let ((counter (q:size queue))) + (dotimes (i (length list)) + (test-peek queue (stringify i) t) + (assert (= counter (q:size queue))) + (test-dequeue queue (stringify i) t) + (assert (= (decf counter) (q:size queue))))) + (assert (= 0 (q:size queue)))) + +(defun test-dequeue-and-peek-empty (queue) + (test-peek queue nil nil) + (assert (= 0 (q:size queue))) + (test-dequeue queue nil nil) + (assert (= 0 (q:size queue)))) + +(defun test-trim (queue list) + (assert (<= (length list) (length (q::%prio-vector queue)))) + (assert (<= (length list) (length (q::%data-vector queue)))) + (q:trim queue) + (assert (= 0 (length (q::%prio-vector queue)))) + (assert (= 0 (length (q::%data-vector queue))))) + +(defun test-handles (queue handles) + (loop for h across handles + do (assert (and (q:handle-data queue h) + (q:handle-priority queue h) + (= (q:handle-priority queue h) + (parse-integer (q:handle-data queue h))))))) + +(defun test-delete (queue handles) + (assert (not (q:delete queue (cons 0 nil)))) + (assert (not (q:delete queue (cons 1 nil)))) + (let ((size (q:size queue)) + (deleted 0)) + (loop for i from 0 + for h across handles + when (zerop (mod i 5)) + do (assert (q:delete queue h)) + (incf deleted) + (verify-heap-property-for-queue queue)) + (assert (= (q:size queue) (- size deleted))))) + +(defun test-adjust (queue handles) + (loop for i from 0 + for h across handles + when (zerop (mod i 5)) ;; deleted previously + do (assert (not (q:adjust-priority queue h 1))) + else when (zerop (mod i 3)) + do (let* ((old (q:handle-priority queue h)) + (new (+ old (cond + ((and (oddp i) (> old 5)) + -5) + ((oddp i) + 11) + (t 7))))) + (assert (q:adjust-priority queue h new)) + (assert (= (q:handle-priority queue h) new)) + (verify-heap-property-for-queue queue)))) + +(defun test-handles2 (queue handles) + (loop for h across handles + for i from 0 + do (cond + ((zerop (mod i 5)) + (assert (not (q:handle-priority queue h))) + (assert (not (q:handle-data queue h)))) + ((zerop (mod i 3)) + (let ((p (parse-integer (q:handle-data queue h)))) + (cond + ((and (oddp i) (> p 5)) + (assert (= (q:handle-priority queue h) + (- p 5)))) + ((oddp i) + (assert (= (q:handle-priority queue h) + (+ p 11)))) + (t (+ p 7))))) + (t + (assert (= (q:handle-priority queue h) + (parse-integer (q:handle-data queue h)))))))) + +(defun test-modified-contents (queue handles) + (let ((mod (make-hash-table))) + (loop for i from 0 + for h across handles + for e = (cdr h) + for n = (when e (parse-integer e)) + do (setf (gethash e mod) + (cond + ((zerop (mod i 5)) + nil) + ((and (zerop (mod i 3)) (oddp i) (> n 5)) + -5) + ((and (zerop (mod i 3)) (oddp i)) 11) + ((zerop (mod i 3)) 7) + (t 0)))) + (loop with prev = -1 + for e = (q:dequeue queue) + for d = (gethash e mod) + while e + do (let* ((n (parse-integer e))) + (assert d) + (assert (>= (+ n d) prev)) + (setf prev (+ n d)))))) + + diff --git a/priority-queue-benchmark/benchmark.lisp b/priority-queue-benchmark/benchmark.lisp index 5b29910..cc0e625 100644 --- a/priority-queue-benchmark/benchmark.lisp +++ b/priority-queue-benchmark/benchmark.lisp @@ -26,11 +26,12 @@ 'test-cl-heap 'test-heap 'test-minheap - 'test-damn-fast-priority-queue - 'test-damn-fast-stable-priority-queue)) + #'test-damn-fast-priority-queue + #'test-damn-fast-updatable-priority-queue + #'test-damn-fast-stable-priority-queue)) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -;;;; Performance testq +;;;; Performance test (defun perform-test (name vector-name vector &key make-fn push-fn peek-fn pop-fn) @@ -193,6 +194,18 @@ :peek-fn (lambda (q) (damn-fast-priority-queue:peek q)) :pop-fn (lambda (q) (damn-fast-priority-queue:dequeue q)))) +(defun test-damn-fast-updatable-priority-queue (vector-name vector) + (declare (optimize speed)) + (perform-test + :damn-fast-updatable-priority-queue + vector-name vector + :make-fn (lambda () (if +pass-capacity-p+ + (damn-fast-updatable-priority-queue:make-queue +capacity+) + (damn-fast-updatable-priority-queue:make-queue))) + :push-fn (lambda (q i) (damn-fast-updatable-priority-queue:enqueue q i i)) + :peek-fn (lambda (q) (damn-fast-updatable-priority-queue:peek q)) + :pop-fn (lambda (q) (damn-fast-updatable-priority-queue:dequeue q)))) + (defun test-damn-fast-stable-priority-queue (vector-name vector) (declare (optimize speed)) (perform-test