deque.bend
bend-kit-collections@0.1.2.0 · 289 lines · 0 definitions
1# Deque: a double-ended queue, as a front list and a reversed back list.
2#
3# Pushing and popping on either end is amortized O(1), in any mix of ends
4# (the refill below says why). Like List, the queries consume the deque: a
5# Data deque is shared with +.
6#
7# The laws at the bottom are checked every time this file is imported.
8import Base
9
10type Deque<a, -A: Kind(a)> is Kind(a):
11 Deq{front: List<a, A>, back: List<a, A>}
12
13def Deque.new(a, -A: Kind(a)) -> Deque<a, A>:
14 Deq{Nil{}, Nil{}}
15
16# the list's first element is the deque's front
17def Deque.from_list(a, -A: Kind(a), xs: List<a, A>) -> Deque<a, A>:
18 Deq{xs, Nil{}}
19
20def Deque.push_front(a, -A: Kind(a), x: A, d: Deque<a, A>) -> Deque<a, A>:
21 match d:
22 case Deq{f, b}:
23 Deq{x <> f, b}
24
25def Deque.push_back(a, -A: Kind(a), x: A, d: Deque<a, A>) -> Deque<a, A>:
26 match d:
27 case Deq{f, b}:
28 Deq{f, x <> b}
29
30def Deque.count.put(
31 a, -A: Kind(a), h: A, rn: List<a, A> & Nat
32) -> List<a, A> & Nat:
33 (rest, n) = rn
34 (h <> rest, 1n+n)
35
36# a list and its length, in one pass that hands the list back
37def Deque.count(a, -A: Kind(a), xs: List<a, A>) -> List<a, A> & Nat:
38 match xs:
39 case Nil{}:
40 (Nil{}, 0n)
41 case h <> t:
42 Deque.count.put(a, A, h, Deque.count(a, A, t))
43
44def Deque.half(n: Nat) -> Nat:
45 match n:
46 case 0n:
47 0n
48 case 1n+p:
49 match p:
50 case 0n:
51 0n
52 case 1n+q:
53 1n+Deque.half(q)
54
55def Deque.split.put(
56 a, -A: Kind(a), h: A, lr: List<a, A> & List<a, A>
57) -> List<a, A> & List<a, A>:
58 (l, r) = lr
59 (h <> l, r)
60
61# the first n elements, and the rest
62def Deque.split(a, -A: Kind(a), n: Nat, xs: List<a, A>) ->
63 List<a, A> & List<a, A>:
64 match n:
65 case 0n:
66 (Nil{}, xs)
67 case 1n+p:
68 match xs:
69 case Nil{}:
70 (Nil{}, Nil{})
71 case h <> t:
72 Deque.split.put(a, A, h, Deque.split(a, A, p, t))
73
74# When one end runs out, the other list is split in half and only the half
75# nearest the empty end is reversed over. Moving everything would let pops
76# that alternate ends reverse the whole deque each time; halving keeps the
77# lists balanced, so every operation stays amortized O(1).
78
79def Deque.pop_front.go(
80 a, -A: Kind(a), xs: List<a, A>, b: List<a, A>
81) -> Maybe<&1, A & Deque<a, A>>:
82 match xs:
83 case Nil{}:
84 None{}
85 case h <> t:
86 Some{(h, Deq{t, b})}
87
88def Deque.pop_front.halves(
89 a, -A: Kind(a), sm: List<a, A> & List<a, A>
90) -> Maybe<&1, A & Deque<a, A>>:
91 (stay, move) = sm
92 Deque.pop_front.go(a, A, List.reverse(a, A, move), stay)
93
94def Deque.pop_front.refill(
95 a, -A: Kind(a), bn: List<a, A> & Nat
96) -> Maybe<&1, A & Deque<a, A>>:
97 (b, n) = bn
98 Deque.pop_front.halves(a, A, Deque.split(a, A, Deque.half(n), b))
99
100def Deque.pop_front(
101 a, -A: Kind(a), d: Deque<a, A>
102) -> Maybe<&1, A & Deque<a, A>>:
103 match d:
104 case Deq{f, b}:
105 match f:
106 case h <> t:
107 Some{(h, Deq{t, b})}
108 case Nil{}:
109 Deque.pop_front.refill(a, A, Deque.count(a, A, b))
110
111def Deque.pop_back.go(
112 a, -A: Kind(a), xs: List<a, A>, f: List<a, A>
113) -> Maybe<&1, A & Deque<a, A>>:
114 match xs:
115 case Nil{}:
116 None{}
117 case h <> t:
118 Some{(h, Deq{f, t})}
119
120def Deque.pop_back.halves(
121 a, -A: Kind(a), sm: List<a, A> & List<a, A>
122) -> Maybe<&1, A & Deque<a, A>>:
123 (stay, move) = sm
124 Deque.pop_back.go(a, A, List.reverse(a, A, move), stay)
125
126def Deque.pop_back.refill(
127 a, -A: Kind(a), fn: List<a, A> & Nat
128) -> Maybe<&1, A & Deque<a, A>>:
129 (f, n) = fn
130 Deque.pop_back.halves(a, A, Deque.split(a, A, Deque.half(n), f))
131
132def Deque.pop_back(
133 a, -A: Kind(a), d: Deque<a, A>
134) -> Maybe<&1, A & Deque<a, A>>:
135 match d:
136 case Deq{f, b}:
137 match b:
138 case h <> t:
139 Some{(h, Deq{f, t})}
140 case Nil{}:
141 Deque.pop_back.refill(a, A, Deque.count(a, A, f))
142
143# the elements, front to back
144def Deque.to_list(a, -A: Kind(a), d: Deque<a, A>) -> List<a, A>:
145 match d:
146 case Deq{f, b}:
147 List.append(a, A, f, List.reverse(a, A, b))
148
149def Deque.length(a, -A: Kind(a), d: Deque<a, A>) -> Nat:
150 match d:
151 case Deq{f, b}:
152 Nat.add(List.length(a, A, f), List.length(a, A, b))
153
154def Deque.is_empty(a, -A: Kind(a), d: Deque<a, A>) -> Bool:
155 match d:
156 case Deq{f, b}:
157 Bool.and(List.is_empty(a, A, f), List.is_empty(a, A, b))
158
159# Laws
160# ----
161# Each law reads a deque through to_list, the only view a user has of it.
162
163# LAW: a new deque reads as the empty list
164law Deque.to_list.new:
165 for -A: Data
166 {Deque.to_list(&2, A, Deque.new(&2, A)) == Nil{} : List<&2, A>}
167
168def Deque.to_list.new(A):
169 {==}
170
171# LAW: push_front puts the element at the head of the list
172law Deque.to_list.push_front:
173 for -A: Data
174 for -x: A
175 for d: Deque<&2, A>
176 {Deque.to_list(&2, A, Deque.push_front(&2, A, x, d))
177 == x <> Deque.to_list(&2, A, d) : List<&2, A>}
178
179def Deque.to_list.push_front(A, x, d):
180 match d:
181 case Deq{f, b}:
182 {==}
183
184# LAW: pop_front undoes push_front
185law Deque.pop_front.push_front:
186 for -A: Data
187 for -x: A
188 for d: Deque<&2, A>
189 {Deque.pop_front(&2, A, Deque.push_front(&2, A, x, d))
190 == Some{(x, d)} : Maybe<&1, A & Deque<&2, A>>}
191
192def Deque.pop_front.push_front(A, x, d):
193 match d:
194 case Deq{f, b}:
195 {==}
196
197# LAW: pop_back undoes push_back
198law Deque.pop_back.push_back:
199 for -A: Data
200 for -x: A
201 for d: Deque<&2, A>
202 {Deque.pop_back(&2, A, Deque.push_back(&2, A, x, d))
203 == Some{(x, d)} : Maybe<&1, A & Deque<&2, A>>}
204
205def Deque.pop_back.push_back(A, x, d):
206 match d:
207 case Deq{f, b}:
208 {==}
209
210# Lemmas over Base's List, by induction on the first list
211
212law Deque.lemma.append_nil:
213 for -A: Data
214 for xs: List<&2, A>
215 {List.append(&2, A, xs, Nil{}) == xs : List<&2, A>}
216
217def Deque.lemma.append_nil(A, xs):
218 match xs:
219 case Nil{}:
220 {==}
221 case h <> t:
222 %Deque.lemma.append_nil(A, t) : {h <> List.append(&2, A, t, Nil{})
223 == h <> _ : List<&2, A>}
224 {==}
225
226law Deque.lemma.append_assoc:
227 for -A: Data
228 for xs: List<&2, A>
229 for -ys: List<&2, A>
230 for -zs: List<&2, A>
231 {List.append(&2, A, List.append(&2, A, xs, ys), zs)
232 == List.append(&2, A, xs, List.append(&2, A, ys, zs)) : List<&2, A>}
233
234def Deque.lemma.append_assoc(A, xs, ys, zs):
235 match xs:
236 case Nil{}:
237 {==}
238 case h <> t:
239 %Deque.lemma.append_assoc(A, t, ys, zs) : {h <> List.append(&2, A,
240 List.append(&2, A, t, ys), zs) == h <> _ : List<&2, A>}
241 {==}
242
243# LAW: a list read into a deque reads back as itself
244law Deque.to_list.from_list:
245 for -A: Data
246 for xs: List<&2, A>
247 {Deque.to_list(&2, A, Deque.from_list(&2, A, xs)) == xs : List<&2, A>}
248
249def Deque.to_list.from_list(A, xs):
250 Deque.lemma.append_nil(A, xs)
251
252# reversing onto an accumulator is reversing, then appending it
253law Deque.lemma.reverse_go:
254 for -A: Data
255 for xs: List<&2, A>
256 for -acc: List<&2, A>
257 {List.append(&2, A, List.reverse.go(&2, A, xs, Nil{}), acc)
258 == List.reverse.go(&2, A, xs, acc) : List<&2, A>}
259
260def Deque.lemma.reverse_go(A, xs, acc):
261 match xs:
262 case Nil{}:
263 {==}
264 case +h <> +t:
265 %Deque.lemma.reverse_go(A, t, [h]) : {List.append(&2, A, _, acc)
266 == List.reverse.go(&2, A, t, h <> acc) : List<&2, A>}
267 %Deque.lemma.reverse_go(A, t, h <> acc) : {List.append(&2, A,
268 List.append(&2, A, List.reverse.go(&2, A, t, Nil{}), [h]), acc)
269 == _ : List<&2, A>}
270 Deque.lemma.append_assoc(A, List.reverse.go(&2, A, t, Nil{}), [h], acc)
271
272# LAW: push_back puts the element at the end of the list
273law Deque.to_list.push_back:
274 for -A: Data
275 for -x: A
276 for d: Deque<&2, A>
277 {Deque.to_list(&2, A, Deque.push_back(&2, A, x, d))
278 == List.append(&2, A, Deque.to_list(&2, A, d), [x]) : List<&2, A>}
279
280def Deque.to_list.push_back(A, x, d):
281 match d:
282 case Deq{+f, +b}:
283 %Deque.lemma.reverse_go(A, b, [x]) : {List.append(&2, A, f, _)
284 == List.append(&2, A, List.append(&2, A, f, List.reverse(&2, A, b)),
285 [x]) : List<&2, A>}
286 %Deque.lemma.append_assoc(A, f, List.reverse(&2, A, b), [x]) : {_
287 == List.append(&2, A, List.append(&2, A, f, List.reverse(&2, A, b)),
288 [x]) : List<&2, A>}
289 {==}