-
Notifications
You must be signed in to change notification settings - Fork 0
/
heap_debug.psl
326 lines (302 loc) · 11.7 KB
/
heap_debug.psl
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
import JJH::*
import *
interface Heapable<> is
op "=?"(Left, Right : Heapable) -> Orders::Full_Ordering
func To_String(Val : Heapable) -> Univ_String
// Also assignable, but everything is Assignable<>
end interface Heapable;
//--------TODO--------------------------------------------------------
// Wait for ability to return multiple things
//--------------------------------------------------------------------
// log(n) insertion and removal heap implemented on ZVector
// Left child is located at 2 * n + 1
// Right child is located at 2 * n + 2 where n is the index of the current node
// Parent is located at (n - 1) / 2
// This allows the tree to be represented as an array
concurrent interface Heap<E is Heapable<>; Pole : Orders::Polarity := #less> is
// create empty Heap
op "[]"() -> Heap is create;
// adds element to heap
op "|="(locked var H : Heap; Elem : E) {Length(H') == Length(H) + 1};
op "|"(Left, Right : E) -> Heap;
op "|"(Left : Heap; Right : E) -> Heap;
op "|"(Left : E; Right : Heap) -> Heap;
op "|"(Left : Heap; Right : Heap) -> Heap;
op "magnitude"(H : Heap) -> Univ_Integer is Length;
// shallow copy. Fresh array, not fresh elements
func Copy(locked H : Heap) -> Heap;
// creates a new empty heap with the given polarity
func Create() -> Heap;
// returns number of elements
func Length(H : Heap) -> Univ_Integer;
// pops highest priority from heap
// return null if H is empty
func Pop(locked var H : Heap) -> optional E {Length(H') <= Length(H)};
// returns a string representation of the heap
func To_String(locked H : Heap) -> Univ_String<>;
// returns the elements organized into a tree in a String
func To_String_Tree(locked H : Heap) -> Univ_String<>;
end interface Heap;
concurrent class Heap<E is Heapable<>; Pole : Orders::Polarity := #less> is
// a polarity of #less will pop smallest first, #greater will pop largest first
type Int_ZVector is ZVector<Univ_Integer>; //ZVector of Integers
// Array of strings indexed by Integers
type Str_Array is Array<Univ_String, Univ_Integer>;
var Data : ZVector<E>; //Data storage for Heap
var Size : Univ_Integer; //Number of non-null elements in Data
// Preserve Heap Order (Parent relation to both children must match Pole)
// Start at bottom and recurse up the tree
func Preserve_Order_Up(locked var H : Heap; N : Integer) is
if N == 0 then
return;
end if;
const Parent_Index := (N - 1) / 2;
const Child_Parent : Ordering := H.Data[N] =? H.Data[Parent_Index];
if Child_Parent == Pole then
H.Data[N] <=> H.Data[Parent_Index]
Preserve_Order_Up(H, Parent_Index)
end if;
end func Preserve_Order_Up;
// Recurses down the tree. Swaps nodes if necessary to preserve heap order
func Preserve_Order_Down(locked var H : Heap; N : Univ_Integer) is
const L := 2 * N + 1;
if L >= H.Size then // If we have no left child, then we have no children
return; // and there's nothing to check
end if;
const R := 2 * N + 2;
const Has_Right : Boolean := R < H.Size; //Check for right child
const Left_To_Parent : Ordering := H.Data[L] =? H.Data[N];
var Right_To_Parent : Ordering;
if Has_Right then
Right_To_Parent := H.Data[R] =? H.Data[N];
end if;
// "and then" used in this if statement (short-circuit boolean operator)
// so that we won't use Right_To_Parent if it's null
if Left_To_Parent == Pole or
(Has_Right and then Right_To_Parent == Pole) then
if Has_Right then // One or both children out of order
const Left_To_Right := H.Data[L] =? H.Data[R];
const Right_To_Left := H.Data[R] =? H.Data[L];
// swap with the higher priority (lesser if Pole == #less
// greater if Pole == #greater)
if Left_To_Right == Pole then
H.Data[N] <=> H.Data[L];
Preserve_Order_Down(H, L); // Recurse down left subtree
elsif Right_To_Left == Pole or Right_To_Left == #equal then
H.Data[N] <=> H.Data[R];
Preserve_Order_Down(H, R); // Recurse down right subtree
end if;
else // Something is out of order and there is no right child
// It must be the left child then
H.Data[N] <=> H.Data[L];
// No need to recurse because
// Rows are filled to completion before next row
end if;
end if;
end func Preserve_Order_Down;
// divide and conquer recursion for To_String
func To_String_Helper(locked H : Heap; Low, High : Univ_Integer) -> Univ_String is
const Len := High - Low;
if Len == 0 then
return ""; // Nothing here
elsif Len == 1 then
return H.Data[Low].To_String(); // Only one thing, return its To_String
end if;
// Split in half and make recursive call with a comma in the middle
const Half := Low + Len / 2;
return H.To_String_Helper(Low, Half) | ", " | H.To_String_Helper(Half, High);
end func To_String_Helper;
// TODO: wait for ability to return multiple things
// instead of passing widths as a var parameter
// Returns the Height of the tree and updates a vector of tree widths
// for use by To_String_Tree
func Tree_Height(var Widths : Int_ZVector) -> Univ_Integer is
var Width := 1;
var Height := 0;
var Col := 0;
for I in 0 ..< Widths.Length() forward loop
Widths[I] := Width;
Col += 1;
if Col == 1 then // At least one element on this row, Height increases
Height += 1;
end if;
if Col == Width then
Width *= 2; // Width doubles at each level of the tree
Col := 0;
end if;
end loop
return Height;
end func Tree_Height;
// TODO: wait for ability to return multiple things
// Return size of largest element To_String and update Strings array with
// either spaces added if smaller than largest or
// trimmed if larger than imposed max size
// for use by To_String_Tree
func To_Strings_Elem_Size(locked H : Heap; var Strings : Str_Array)
-> Univ_Integer is
// Find max element size
var Max_Elem_Size := 0;
for I in 0 ..< Strings.Length() forward loop
const Size := H.Data[I].To_String().Length();
if Size > Max_Elem_Size then
Max_Elem_Size := Size;
end if;
end loop;
const Imposed_Max := 5
// Tree To_String grows in width very quickly, trim the To_Strings to make
// the size manigable
if Max_Elem_Size > Imposed_Max then
Max_Elem_Size := Imposed_Max;
end if;
for I in 0 ..< Strings.Length() concurrent loop
const Str := H.Data[I].To_String();
if Str.Length() <= Max_Elem_Size then
// Add blanks to beginning
Strings[I] := (" " * (Max_Elem_Size - Str.Length())) | Str;
elsif Str.Length() > Max_Elem_Size then
// only use beginning up to max
Strings[I] := Str[1 .. Max_Elem_Size - 1] | "…";
end if
end loop;
return Max_Elem_Size;
end func To_Strings_Elem_Size;
// Move all elements from smaller into larger
func Move(var Smaller : Heap; var Larger : Heap) -> Heap is
for i in 0 ..< Length(Smaller) concurrent loop
Larger |= Smaller.Pop();
end loop;
return Larger;
end func Move;
exports
op "|="(locked var H : Heap; Elem : E) {Length(H') == Length(H) + 1} is
if H.Data.Length() == H.Size then //no nulls, concatenate onto end
H.Data |= Elem;
else // there are some nulls at the end, replace the first one
H.Data[H.Size] := Elem;
end if;
H.Size += 1;
// Traverse up the tree Preserving heap order
Preserve_Order_Up(H, H.Size - 1);
end op "|=";
func Create() -> Heap is
return (Data => [], Size => 0);
end func Create;
op "|"(Left, Right : E) -> Heap is
var Result := Create();
Result |= Left;
Result |= Right;
return Result;
end op "|";
op "|"(Left : Heap; Right : E) -> Heap is
var Result := Copy(Left);
Result |= Right;
return Result;
op "|"(Left : E; Right : Heap) -> Heap is
return Right | Left
end op "|";
op "|"(Left : Heap; Right : Heap) -> Heap is
if Length(Left) < Length(Right) then
var Smaller := Copy(Left);
var Larger := Copy(Right);
return Move(Smaller, Larger);
else
var Smaller := Copy(Right);
var Larger := Copy(Left);
return Move(Smaller, Larger);
end if;
end op "|";
func Copy(locked H : Heap) -> Heap is
return (Data => H.Data[0 ..< Length(H.Data)], Size => H.Size);
end func Copy;
func Length(H : Heap) -> Univ_Integer is
return H.Size;
end func Length;
func Pop(locked var H : Heap) -> optional E {Length(H') <= Length(H)} is
if H.Size == 0 then
return null;
end if;
H.Data[0] <=> H.Data[H.Size - 1];
const To_Return <== H.Data[H.Size - 1];
H.Size -= 1;
Preserve_Order_Down(H, 0);
return To_Return;
end func Pop;
func To_String(locked H : Heap) -> Univ_String<> is
return "[" | H.To_String_Helper(0, H.Size) | "]";
end func To_String;
// Fancy tree representation of Heap
func To_String_Tree(locked H : Heap) -> Univ_String is
// const H_Copy : Heap := H;
var Strings : Str_Array := Str_Array::Create(0 ..< H.Size, "");
const Elem_Size : Univ_Integer := H.To_Strings_Elem_Size(Strings);
var Widths : Int_ZVector := Int_ZVector::Create(H.Size, 0);
const Height := Tree_Height(Widths);
var Col := 0;
var Row := 0;
var Result : Univ_String := "";
// ? operator used to ensure 2**negative doesn't happen
// Number of blanks to the left of the root
const Root_Left_Blanks := Height - Row >= 1 ?
Elem_Size * (2**(Height - Row - 1) - 1) : 0;
// Useful to know that "string" * negative returns ""
Result |= " " * Root_Left_Blanks;
for I in 0 ..< H.Size forward loop
Col += 1;
if Col == 1 then
Row += 1;
end if;
Result |= Strings[I];
if Col == Widths[I] then
Result |= "\n";
// Number of blanks to the left of first element on this row
const Left_Blanks := Height - Row >= 1 ?
Elem_Size * (2**(Height - Row - 1) - 1) : 0;
Result |= " " * Left_Blanks;
Col := 0;
else
// Number of blanks between elements of the same row
const Inter_Blanks := Height - Row >= 0 ?
Elem_Size * (2 * 2**(Height - Row) - 1) : 0;
Result |= " " * Inter_Blanks;
end if;
end loop
return Result;
end func To_String_Tree;
end class Heap;
func Test() is
type Int_Heap is Heap<Univ_Integer, #less>;
var H : Int_Heap := Int_Heap::Create();
for J in 0 ..< 3 concurrent loop
for I in 0 ..< 3 concurrent loop
H |= I + J;
end loop;
end loop;
Println(H.To_String_Tree());
for I in 0 .. 10 forward loop
Println("Popped " | H.Pop());
//if I == 5 then
// for J in 10000 ..< 10010 concurrent loop
// H |= J * 10;
// end loop;
//end if;
Println(H.To_String_Tree());
end loop;
H |= 0;
Println(H.To_String_Tree());
type Char_Heap is Heap<Univ_Character, #less>;
var Letters : Char_Heap := Create();
Letters |= 'a';
Println("a == " | Letters.Pop());
Letters |= 'b';
Letters |= 'c';
Letters |= 'a';
Println("a == " | Letters.Pop());
Println("b == " | Letters.Pop());
Println("c == " | Letters.Pop());
Letters |= 'c';
Letters |= 'b';
Letters |= 'a';
Println("a == " | Letters.Pop());
Println("b == " | Letters.Pop());
Println("c == " | Letters.Pop());
end func Test;