pq.sire (4321B)
1 ; Copyright 2023 The Plunder Authors 2 ; Use of this source code is governed by a BSD-style license that can be 3 ; found in the LICENSE file. 4 5 #### pq <- splitmix 6 7 ;;;; Priority Queues 8 ;;;; =============== 9 ;;;; 10 ;;;; This implementation is based on section 4.16 of the book 'ML for the 11 ;;;; Working Programmer' by Larry C. Paulson (Cambridge University Press 12 ;;;; [1996]). 13 ;;;; 14 ;;;; It is modified to support a customizeable comparator function (`cmp`, 15 ;;;; below) which is passed as an argument to many `pq.*` functions. This 16 ;;;; allows similar functionality to, for example, a Haskell `Ord` 17 ;;;; instance. `cmp` takes 2 arguments and outputs a LT/EQ/GT value 18 ;;;; (see 03_nat.sire) indicating their relation to each other. 19 20 21 ;;; Imports ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; 22 23 :| sire_02_bit [if and TRUE FALSE] 24 :| sire_03_nat [LT EQ GT div sub] 25 :| sire_04_cmp [eql cmp neq] 26 :| sire_07_dat [listLen listUnsafeHead listDrop NIL listRev listFoldr] 27 :| sire_07_dat [SOME NONE maybeCase] 28 :| prelude ; 29 :| stew ; 30 31 32 ;;; Types ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; 33 34 # data (Pq a) 35 - LF 36 - BR a Pq Pq 37 38 = pqEmpty LF 39 40 = (pqNull t) 41 | (t == pqEmpty) 42 43 =?= 1 | pqNull LF 44 =?= 0 | pqNull (BR 7 LF LF) 45 46 = (pqInsert cmp w t) 47 ^ _ w t 48 ? (go w t) 49 # case t 50 - LF 51 | BR w LF LF 52 - BR v t1 t2 53 | if (cmp w v == GT) | (BR v (go w t2) t1) 54 | else | (BR w (go v t2) t1) 55 56 = (leftRem cmp t) 57 # case t 58 - LF [NONE t] 59 - BR w t1 t2 60 | if (pqNull t1 && pqNull t2) 61 | (SOME w, LF) 62 | else 63 @ [mbW t] (leftRem cmp t1) 64 | (mbW, BR w t2 t) 65 66 = test1 | pqInsert cmp 0 pqEmpty 67 = test2 | pqInsert cmp 1 test1 68 = test3 | leftRem cmp test2 69 70 = (siftDown cmp w t1 t2) 71 ^ _ w t1 t2 72 ? (go w t1 t2) 73 # case t1 74 - LF 75 # case t2 76 - LF (BR w LF LF) 77 - _ (go w t2 t1) 78 - BR v1 t1L t1R 79 # case t2 80 - LF 81 | if (and (pqNull t1L) (pqNull t1R)) 82 | if (cmp w v1 == GT) 83 | BR v1 (BR w LF LF) LF 84 | else 85 | BR w t1 LF 86 | die {siftDown: wat} 87 - BR v2 t2L t2R 88 | if ((GT /= cmp w v1) && (GT /= cmp w v2)) 89 | BR w t1 t2 90 | if (GT == cmp v1 v2) 91 | BR v2 t1 (go w t2L t2R) 92 | else 93 | BR v1 (go w t1L t1R) t2 94 95 = (pqDelMin cmp t) 96 # case t 97 - LF [NONE LF] 98 - BR v t1 t2 99 @ [mbW t] (leftRem cmp t1) 100 : w < maybeCase mbW (SOME v, LF) 101 | (SOME v, siftDown cmp w t2 t) 102 103 = (pqMin t) 104 # case t 105 - LF | NONE 106 - BR w _ _ | SOME w 107 108 =?= (SOME 7) | pqMin (BR 7 LF LF) 109 110 = (heapify cmp ls) 111 ^ _ (listLen ls) ls 112 ? (go n vs) 113 | ifz n | [LF vs] 114 @ v | listUnsafeHead vs 115 @ vs | listDrop 1 vs 116 @ [t1 vs1] | go (div n 2) vs 117 @ [t2 vs2] | go (div (sub n 1) 2) vs1 118 | (siftDown cmp v t1 t2, vs2) 119 120 = (pqFromList cmp vs) 121 | idx 0 (heapify cmp vs) 122 123 = (pqFromList0 cmp vs) 124 | listFoldl (acc x & pqInsert cmp x acc) pqEmpty vs 125 126 = (pqToList cmp t) 127 ^ _ NIL t 128 ? (go acc t) 129 @ [mbW t1] (pqDelMin cmp t) 130 : w < maybeCase mbW (listRev acc) 131 | go (w::acc) t1 132 133 = (pqSort cmp xs) 134 | pqToList cmp 135 | pqFromList cmp 136 | xs 137 138 = (pqSort0 cmp xs) 139 | pqToList cmp 140 | pqFromList0 cmp 141 | xs 142 143 =?= ~[0 1 2 3 5 7 9 9] 144 | pqSort cmp ~[1 9 7 5 2 0 9 3] 145 146 =?= ~[0 1 2 3 5 7 9 9] 147 | pqSort0 cmp ~[1 9 7 5 2 0 9 3] 148 149 ;; more sophisticated tests, examining laziness of pq values. 150 ;; we ensure that infinite values at position 1 of a tuple can pass in/out of 151 ;; pqs, provided they are not touched. 152 153 = (cmpPairNatKey p1 p2) 154 | cmp idx-0-p1 idx-0-p2 155 156 = (repeat n) 157 @@ z=(n::z) 158 z 159 160 =?= 9 161 # case 162 | pqMin 163 | pqInsert cmpPairNatKey (9, repeat 3) pqEmpty 164 - NONE | 7 165 - SOME x | idx 0 x 166 167 ; insert in ascending order of key 168 =?= 110 169 @ ls0 : x < listGen 11 170 | (x, repeat x) 171 @ pq (pqFromList0 cmpPairNatKey ls0) 172 @ ls1 (pqToList cmpPairNatKey pq) 173 | listSum : p < listForEach ls1 174 | add idx-0-p (listUnsafeHead idx-1-p) 175 176 ; insert in descending order of key 177 178 =?= 110 179 @ ls0 : x < listGen 11 180 | (sub 10 x, repeat (sub 10 x)) 181 @ pq (pqFromList0 cmpPairNatKey ls0) 182 @ ls1 (pqToList cmpPairNatKey pq) 183 | listSum : p < listForEach ls1 184 | add idx-0-p (listUnsafeHead idx-1-p) 185 186 187 ;;; Exports ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; 188 189 ^-^ 190 ^-^ pqEmpty pqInsert pqDelMin pqMin pqNull 191 ^-^ pqFromList pqToList pqSort 192 ^-^