d_heap 0.3.0 → 0.7.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.clang-format +21 -0
- data/.github/workflows/main.yml +18 -3
- data/.gitignore +1 -0
- data/.rubocop.yml +32 -2
- data/.yardopts +10 -0
- data/CHANGELOG.md +93 -0
- data/D +7 -0
- data/README.md +416 -154
- data/d_heap.gemspec +20 -8
- data/docs/benchmarks-2.txt +93 -0
- data/docs/benchmarks-mem.txt +39 -0
- data/docs/benchmarks.txt +686 -0
- data/docs/profile.txt +358 -0
- data/ext/d_heap/.rubocop.yml +7 -0
- data/ext/d_heap/d_heap.c +917 -295
- data/ext/d_heap/extconf.rb +45 -3
- data/images/push_n.png +0 -0
- data/images/push_n_pop_n.png +0 -0
- data/images/push_pop.png +0 -0
- data/images/wikipedia-min-heap.png +0 -0
- data/lib/d_heap.rb +116 -3
- data/lib/d_heap/version.rb +1 -1
- metadata +33 -17
- data/.rspec +0 -3
- data/.travis.yml +0 -6
- data/Gemfile +0 -11
- data/Gemfile.lock +0 -67
- data/Rakefile +0 -20
- data/bin/console +0 -15
- data/bin/rake +0 -29
- data/bin/rspec +0 -29
- data/bin/rubocop +0 -29
- data/bin/setup +0 -8
- data/ext/d_heap/d_heap.h +0 -41
data/docs/profile.txt
ADDED
@@ -0,0 +1,358 @@
|
|
1
|
+
Profiling run at 2021-02-01 00:43:34 -0500
|
2
|
+
ruby v2.7.2, DHeap v0.6.1
|
3
|
+
|
4
|
+
~~~~~~ filling @dheap_bm_random_vals with 1.0M ~~~~~~
|
5
|
+
########################################################################
|
6
|
+
# Profile w/ N=10 (i=1000000)
|
7
|
+
# (n.b. RubyProf & tracepoint can change relative performance.
|
8
|
+
# A sampling profiler can provide more accurate relative metrics.
|
9
|
+
########################################################################
|
10
|
+
|
11
|
+
Filling ruby binary heap ---------------------------
|
12
|
+
Profiling ruby binary heap ---------------------------
|
13
|
+
Measure Mode: wall_time
|
14
|
+
Thread ID: 1400
|
15
|
+
Fiber ID: 1380
|
16
|
+
Total: 5.194751
|
17
|
+
Sort by: self_time
|
18
|
+
|
19
|
+
%self total self wait child calls name location
|
20
|
+
41.50 2.156 2.156 0.000 0.000 1000000 DHeap::Benchmarks::RbHeap#<< /home/nick/src/d_heap/lib/d_heap/benchmarks/implementations.rb:107
|
21
|
+
36.37 2.137 1.889 0.000 0.247 1000000 DHeap::Benchmarks::RbHeap#pop /home/nick/src/d_heap/lib/d_heap/benchmarks/implementations.rb:121
|
22
|
+
14.92 5.195 0.775 0.000 4.420 1 DHeap::Benchmarks::Scenarios#repeated_push_pop /home/nick/src/d_heap/lib/d_heap/benchmarks.rb:81
|
23
|
+
2.46 0.128 0.128 0.000 0.000 1000000 Array#fetch
|
24
|
+
2.41 0.125 0.125 0.000 0.000 1000000 Array#pop
|
25
|
+
2.35 0.122 0.122 0.000 0.000 1000000 Array#first
|
26
|
+
|
27
|
+
* recursively called methods
|
28
|
+
|
29
|
+
Columns are:
|
30
|
+
|
31
|
+
%self - The percentage of time spent in this method, derived from self_time/total_time.
|
32
|
+
total - The time spent in this method and its children.
|
33
|
+
self - The time spent in this method.
|
34
|
+
wait - The amount of time this method waited for other threads.
|
35
|
+
child - The time spent in this method's children.
|
36
|
+
calls - The number of times this method was called.
|
37
|
+
name - The name of the method.
|
38
|
+
location - The location of the method.
|
39
|
+
|
40
|
+
The interpretation of method names is:
|
41
|
+
|
42
|
+
* MyObject#test - An instance method "test" of the class "MyObject"
|
43
|
+
* <Object:MyObject>#test - The <> characters indicate a method on a singleton class.
|
44
|
+
|
45
|
+
Filling quaternary DHeap ---------------------------
|
46
|
+
Profiling quaternary DHeap ---------------------------
|
47
|
+
Measure Mode: wall_time
|
48
|
+
Thread ID: 1400
|
49
|
+
Fiber ID: 1380
|
50
|
+
Total: 1.103473
|
51
|
+
Sort by: self_time
|
52
|
+
|
53
|
+
%self total self wait child calls name location
|
54
|
+
64.92 1.103 0.716 0.000 0.387 1 DHeap::Benchmarks::Scenarios#repeated_push_pop /home/nick/src/d_heap/lib/d_heap/benchmarks.rb:81
|
55
|
+
12.09 0.133 0.133 0.000 0.000 1000000 DHeap#<<
|
56
|
+
11.82 0.130 0.130 0.000 0.000 1000000 DHeap#pop
|
57
|
+
11.16 0.123 0.123 0.000 0.000 1000000 Array#fetch
|
58
|
+
|
59
|
+
* recursively called methods
|
60
|
+
|
61
|
+
Columns are:
|
62
|
+
|
63
|
+
%self - The percentage of time spent in this method, derived from self_time/total_time.
|
64
|
+
total - The time spent in this method and its children.
|
65
|
+
self - The time spent in this method.
|
66
|
+
wait - The amount of time this method waited for other threads.
|
67
|
+
child - The time spent in this method's children.
|
68
|
+
calls - The number of times this method was called.
|
69
|
+
name - The name of the method.
|
70
|
+
location - The location of the method.
|
71
|
+
|
72
|
+
The interpretation of method names is:
|
73
|
+
|
74
|
+
* MyObject#test - An instance method "test" of the class "MyObject"
|
75
|
+
* <Object:MyObject>#test - The <> characters indicate a method on a singleton class.
|
76
|
+
|
77
|
+
########################################################################
|
78
|
+
# Profile w/ N=100 (i=1000000)
|
79
|
+
# (n.b. RubyProf & tracepoint can change relative performance.
|
80
|
+
# A sampling profiler can provide more accurate relative metrics.
|
81
|
+
########################################################################
|
82
|
+
|
83
|
+
Filling ruby binary heap ---------------------------
|
84
|
+
Profiling ruby binary heap ---------------------------
|
85
|
+
Measure Mode: wall_time
|
86
|
+
Thread ID: 1400
|
87
|
+
Fiber ID: 1380
|
88
|
+
Total: 7.422039
|
89
|
+
Sort by: self_time
|
90
|
+
|
91
|
+
%self total self wait child calls name location
|
92
|
+
47.72 3.542 3.542 0.000 0.000 1000000 DHeap::Benchmarks::RbHeap#<< /home/nick/src/d_heap/lib/d_heap/benchmarks/implementations.rb:107
|
93
|
+
36.82 2.984 2.733 0.000 0.251 1000000 DHeap::Benchmarks::RbHeap#pop /home/nick/src/d_heap/lib/d_heap/benchmarks/implementations.rb:121
|
94
|
+
10.39 7.422 0.771 0.000 6.651 1 DHeap::Benchmarks::Scenarios#repeated_push_pop /home/nick/src/d_heap/lib/d_heap/benchmarks.rb:81
|
95
|
+
1.71 0.127 0.127 0.000 0.000 1000000 Array#pop
|
96
|
+
1.69 0.125 0.125 0.000 0.000 1000000 Array#fetch
|
97
|
+
1.67 0.124 0.124 0.000 0.000 1000000 Array#first
|
98
|
+
|
99
|
+
* recursively called methods
|
100
|
+
|
101
|
+
Columns are:
|
102
|
+
|
103
|
+
%self - The percentage of time spent in this method, derived from self_time/total_time.
|
104
|
+
total - The time spent in this method and its children.
|
105
|
+
self - The time spent in this method.
|
106
|
+
wait - The amount of time this method waited for other threads.
|
107
|
+
child - The time spent in this method's children.
|
108
|
+
calls - The number of times this method was called.
|
109
|
+
name - The name of the method.
|
110
|
+
location - The location of the method.
|
111
|
+
|
112
|
+
The interpretation of method names is:
|
113
|
+
|
114
|
+
* MyObject#test - An instance method "test" of the class "MyObject"
|
115
|
+
* <Object:MyObject>#test - The <> characters indicate a method on a singleton class.
|
116
|
+
|
117
|
+
Filling quaternary DHeap ---------------------------
|
118
|
+
Profiling quaternary DHeap ---------------------------
|
119
|
+
Measure Mode: wall_time
|
120
|
+
Thread ID: 1400
|
121
|
+
Fiber ID: 1380
|
122
|
+
Total: 1.163539
|
123
|
+
Sort by: self_time
|
124
|
+
|
125
|
+
%self total self wait child calls name location
|
126
|
+
64.38 1.164 0.749 0.000 0.414 1 DHeap::Benchmarks::Scenarios#repeated_push_pop /home/nick/src/d_heap/lib/d_heap/benchmarks.rb:81
|
127
|
+
12.52 0.146 0.146 0.000 0.000 1000000 DHeap#<<
|
128
|
+
12.43 0.145 0.145 0.000 0.000 1000000 DHeap#pop
|
129
|
+
10.67 0.124 0.124 0.000 0.000 1000000 Array#fetch
|
130
|
+
|
131
|
+
* recursively called methods
|
132
|
+
|
133
|
+
Columns are:
|
134
|
+
|
135
|
+
%self - The percentage of time spent in this method, derived from self_time/total_time.
|
136
|
+
total - The time spent in this method and its children.
|
137
|
+
self - The time spent in this method.
|
138
|
+
wait - The amount of time this method waited for other threads.
|
139
|
+
child - The time spent in this method's children.
|
140
|
+
calls - The number of times this method was called.
|
141
|
+
name - The name of the method.
|
142
|
+
location - The location of the method.
|
143
|
+
|
144
|
+
The interpretation of method names is:
|
145
|
+
|
146
|
+
* MyObject#test - An instance method "test" of the class "MyObject"
|
147
|
+
* <Object:MyObject>#test - The <> characters indicate a method on a singleton class.
|
148
|
+
|
149
|
+
########################################################################
|
150
|
+
# Profile w/ N=1000 (i=1000000)
|
151
|
+
# (n.b. RubyProf & tracepoint can change relative performance.
|
152
|
+
# A sampling profiler can provide more accurate relative metrics.
|
153
|
+
########################################################################
|
154
|
+
|
155
|
+
Filling ruby binary heap ---------------------------
|
156
|
+
Profiling ruby binary heap ---------------------------
|
157
|
+
Measure Mode: wall_time
|
158
|
+
Thread ID: 1400
|
159
|
+
Fiber ID: 1380
|
160
|
+
Total: 9.693127
|
161
|
+
Sort by: self_time
|
162
|
+
|
163
|
+
%self total self wait child calls name location
|
164
|
+
52.28 5.068 5.068 0.000 0.000 1000000 DHeap::Benchmarks::RbHeap#<< /home/nick/src/d_heap/lib/d_heap/benchmarks/implementations.rb:107
|
165
|
+
35.52 3.702 3.443 0.000 0.259 1000000 DHeap::Benchmarks::RbHeap#pop /home/nick/src/d_heap/lib/d_heap/benchmarks/implementations.rb:121
|
166
|
+
8.18 9.693 0.793 0.000 8.900 1 DHeap::Benchmarks::Scenarios#repeated_push_pop /home/nick/src/d_heap/lib/d_heap/benchmarks.rb:81
|
167
|
+
1.36 0.131 0.131 0.000 0.000 1000000 Array#pop
|
168
|
+
1.35 0.131 0.131 0.000 0.000 1000000 Array#fetch
|
169
|
+
1.32 0.128 0.128 0.000 0.000 1000000 Array#first
|
170
|
+
|
171
|
+
* recursively called methods
|
172
|
+
|
173
|
+
Columns are:
|
174
|
+
|
175
|
+
%self - The percentage of time spent in this method, derived from self_time/total_time.
|
176
|
+
total - The time spent in this method and its children.
|
177
|
+
self - The time spent in this method.
|
178
|
+
wait - The amount of time this method waited for other threads.
|
179
|
+
child - The time spent in this method's children.
|
180
|
+
calls - The number of times this method was called.
|
181
|
+
name - The name of the method.
|
182
|
+
location - The location of the method.
|
183
|
+
|
184
|
+
The interpretation of method names is:
|
185
|
+
|
186
|
+
* MyObject#test - An instance method "test" of the class "MyObject"
|
187
|
+
* <Object:MyObject>#test - The <> characters indicate a method on a singleton class.
|
188
|
+
|
189
|
+
Filling quaternary DHeap ---------------------------
|
190
|
+
Profiling quaternary DHeap ---------------------------
|
191
|
+
Measure Mode: wall_time
|
192
|
+
Thread ID: 1400
|
193
|
+
Fiber ID: 1380
|
194
|
+
Total: 1.125575
|
195
|
+
Sort by: self_time
|
196
|
+
|
197
|
+
%self total self wait child calls name location
|
198
|
+
64.22 1.126 0.723 0.000 0.403 1 DHeap::Benchmarks::Scenarios#repeated_push_pop /home/nick/src/d_heap/lib/d_heap/benchmarks.rb:81
|
199
|
+
13.16 0.148 0.148 0.000 0.000 1000000 DHeap#<<
|
200
|
+
12.01 0.135 0.135 0.000 0.000 1000000 DHeap#pop
|
201
|
+
10.62 0.119 0.119 0.000 0.000 1000000 Array#fetch
|
202
|
+
|
203
|
+
* recursively called methods
|
204
|
+
|
205
|
+
Columns are:
|
206
|
+
|
207
|
+
%self - The percentage of time spent in this method, derived from self_time/total_time.
|
208
|
+
total - The time spent in this method and its children.
|
209
|
+
self - The time spent in this method.
|
210
|
+
wait - The amount of time this method waited for other threads.
|
211
|
+
child - The time spent in this method's children.
|
212
|
+
calls - The number of times this method was called.
|
213
|
+
name - The name of the method.
|
214
|
+
location - The location of the method.
|
215
|
+
|
216
|
+
The interpretation of method names is:
|
217
|
+
|
218
|
+
* MyObject#test - An instance method "test" of the class "MyObject"
|
219
|
+
* <Object:MyObject>#test - The <> characters indicate a method on a singleton class.
|
220
|
+
|
221
|
+
########################################################################
|
222
|
+
# Profile w/ N=10000 (i=1000000)
|
223
|
+
# (n.b. RubyProf & tracepoint can change relative performance.
|
224
|
+
# A sampling profiler can provide more accurate relative metrics.
|
225
|
+
########################################################################
|
226
|
+
|
227
|
+
Filling ruby binary heap ---------------------------
|
228
|
+
Profiling ruby binary heap ---------------------------
|
229
|
+
Measure Mode: wall_time
|
230
|
+
Thread ID: 1400
|
231
|
+
Fiber ID: 1380
|
232
|
+
Total: 13.737007
|
233
|
+
Sort by: self_time
|
234
|
+
|
235
|
+
%self total self wait child calls name location
|
236
|
+
49.46 6.794 6.794 0.000 0.000 1000000 DHeap::Benchmarks::RbHeap#<< /home/nick/src/d_heap/lib/d_heap/benchmarks/implementations.rb:107
|
237
|
+
41.97 6.017 5.765 0.000 0.251 1000000 DHeap::Benchmarks::RbHeap#pop /home/nick/src/d_heap/lib/d_heap/benchmarks/implementations.rb:121
|
238
|
+
5.80 13.737 0.796 0.000 12.941 1 DHeap::Benchmarks::Scenarios#repeated_push_pop /home/nick/src/d_heap/lib/d_heap/benchmarks.rb:81
|
239
|
+
|
240
|
+
* recursively called methods
|
241
|
+
|
242
|
+
Columns are:
|
243
|
+
|
244
|
+
%self - The percentage of time spent in this method, derived from self_time/total_time.
|
245
|
+
total - The time spent in this method and its children.
|
246
|
+
self - The time spent in this method.
|
247
|
+
wait - The amount of time this method waited for other threads.
|
248
|
+
child - The time spent in this method's children.
|
249
|
+
calls - The number of times this method was called.
|
250
|
+
name - The name of the method.
|
251
|
+
location - The location of the method.
|
252
|
+
|
253
|
+
The interpretation of method names is:
|
254
|
+
|
255
|
+
* MyObject#test - An instance method "test" of the class "MyObject"
|
256
|
+
* <Object:MyObject>#test - The <> characters indicate a method on a singleton class.
|
257
|
+
|
258
|
+
Filling quaternary DHeap ---------------------------
|
259
|
+
Profiling quaternary DHeap ---------------------------
|
260
|
+
Measure Mode: wall_time
|
261
|
+
Thread ID: 1400
|
262
|
+
Fiber ID: 1380
|
263
|
+
Total: 1.179968
|
264
|
+
Sort by: self_time
|
265
|
+
|
266
|
+
%self total self wait child calls name location
|
267
|
+
62.48 1.180 0.737 0.000 0.443 1 DHeap::Benchmarks::Scenarios#repeated_push_pop /home/nick/src/d_heap/lib/d_heap/benchmarks.rb:81
|
268
|
+
13.86 0.164 0.164 0.000 0.000 1000000 DHeap#<<
|
269
|
+
13.46 0.159 0.159 0.000 0.000 1000000 DHeap#pop
|
270
|
+
10.20 0.120 0.120 0.000 0.000 1000000 Array#fetch
|
271
|
+
|
272
|
+
* recursively called methods
|
273
|
+
|
274
|
+
Columns are:
|
275
|
+
|
276
|
+
%self - The percentage of time spent in this method, derived from self_time/total_time.
|
277
|
+
total - The time spent in this method and its children.
|
278
|
+
self - The time spent in this method.
|
279
|
+
wait - The amount of time this method waited for other threads.
|
280
|
+
child - The time spent in this method's children.
|
281
|
+
calls - The number of times this method was called.
|
282
|
+
name - The name of the method.
|
283
|
+
location - The location of the method.
|
284
|
+
|
285
|
+
The interpretation of method names is:
|
286
|
+
|
287
|
+
* MyObject#test - An instance method "test" of the class "MyObject"
|
288
|
+
* <Object:MyObject>#test - The <> characters indicate a method on a singleton class.
|
289
|
+
|
290
|
+
########################################################################
|
291
|
+
# Profile w/ N=100000 (i=1000000)
|
292
|
+
# (n.b. RubyProf & tracepoint can change relative performance.
|
293
|
+
# A sampling profiler can provide more accurate relative metrics.
|
294
|
+
########################################################################
|
295
|
+
|
296
|
+
Filling ruby binary heap ---------------------------
|
297
|
+
Profiling ruby binary heap ---------------------------
|
298
|
+
Measure Mode: wall_time
|
299
|
+
Thread ID: 1400
|
300
|
+
Fiber ID: 1380
|
301
|
+
Total: 16.425915
|
302
|
+
Sort by: self_time
|
303
|
+
|
304
|
+
%self total self wait child calls name location
|
305
|
+
50.99 8.623 8.376 0.000 0.247 1000000 DHeap::Benchmarks::RbHeap#pop /home/nick/src/d_heap/lib/d_heap/benchmarks/implementations.rb:121
|
306
|
+
41.82 6.869 6.869 0.000 0.000 1000000 DHeap::Benchmarks::RbHeap#<< /home/nick/src/d_heap/lib/d_heap/benchmarks/implementations.rb:107
|
307
|
+
4.89 16.426 0.803 0.000 15.623 1 DHeap::Benchmarks::Scenarios#repeated_push_pop /home/nick/src/d_heap/lib/d_heap/benchmarks.rb:81
|
308
|
+
|
309
|
+
* recursively called methods
|
310
|
+
|
311
|
+
Columns are:
|
312
|
+
|
313
|
+
%self - The percentage of time spent in this method, derived from self_time/total_time.
|
314
|
+
total - The time spent in this method and its children.
|
315
|
+
self - The time spent in this method.
|
316
|
+
wait - The amount of time this method waited for other threads.
|
317
|
+
child - The time spent in this method's children.
|
318
|
+
calls - The number of times this method was called.
|
319
|
+
name - The name of the method.
|
320
|
+
location - The location of the method.
|
321
|
+
|
322
|
+
The interpretation of method names is:
|
323
|
+
|
324
|
+
* MyObject#test - An instance method "test" of the class "MyObject"
|
325
|
+
* <Object:MyObject>#test - The <> characters indicate a method on a singleton class.
|
326
|
+
|
327
|
+
Filling quaternary DHeap ---------------------------
|
328
|
+
Profiling quaternary DHeap ---------------------------
|
329
|
+
Measure Mode: wall_time
|
330
|
+
Thread ID: 1400
|
331
|
+
Fiber ID: 1380
|
332
|
+
Total: 1.205573
|
333
|
+
Sort by: self_time
|
334
|
+
|
335
|
+
%self total self wait child calls name location
|
336
|
+
59.14 1.206 0.713 0.000 0.493 1 DHeap::Benchmarks::Scenarios#repeated_push_pop /home/nick/src/d_heap/lib/d_heap/benchmarks.rb:81
|
337
|
+
16.93 0.204 0.204 0.000 0.000 1000000 DHeap#pop
|
338
|
+
13.88 0.167 0.167 0.000 0.000 1000000 DHeap#<<
|
339
|
+
10.05 0.121 0.121 0.000 0.000 1000000 Array#fetch
|
340
|
+
|
341
|
+
* recursively called methods
|
342
|
+
|
343
|
+
Columns are:
|
344
|
+
|
345
|
+
%self - The percentage of time spent in this method, derived from self_time/total_time.
|
346
|
+
total - The time spent in this method and its children.
|
347
|
+
self - The time spent in this method.
|
348
|
+
wait - The amount of time this method waited for other threads.
|
349
|
+
child - The time spent in this method's children.
|
350
|
+
calls - The number of times this method was called.
|
351
|
+
name - The name of the method.
|
352
|
+
location - The location of the method.
|
353
|
+
|
354
|
+
The interpretation of method names is:
|
355
|
+
|
356
|
+
* MyObject#test - An instance method "test" of the class "MyObject"
|
357
|
+
* <Object:MyObject>#test - The <> characters indicate a method on a singleton class.
|
358
|
+
|
data/ext/d_heap/d_heap.c
CHANGED
@@ -1,99 +1,286 @@
|
|
1
|
-
#include "
|
2
|
-
|
3
|
-
|
4
|
-
ID id_ivar_values;
|
5
|
-
ID id_ivar_scores;
|
6
|
-
ID id_ivar_d;
|
7
|
-
|
8
|
-
#define Get_DHeap(hobj, hstruct) ((hstruct) = get_dheap_struct(hobj))
|
9
|
-
|
10
|
-
#define DHEAP_IDX_LAST(heap) (DHEAP_SIZE(heap) - 1)
|
11
|
-
#define DHEAP_IDX_PARENT(heap, idx) ((idx - 1) / heap->d)
|
12
|
-
#define DHEAP_IDX_CHILD0(heap, idx) ((idx * heap->d) + 1)
|
13
|
-
|
14
|
-
#define DHEAP_SIZE(heap) (RARRAY_LEN((heap)->scores))
|
15
|
-
#define DHEAP_VALUE(heap, idx) RARRAY_AREF((heap)->values, idx)
|
16
|
-
#define DHEAP_SCORE(heap, idx) RARRAY_AREF((heap)->scores, idx)
|
17
|
-
#define DHEAP_ASSIGN(heap, idx, scr, val) \
|
18
|
-
rb_ary_store(heap->scores, idx, scr); \
|
19
|
-
rb_ary_store(heap->values, idx, val);
|
20
|
-
#define DHEAP_APPEND(heap, scr, val) \
|
21
|
-
rb_ary_push((heap)->scores, scr); \
|
22
|
-
rb_ary_push((heap)->values, val);
|
23
|
-
#define DHEAP_DROP_LAST(heap) ( \
|
24
|
-
rb_ary_pop(heap->scores), \
|
25
|
-
rb_ary_pop(heap->values) \
|
26
|
-
) // score, value
|
27
|
-
|
28
|
-
#define DHEAP_Check_d_size(d) \
|
29
|
-
if (d < 2) { \
|
30
|
-
rb_raise(rb_eArgError, "DHeap d=%d is too small", d); \
|
31
|
-
} \
|
32
|
-
if (d > DHEAP_MAX_D) { \
|
33
|
-
rb_raise(rb_eArgError, "DHeap d=%d is too large", d); \
|
34
|
-
}
|
1
|
+
#include "ruby.h"
|
2
|
+
#include <float.h>
|
3
|
+
#include <math.h>
|
35
4
|
|
36
|
-
#
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
5
|
+
#if CHAR_BIT != 8
|
6
|
+
# error "DHeap assumes 8-bit bytes"
|
7
|
+
#endif
|
8
|
+
|
9
|
+
#if SIZE_MAX != ULONG_MAX
|
10
|
+
# error "DHeap assumes 'size_t' is 'unsigned long'"
|
11
|
+
#endif
|
12
|
+
|
13
|
+
#if LDBL_MANT_DIG < SIZEOF_UNSIGNED_LONG_LONG * 8
|
14
|
+
# error "DHeap assumes 'long double' mantissa can store 'unsigned long long'"
|
15
|
+
#endif
|
16
|
+
|
17
|
+
// TODO: test this code on a 32 bit system (it MUST have some bugs!)
|
18
|
+
// TODO: perhaps just convert from "unsigned long long" to "uint64_t"!
|
19
|
+
#if SIZEOF_UNSIGNED_LONG_LONG * 8 != 64
|
20
|
+
# error "DHeap assumes 64-bit 'unsigned long long'"
|
21
|
+
#endif
|
22
|
+
|
23
|
+
/********************************************************************
|
24
|
+
*
|
25
|
+
* Type definitions
|
26
|
+
*
|
27
|
+
********************************************************************/
|
43
28
|
|
44
|
-
struct dheap_struct {
|
45
|
-
int d;
|
46
|
-
VALUE scores;
|
47
|
-
VALUE values;
|
48
|
-
};
|
49
29
|
typedef struct dheap_struct dheap_t;
|
30
|
+
typedef struct dheap_entry ENTRY;
|
31
|
+
|
32
|
+
typedef double SCORE;
|
33
|
+
|
34
|
+
/********************************************************************
|
35
|
+
*
|
36
|
+
* Struct definitions
|
37
|
+
*
|
38
|
+
********************************************************************/
|
39
|
+
|
40
|
+
struct dheap_struct
|
41
|
+
{
|
42
|
+
int d;
|
43
|
+
size_t size;
|
44
|
+
size_t capa;
|
45
|
+
ENTRY *entries;
|
46
|
+
#ifdef DHEAP_MAP
|
47
|
+
VALUE indexes; // Hash
|
48
|
+
#endif
|
49
|
+
};
|
50
|
+
|
51
|
+
struct dheap_entry
|
52
|
+
{
|
53
|
+
SCORE score;
|
54
|
+
VALUE value;
|
55
|
+
};
|
56
|
+
|
57
|
+
#define DHEAPMAP_P(heap) UNLIKELY(RTEST((heap)->indexes))
|
58
|
+
|
59
|
+
/********************************************************************
|
60
|
+
*
|
61
|
+
* Constant definitions
|
62
|
+
*
|
63
|
+
********************************************************************/
|
64
|
+
|
65
|
+
#define DHEAP_DEFAULT_D 6
|
66
|
+
#define DHEAP_MAX_D INT_MAX
|
67
|
+
|
68
|
+
// sizeof(ENTRY) => 16 bytes, 128-bits
|
69
|
+
// one kilobyte = 32 * 32 bytes
|
70
|
+
#define DHEAP_DEFAULT_CAPA 32
|
71
|
+
#define DHEAP_MAX_CAPA (SIZE_MAX / (int)sizeof(ENTRY))
|
72
|
+
#define DHEAP_CAPA_INCR_MAX (10 * 1024 * 1024 / (int)sizeof(ENTRY))
|
73
|
+
|
74
|
+
static ID id_cmp; // <=>
|
75
|
+
static ID id_abs; // abs
|
76
|
+
static ID id_lshift; // <<
|
77
|
+
static ID id_uminus; // -@
|
78
|
+
|
79
|
+
static const rb_data_type_t dheap_data_type;
|
80
|
+
|
81
|
+
/********************************************************************
|
82
|
+
*
|
83
|
+
* Metaprogramming macros
|
84
|
+
*
|
85
|
+
********************************************************************/
|
86
|
+
|
87
|
+
#define LIKELY RB_LIKELY
|
88
|
+
#define UNLIKELY RB_UNLIKELY
|
89
|
+
|
90
|
+
#ifdef DHEAP_MAP
|
91
|
+
# define DHEAP_DISPATCH_EXPR(func, heap, ...) \
|
92
|
+
(DHEAPMAP_P(heap) ? dheapmap_##func(heap, __VA_ARGS__) \
|
93
|
+
: dheap_##func(heap, __VA_ARGS__))
|
94
|
+
#else
|
95
|
+
# define DHEAP_DISPATCH_EXPR(func, heap, ...) \
|
96
|
+
dheap_##func(heap, __VA_ARGS__);
|
97
|
+
#endif
|
98
|
+
|
99
|
+
#ifdef DHEAP_MAP
|
100
|
+
# define DHEAP_DISPATCH_STMT(heap, macro, ...) \
|
101
|
+
do { \
|
102
|
+
if (DHEAPMAP_P(heap)) { \
|
103
|
+
macro(dheapmap, heap, __VA_ARGS__); \
|
104
|
+
} else { \
|
105
|
+
macro(dheap, heap, __VA_ARGS__); \
|
106
|
+
} \
|
107
|
+
} while (0)
|
108
|
+
#else
|
109
|
+
# define DHEAP_DISPATCH_STMT(heap, macro, ...) \
|
110
|
+
macro(dheap, heap, __VA_ARGS__)
|
111
|
+
#endif
|
112
|
+
|
113
|
+
/********************************************************************
|
114
|
+
*
|
115
|
+
* SCORE: casting to and from VALUE
|
116
|
+
* adapted from similar methods in ruby's object.c
|
117
|
+
*
|
118
|
+
********************************************************************/
|
119
|
+
|
120
|
+
#define SCORE2NUM(score) rb_float_new(score)
|
121
|
+
#define VAL2SCORE(val) NUM2DBL(RB_FLOAT_TYPE_P(val) ? val : rb_Float(val))
|
122
|
+
#define CMP_LT(a, b) ((a) < (b))
|
123
|
+
#define CMP_LTE(a, b) ((a) <= (b))
|
124
|
+
|
125
|
+
/********************************************************************
|
126
|
+
*
|
127
|
+
* DHeap ENTRY accessors
|
128
|
+
*
|
129
|
+
********************************************************************/
|
50
130
|
|
131
|
+
#define DHEAP_SCORE(heap, idx) (DHEAP_GET(heap, idx).score)
|
132
|
+
#define DHEAP_VALUE(heap, idx) (DHEAP_GET(heap, idx).value)
|
133
|
+
|
134
|
+
#define DHEAP_ENTRY_ARY(heap, idx) \
|
135
|
+
(((heap)->size <= (idx)) \
|
136
|
+
? Qnil \
|
137
|
+
: rb_ary_new_from_args( \
|
138
|
+
2, DHEAP_VALUE(heap, idx), SCORE2NUM(DHEAP_SCORE(heap, idx))))
|
139
|
+
|
140
|
+
#define DHEAP_GET(heap, idx) ((heap)->entries[idx])
|
141
|
+
#define DHEAP_SET(T, heap, index, entry) \
|
142
|
+
do { \
|
143
|
+
DHEAP_GET(heap, index) = (entry); \
|
144
|
+
DHEAP_SET_##T(heap, index, entry); \
|
145
|
+
} while (0)
|
146
|
+
|
147
|
+
#define DHEAP_SET_dheap(heap, index, entry) /* noop */
|
148
|
+
|
149
|
+
#ifdef DHEAP_MAP
|
150
|
+
# define DHEAP_SET_dheapmap(heap, index, entry) \
|
151
|
+
rb_hash_aset((heap)->indexes, (entry).value, ULONG2NUM(index))
|
152
|
+
#endif
|
153
|
+
|
154
|
+
/********************************************************************
|
155
|
+
*
|
156
|
+
* DHeap index math
|
157
|
+
*
|
158
|
+
********************************************************************/
|
159
|
+
|
160
|
+
#define DHEAP_IDX_LAST(heap) ((heap)->size - 1)
|
161
|
+
#define DHEAP_IDX_PARENT(heap, idx) (((idx)-1) / (heap)->d)
|
162
|
+
#define DHEAP_IDX_CHILD_0(heap, idx) (((idx) * (heap)->d) + 1)
|
163
|
+
#define DHEAP_IDX_CHILD_D(heap, idx) (((idx) * (heap)->d) + (heap)->d)
|
164
|
+
|
165
|
+
#ifdef DEBUG
|
166
|
+
# define ASSERT_DHEAP_IDX_OK(heap, index) \
|
167
|
+
do { \
|
168
|
+
if (DHEAP_IDX_LAST(heap) < index) { \
|
169
|
+
rb_raise(rb_eIndexError, "DHeap index %ld too large", index); \
|
170
|
+
} \
|
171
|
+
} while (0)
|
172
|
+
#else
|
173
|
+
# define ASSERT_DHEAP_IDX_OK(heap, index)
|
174
|
+
#endif
|
175
|
+
|
176
|
+
/********************************************************************
|
177
|
+
*
|
178
|
+
* rb_data_type_t definitions
|
179
|
+
*
|
180
|
+
********************************************************************/
|
181
|
+
|
182
|
+
#ifdef HAVE_RB_GC_MARK_MOVABLE
|
51
183
|
static void
|
52
184
|
dheap_compact(void *ptr)
|
53
185
|
{
|
54
186
|
dheap_t *heap = ptr;
|
55
|
-
|
56
|
-
|
187
|
+
for (size_t i = 0; i < heap->size; ++i) {
|
188
|
+
if (DHEAP_VALUE(heap, i))
|
189
|
+
DHEAP_VALUE(heap, i) = rb_gc_location(DHEAP_VALUE(heap, i));
|
190
|
+
}
|
191
|
+
# ifdef DHEAP_MAP
|
192
|
+
if (DHEAPMAP_P(heap)) heap->indexes = rb_gc_location(heap->indexes);
|
193
|
+
# endif
|
57
194
|
}
|
195
|
+
#else
|
196
|
+
# define rb_gc_mark_movable(x) rb_gc_mark(x)
|
197
|
+
#endif
|
58
198
|
|
59
199
|
static void
|
60
200
|
dheap_mark(void *ptr)
|
61
201
|
{
|
62
202
|
dheap_t *heap = ptr;
|
63
|
-
|
64
|
-
|
203
|
+
for (size_t i = 0; i < heap->size; ++i) {
|
204
|
+
if (DHEAP_VALUE(heap, i)) rb_gc_mark_movable(DHEAP_VALUE(heap, i));
|
205
|
+
}
|
206
|
+
#ifdef DHEAP_MAP
|
207
|
+
if (DHEAPMAP_P(heap)) rb_gc_mark_movable(heap->indexes);
|
208
|
+
#endif
|
209
|
+
}
|
210
|
+
|
211
|
+
static void
|
212
|
+
dheap_free(void *ptr)
|
213
|
+
{
|
214
|
+
dheap_t *heap = ptr;
|
215
|
+
heap->size = 0;
|
216
|
+
if (heap->entries) {
|
217
|
+
xfree(heap->entries);
|
218
|
+
heap->entries = NULL;
|
219
|
+
}
|
220
|
+
heap->capa = 0;
|
221
|
+
xfree(ptr);
|
222
|
+
#ifdef DHEAP_MAP
|
223
|
+
heap->indexes = Qnil;
|
224
|
+
#endif
|
65
225
|
}
|
66
226
|
|
67
227
|
static size_t
|
68
228
|
dheap_memsize(const void *ptr)
|
69
229
|
{
|
70
230
|
const dheap_t *heap = ptr;
|
71
|
-
size_t
|
231
|
+
size_t size = 0;
|
232
|
+
size += sizeof(*heap);
|
233
|
+
size += sizeof(ENTRY) * heap->capa;
|
72
234
|
return size;
|
73
235
|
}
|
74
236
|
|
75
237
|
static const rb_data_type_t dheap_data_type = {
|
76
238
|
"DHeap",
|
77
|
-
{
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
239
|
+
{ (void (*)(void *))dheap_mark,
|
240
|
+
(void (*)(void *))dheap_free,
|
241
|
+
(size_t(*)(const void *))dheap_memsize,
|
242
|
+
#ifdef HAVE_RB_GC_MARK_MOVABLE
|
243
|
+
(void (*)(void *))dheap_compact,
|
244
|
+
{ 0 }
|
245
|
+
#else
|
246
|
+
{ 0 }
|
247
|
+
#endif
|
82
248
|
},
|
83
|
-
0,
|
249
|
+
0,
|
250
|
+
0,
|
84
251
|
RUBY_TYPED_FREE_IMMEDIATELY,
|
85
252
|
};
|
86
253
|
|
254
|
+
/********************************************************************
|
255
|
+
*
|
256
|
+
* DHeap comparisons
|
257
|
+
*
|
258
|
+
********************************************************************/
|
259
|
+
|
260
|
+
/********************************************************************
|
261
|
+
*
|
262
|
+
* DHeap allocation and initialization and resizing
|
263
|
+
*
|
264
|
+
********************************************************************/
|
265
|
+
|
87
266
|
static VALUE
|
88
267
|
dheap_s_alloc(VALUE klass)
|
89
268
|
{
|
90
|
-
VALUE
|
269
|
+
VALUE obj;
|
91
270
|
dheap_t *heap;
|
92
271
|
|
272
|
+
#pragma GCC diagnostic push
|
273
|
+
#pragma GCC diagnostic ignored "-Wpedantic"
|
274
|
+
// TypedData_Make_Struct uses a non-std "statement expression"
|
93
275
|
obj = TypedData_Make_Struct(klass, dheap_t, &dheap_data_type, heap);
|
94
|
-
|
95
|
-
heap->
|
96
|
-
heap->
|
276
|
+
#pragma GCC diagnostic pop
|
277
|
+
heap->d = DHEAP_DEFAULT_D;
|
278
|
+
heap->size = 0;
|
279
|
+
heap->capa = 0;
|
280
|
+
heap->entries = NULL;
|
281
|
+
#ifdef DHEAP_MAP
|
282
|
+
heap->indexes = Qnil;
|
283
|
+
#endif
|
97
284
|
|
98
285
|
return obj;
|
99
286
|
}
|
@@ -103,168 +290,183 @@ get_dheap_struct(VALUE self)
|
|
103
290
|
{
|
104
291
|
dheap_t *heap;
|
105
292
|
TypedData_Get_Struct(self, dheap_t, &dheap_data_type, heap);
|
106
|
-
Check_Type(heap->scores, T_ARRAY);
|
107
293
|
return heap;
|
108
294
|
}
|
109
295
|
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
|
116
|
-
static VALUE
|
117
|
-
dheap_initialize(int argc, VALUE *argv, VALUE self) {
|
118
|
-
rb_check_arity(argc, 0, 1);
|
119
|
-
dheap_t *heap;
|
120
|
-
TypedData_Get_Struct(self, dheap_t, &dheap_data_type, heap);
|
296
|
+
static inline dheap_t *
|
297
|
+
get_dheap_struct_unfrozen(VALUE self)
|
298
|
+
{
|
299
|
+
rb_check_frozen(self);
|
300
|
+
return get_dheap_struct(self);
|
301
|
+
}
|
121
302
|
|
122
|
-
|
123
|
-
|
124
|
-
|
303
|
+
void
|
304
|
+
dheap_set_capa(dheap_t *heap, size_t new_capa)
|
305
|
+
{
|
306
|
+
// Do nothing if we already have the capacity or are resizing too small
|
307
|
+
if (new_capa <= heap->capa || new_capa <= heap->size) return;
|
308
|
+
|
309
|
+
// allocate
|
310
|
+
if (heap->entries) {
|
311
|
+
RB_REALLOC_N(heap->entries, ENTRY, new_capa);
|
312
|
+
} else {
|
313
|
+
heap->entries = RB_ZALLOC_N(ENTRY, new_capa);
|
125
314
|
}
|
126
|
-
|
127
|
-
|
128
|
-
|
129
|
-
heap->scores = rb_ary_new_capa(10000);
|
130
|
-
heap->values = rb_ary_new_capa(10000);
|
315
|
+
heap->capa = new_capa;
|
316
|
+
}
|
131
317
|
|
132
|
-
|
318
|
+
static void
|
319
|
+
dheap_incr_capa(dheap_t *heap, size_t new_size)
|
320
|
+
{
|
321
|
+
size_t new_capa = heap->capa;
|
322
|
+
while (new_capa < new_size) {
|
323
|
+
if (new_capa <= DHEAP_CAPA_INCR_MAX) {
|
324
|
+
// double it... up to DHEAP_CAPA_INCR_MAX
|
325
|
+
new_capa *= 2;
|
326
|
+
} else if (DHEAP_MAX_CAPA - DHEAP_CAPA_INCR_MAX < heap->capa) {
|
327
|
+
// avoid overflow
|
328
|
+
new_capa = DHEAP_MAX_CAPA;
|
329
|
+
} else {
|
330
|
+
new_capa += DHEAP_CAPA_INCR_MAX;
|
331
|
+
}
|
332
|
+
}
|
333
|
+
dheap_set_capa(heap, new_capa);
|
133
334
|
}
|
134
335
|
|
135
|
-
|
136
|
-
|
137
|
-
|
138
|
-
|
139
|
-
|
140
|
-
|
141
|
-
|
142
|
-
|
143
|
-
|
144
|
-
|
145
|
-
|
146
|
-
|
147
|
-
|
336
|
+
static void
|
337
|
+
dheap_ensure_room_for_push(dheap_t *heap, size_t incr_by)
|
338
|
+
{
|
339
|
+
// check for overflow of new_size
|
340
|
+
if (DHEAP_MAX_CAPA - incr_by < heap->size) {
|
341
|
+
rb_raise(rb_eIndexError,
|
342
|
+
"size increase overflow: %zu + %zu",
|
343
|
+
heap->size,
|
344
|
+
incr_by);
|
345
|
+
} else {
|
346
|
+
size_t new_size = heap->size + incr_by;
|
347
|
+
// if existing capacity is too small
|
348
|
+
if (heap->capa < new_size) dheap_incr_capa(heap, new_size);
|
349
|
+
}
|
148
350
|
}
|
149
351
|
|
150
|
-
|
151
|
-
|
152
|
-
|
352
|
+
static inline int
|
353
|
+
dheap_value_to_int_d(VALUE num)
|
354
|
+
{
|
355
|
+
int d = NUM2INT(num);
|
356
|
+
if (d < 2) rb_raise(rb_eArgError, "DHeap d=%u is too small", d);
|
357
|
+
if (d > DHEAP_MAX_D) rb_raise(rb_eArgError, "DHeap d=%u is too large", d);
|
358
|
+
return d;
|
359
|
+
}
|
153
360
|
|
154
|
-
static inline
|
155
|
-
|
361
|
+
static inline size_t
|
362
|
+
dheap_value_to_capa(VALUE num)
|
156
363
|
{
|
157
|
-
|
158
|
-
|
159
|
-
|
364
|
+
size_t capa = NUM2ULONG(num);
|
365
|
+
if (capa < 1) {
|
366
|
+
rb_raise(rb_eArgError, "DHeap capa=%zu must be positive", capa);
|
367
|
+
}
|
368
|
+
return capa;
|
160
369
|
}
|
161
370
|
|
162
|
-
|
371
|
+
static VALUE
|
372
|
+
dheap_init(VALUE self, VALUE d, VALUE capa, VALUE map)
|
373
|
+
{
|
374
|
+
dheap_t *heap = get_dheap_struct(self);
|
163
375
|
|
164
|
-
|
165
|
-
|
166
|
-
#define CMP_GT(a, b) (optimized_cmp(a, b) > 0)
|
167
|
-
#define CMP_GTE(a, b) (optimized_cmp(a, b) >= 0)
|
376
|
+
if (heap->entries || heap->size || heap->capa)
|
377
|
+
rb_raise(rb_eScriptError, "DHeap already initialized.");
|
168
378
|
|
169
|
-
|
170
|
-
|
171
|
-
|
172
|
-
|
173
|
-
|
174
|
-
*/
|
175
|
-
static inline int
|
176
|
-
optimized_cmp(VALUE a, VALUE b) {
|
177
|
-
if (a == b) // Fixnum equality and object equality
|
178
|
-
return 0;
|
179
|
-
if (FIXNUM_P(a) && FIXNUM_P(b))
|
180
|
-
return (FIX2LONG(a) < FIX2LONG(b)) ? -1 : 1;
|
181
|
-
if (RB_FLOAT_TYPE_P(a) && RB_FLOAT_TYPE_P(b))
|
182
|
-
{
|
183
|
-
double x, y;
|
184
|
-
x = RFLOAT_VALUE(a);
|
185
|
-
y = RFLOAT_VALUE(b);
|
186
|
-
if (isnan(x) || isnan(y)) rb_cmperr(a, b); // raise ArgumentError
|
187
|
-
return (x < y) ? -1 : ((x == y) ? 0 : 1);
|
188
|
-
}
|
189
|
-
if (RB_TYPE_P(a, T_BIGNUM) && RB_TYPE_P(b, T_BIGNUM))
|
190
|
-
return FIX2INT(rb_big_cmp(a, b));
|
191
|
-
if (STRING_P(a) && STRING_P(b))
|
192
|
-
return rb_str_cmp(a, b);
|
379
|
+
heap->d = dheap_value_to_int_d(d);
|
380
|
+
dheap_set_capa(heap, dheap_value_to_capa(capa));
|
381
|
+
#ifdef DHEAP_MAP
|
382
|
+
if (RTEST(map)) heap->indexes = rb_hash_new();
|
383
|
+
#endif
|
193
384
|
|
194
|
-
|
195
|
-
return rb_cmpint(rb_funcallv(a, id_cmp, 1, &b), a, b);
|
385
|
+
return self;
|
196
386
|
}
|
197
387
|
|
198
|
-
|
199
|
-
|
200
|
-
|
201
|
-
|
202
|
-
|
203
|
-
|
204
|
-
VALUE sift_score = DHEAP_SCORE(heap, sift_index);
|
388
|
+
/* @!visibility private */
|
389
|
+
static VALUE
|
390
|
+
dheap_initialize_copy(VALUE copy, VALUE orig)
|
391
|
+
{
|
392
|
+
dheap_t *heap_copy = get_dheap_struct_unfrozen(copy);
|
393
|
+
dheap_t *heap_orig = get_dheap_struct(orig);
|
205
394
|
|
206
|
-
|
207
|
-
for (long parent_index; 0 < sift_index; sift_index = parent_index) {
|
208
|
-
debug(rb_sprintf("sift up(%"PRIsVALUE", %d, %ld)", heap->values, heap->d, sift_index));
|
209
|
-
parent_index = DHEAP_IDX_PARENT(heap, sift_index);
|
210
|
-
VALUE parent_score = DHEAP_SCORE(heap, parent_index);
|
395
|
+
heap_copy->d = heap_orig->d;
|
211
396
|
|
212
|
-
|
213
|
-
|
397
|
+
dheap_set_capa(heap_copy, heap_orig->capa);
|
398
|
+
heap_copy->size = heap_orig->size;
|
399
|
+
if (heap_copy->size)
|
400
|
+
MEMCPY(heap_copy->entries, heap_orig->entries, ENTRY, heap_orig->size);
|
401
|
+
#ifdef DHEAP_MAP
|
402
|
+
if (RTEST(heap_orig->indexes))
|
403
|
+
heap_copy->indexes = rb_hash_dup(heap_orig->indexes);
|
404
|
+
#endif
|
214
405
|
|
215
|
-
|
216
|
-
|
217
|
-
DHEAP_ASSIGN(heap, sift_index, parent_score, parent_value);
|
218
|
-
DHEAP_ASSIGN(heap, parent_index, sift_score, sift_value);
|
219
|
-
}
|
220
|
-
debug(rb_sprintf("sifted (%"PRIsVALUE", %d, %ld)", heap->values, heap->d, sift_index));
|
221
|
-
return LONG2NUM(sift_index);
|
222
|
-
}
|
223
|
-
|
224
|
-
VALUE
|
225
|
-
dheap_ary_sift_down(dheap_t *heap, long sift_index) {
|
226
|
-
long last_index = DHEAP_IDX_LAST(heap);
|
227
|
-
DHEAP_Check_Index(sift_index, last_index);
|
228
|
-
|
229
|
-
VALUE sift_value = DHEAP_VALUE(heap, sift_index);
|
230
|
-
VALUE sift_score = DHEAP_SCORE(heap, sift_index);
|
231
|
-
|
232
|
-
// iteratively sift it down to where it belongs
|
233
|
-
for (long child_index; sift_index < last_index; sift_index = child_index) {
|
234
|
-
debug(rb_sprintf("sift dn(%"PRIsVALUE", %d, %ld)", heap->values, heap->d, sift_index));
|
235
|
-
// find first child index, and break if we've reached the last layer
|
236
|
-
long child_idx0 = child_index = DHEAP_IDX_CHILD0(heap, sift_index);
|
237
|
-
if (last_index < child_idx0) break;
|
238
|
-
|
239
|
-
// find the min child (and its child_index)
|
240
|
-
// requires "d" comparisons to find min child and compare to sift_score
|
241
|
-
long last_sibidx = child_idx0 + heap->d - 1;
|
242
|
-
if (last_index < last_sibidx) last_sibidx = last_index;
|
243
|
-
VALUE child_score = DHEAP_SCORE(heap, child_idx0);
|
244
|
-
child_index = child_idx0;
|
245
|
-
for (long sibling_index = child_idx0 + 1;
|
246
|
-
sibling_index <= last_sibidx;
|
247
|
-
++sibling_index) {
|
248
|
-
VALUE sibling_score = DHEAP_SCORE(heap, sibling_index);
|
249
|
-
|
250
|
-
if (CMP_LT(sibling_score, child_score)) {
|
251
|
-
child_score = sibling_score;
|
252
|
-
child_index = sibling_index;
|
253
|
-
}
|
254
|
-
}
|
406
|
+
return copy;
|
407
|
+
}
|
255
408
|
|
256
|
-
|
257
|
-
|
409
|
+
/********************************************************************
|
410
|
+
*
|
411
|
+
* DHeap sift up/down
|
412
|
+
*
|
413
|
+
********************************************************************/
|
414
|
+
|
415
|
+
#define DHEAP_SIFT_UP(T, heap, i) \
|
416
|
+
do { \
|
417
|
+
size_t sift_idx = i; \
|
418
|
+
ENTRY entry = DHEAP_GET(heap, sift_idx); \
|
419
|
+
ASSERT_DHEAP_IDX_OK(heap, sift_idx); \
|
420
|
+
for (size_t parent_idx; 0 < sift_idx; sift_idx = parent_idx) { \
|
421
|
+
parent_idx = DHEAP_IDX_PARENT(heap, sift_idx); \
|
422
|
+
if (CMP_LTE(DHEAP_SCORE((heap), parent_idx), entry.score)) break; \
|
423
|
+
DHEAP_SET(T, heap, sift_idx, DHEAP_GET(heap, parent_idx)); \
|
424
|
+
} \
|
425
|
+
DHEAP_SET(T, heap, sift_idx, entry); \
|
426
|
+
} while (0)
|
427
|
+
|
428
|
+
static inline size_t
|
429
|
+
dheap_min_child(dheap_t *heap, size_t parent, size_t last_index)
|
430
|
+
{
|
431
|
+
size_t min_child = DHEAP_IDX_CHILD_0(heap, parent);
|
432
|
+
size_t last_sib = DHEAP_IDX_CHILD_D(heap, parent);
|
433
|
+
if (UNLIKELY(last_index < last_sib)) last_sib = last_index;
|
258
434
|
|
259
|
-
|
260
|
-
|
261
|
-
|
262
|
-
|
435
|
+
for (size_t sibidx = min_child + 1; sibidx <= last_sib; ++sibidx) {
|
436
|
+
if (CMP_LT(DHEAP_SCORE(heap, sibidx), DHEAP_SCORE(heap, min_child))) {
|
437
|
+
min_child = sibidx;
|
438
|
+
}
|
263
439
|
}
|
264
|
-
|
265
|
-
return LONG2NUM(sift_index);
|
440
|
+
return min_child;
|
266
441
|
}
|
267
442
|
|
443
|
+
#define DHEAP_CAN_SIFT_DOWN(heap, index, last_index) \
|
444
|
+
(LIKELY(1 <= last_index && index <= DHEAP_IDX_PARENT(heap, last_index)))
|
445
|
+
|
446
|
+
#define DHEAP_SIFT_DOWN(T, heap, i) \
|
447
|
+
do { \
|
448
|
+
size_t sift_idx = i; \
|
449
|
+
size_t last_idx = DHEAP_IDX_LAST(heap); \
|
450
|
+
ASSERT_DHEAP_IDX_OK(heap, sift_idx); \
|
451
|
+
if (DHEAP_CAN_SIFT_DOWN(heap, sift_idx, last_idx)) { \
|
452
|
+
ENTRY entry = heap->entries[sift_idx]; \
|
453
|
+
size_t last_parent = DHEAP_IDX_PARENT(heap, last_idx); \
|
454
|
+
while (sift_idx <= last_parent) { \
|
455
|
+
size_t min_child = dheap_min_child(heap, sift_idx, last_idx); \
|
456
|
+
if (CMP_LTE(entry.score, DHEAP_SCORE(heap, min_child))) break; \
|
457
|
+
DHEAP_SET(T, heap, sift_idx, (heap)->entries[min_child]); \
|
458
|
+
sift_idx = min_child; \
|
459
|
+
} \
|
460
|
+
DHEAP_SET(T, heap, sift_idx, entry); \
|
461
|
+
} \
|
462
|
+
} while (0)
|
463
|
+
|
464
|
+
/********************************************************************
|
465
|
+
*
|
466
|
+
* DHeap attributes
|
467
|
+
*
|
468
|
+
********************************************************************/
|
469
|
+
|
268
470
|
/*
|
269
471
|
* @return [Integer] the number of elements in the heap
|
270
472
|
*/
|
@@ -272,19 +474,19 @@ static VALUE
|
|
272
474
|
dheap_size(VALUE self)
|
273
475
|
{
|
274
476
|
dheap_t *heap = get_dheap_struct(self);
|
275
|
-
|
276
|
-
return LONG2NUM(size);
|
477
|
+
return ULONG2NUM(heap->size);
|
277
478
|
}
|
278
479
|
|
480
|
+
#define DHEAP_EMPTY_P(heap) UNLIKELY((heap)->size <= 0)
|
481
|
+
|
279
482
|
/*
|
280
|
-
* @return [Boolean]
|
483
|
+
* @return [Boolean] if the heap is empty
|
281
484
|
*/
|
282
485
|
static VALUE
|
283
486
|
dheap_empty_p(VALUE self)
|
284
487
|
{
|
285
488
|
dheap_t *heap = get_dheap_struct(self);
|
286
|
-
|
287
|
-
return size == 0 ? Qtrue : Qfalse;
|
489
|
+
return DHEAP_EMPTY_P(heap) ? Qtrue : Qfalse;
|
288
490
|
}
|
289
491
|
|
290
492
|
/*
|
@@ -297,188 +499,608 @@ dheap_attr_d(VALUE self)
|
|
297
499
|
return INT2FIX(heap->d);
|
298
500
|
}
|
299
501
|
|
502
|
+
/********************************************************************
|
503
|
+
*
|
504
|
+
* DHeap push
|
505
|
+
*
|
506
|
+
********************************************************************/
|
507
|
+
|
508
|
+
#define DHEAP_PUSH(T, heap, entry) \
|
509
|
+
do { \
|
510
|
+
dheap_ensure_room_for_push(heap, 1); \
|
511
|
+
DHEAP_SET(T, heap, (heap)->size, *(entry)); \
|
512
|
+
++heap->size; \
|
513
|
+
DHEAP_SIFT_UP(T, heap, DHEAP_IDX_LAST(heap)); \
|
514
|
+
} while (0)
|
515
|
+
|
516
|
+
static inline void
|
517
|
+
dheap_push_entry(VALUE self, ENTRY *entry)
|
518
|
+
{
|
519
|
+
dheap_t *heap = get_dheap_struct_unfrozen(self);
|
520
|
+
DHEAP_PUSH(dheap, heap, entry);
|
521
|
+
}
|
522
|
+
|
523
|
+
#ifdef DHEAP_MAP
|
524
|
+
static inline void
|
525
|
+
dheapmap_update_entry(dheap_t *heap, size_t index, ENTRY *entry)
|
526
|
+
{
|
527
|
+
SCORE prev = DHEAP_SCORE(heap, index);
|
528
|
+
DHEAP_SET(dheapmap, heap, index, *entry);
|
529
|
+
if (CMP_LT(prev, entry->score)) {
|
530
|
+
DHEAP_SIFT_DOWN(dheapmap, heap, index);
|
531
|
+
} else {
|
532
|
+
DHEAP_SIFT_UP(dheapmap, heap, index);
|
533
|
+
}
|
534
|
+
}
|
535
|
+
|
536
|
+
static inline void
|
537
|
+
dheapmap_push_entry(VALUE self, ENTRY *entry)
|
538
|
+
{
|
539
|
+
dheap_t *heap = get_dheap_struct_unfrozen(self);
|
540
|
+
VALUE idxval = rb_hash_lookup2(heap->indexes, entry->value, Qfalse);
|
541
|
+
if (idxval) {
|
542
|
+
size_t index = NUM2ULONG(idxval);
|
543
|
+
dheapmap_update_entry(heap, index, entry);
|
544
|
+
return;
|
545
|
+
}
|
546
|
+
DHEAP_PUSH(dheapmap, heap, entry);
|
547
|
+
}
|
548
|
+
#endif
|
549
|
+
|
300
550
|
/*
|
301
|
-
*
|
302
|
-
*
|
551
|
+
* Inserts a value into the heap, using a score to determine sort-order.
|
552
|
+
*
|
553
|
+
* Score comes first, as an analogy with the +Array#insert+ index.
|
554
|
+
*
|
555
|
+
* Time complexity: <b>O(log n / log d)</b> <i>(worst-case)</i>
|
556
|
+
*
|
557
|
+
* @param score [Integer,Float,#to_f] a score to compare against other scores.
|
558
|
+
* @param value [Object] an object that is associated with the score.
|
303
559
|
*
|
304
560
|
* @return [self]
|
305
561
|
*/
|
306
562
|
static VALUE
|
307
|
-
|
308
|
-
|
309
|
-
|
310
|
-
|
311
|
-
|
312
|
-
|
563
|
+
dheap_insert(VALUE self, VALUE score, VALUE value)
|
564
|
+
{
|
565
|
+
ENTRY entry = { VAL2SCORE(score), value };
|
566
|
+
dheap_push_entry(self, &entry);
|
567
|
+
return self;
|
568
|
+
}
|
569
|
+
|
570
|
+
#ifdef DHEAP_MAP
|
571
|
+
/* (see DHeap#insert) */
|
572
|
+
static VALUE
|
573
|
+
dheapmap_insert(VALUE self, VALUE score, VALUE value)
|
574
|
+
{
|
575
|
+
ENTRY entry = { VAL2SCORE(score), value };
|
576
|
+
dheapmap_push_entry(self, &entry);
|
577
|
+
return self;
|
578
|
+
}
|
579
|
+
#endif
|
580
|
+
|
581
|
+
static inline ENTRY
|
582
|
+
dheap_push_args_to_entry(int argc, VALUE *argv)
|
583
|
+
{
|
584
|
+
ENTRY entry;
|
585
|
+
rb_check_arity(argc, 1, 2);
|
586
|
+
entry.value = argv[0];
|
587
|
+
entry.score = VAL2SCORE(argc < 2 ? entry.value : argv[1]);
|
588
|
+
return entry;
|
313
589
|
}
|
314
590
|
|
315
591
|
/*
|
316
|
-
* @overload push(
|
592
|
+
* @overload push(value, score = value)
|
317
593
|
*
|
318
594
|
* Push a value onto heap, using a score to determine sort-order.
|
319
595
|
*
|
320
|
-
*
|
321
|
-
*
|
596
|
+
* Value comes first because the separate score is optional, and because it
|
597
|
+
* feels like a more natural variation on +Array#push+ or +Queue#enq+. If a
|
598
|
+
* score isn't provided, the value must be an Integer or can be cast with
|
599
|
+
* +Float(value)+.
|
322
600
|
*
|
323
601
|
* Time complexity: <b>O(log n / log d)</b> <i>(worst-case)</i>
|
324
602
|
*
|
325
|
-
* @param score [#<=>] a value that can be compared to other scores.
|
326
603
|
* @param value [Object] an object that is associated with the score.
|
604
|
+
* @param score [Integer,Float,#to_f] a score to compare against other scores.
|
327
605
|
*
|
328
|
-
* @return [
|
606
|
+
* @return [self]
|
329
607
|
*/
|
330
608
|
static VALUE
|
331
|
-
dheap_push(int argc, VALUE *argv, VALUE self)
|
332
|
-
|
333
|
-
|
334
|
-
|
335
|
-
|
336
|
-
|
609
|
+
dheap_push(int argc, VALUE *argv, VALUE self)
|
610
|
+
{
|
611
|
+
ENTRY entry = dheap_push_args_to_entry(argc, argv);
|
612
|
+
dheap_push_entry(self, &entry);
|
613
|
+
return self;
|
614
|
+
}
|
337
615
|
|
338
|
-
|
339
|
-
|
340
|
-
|
616
|
+
#ifdef DHEAP_MAP
|
617
|
+
/* (see DHeap#push) */
|
618
|
+
static VALUE
|
619
|
+
dheapmap_push(int argc, VALUE *argv, VALUE self)
|
620
|
+
{
|
621
|
+
ENTRY entry = dheap_push_args_to_entry(argc, argv);
|
622
|
+
dheapmap_push_entry(self, &entry);
|
623
|
+
return self;
|
341
624
|
}
|
625
|
+
#endif
|
342
626
|
|
343
627
|
/*
|
344
|
-
* Pushes a
|
628
|
+
* Pushes a value onto the heap.
|
345
629
|
*
|
346
|
-
* The
|
630
|
+
* The score will be derived from the value, by using the value itself if it is
|
631
|
+
* an Integer, otherwise by casting it with +Float(value)+.
|
347
632
|
*
|
348
633
|
* Time complexity: <b>O(log n / log d)</b> <i>(worst-case)</i>
|
349
634
|
*
|
350
|
-
* @param value [
|
635
|
+
* @param value [Integer,#to_f] a value with an intrinsic numeric score
|
351
636
|
* @return [self]
|
352
637
|
*/
|
353
638
|
static VALUE
|
354
|
-
|
355
|
-
|
639
|
+
dheap_lshift(VALUE self, VALUE value)
|
640
|
+
{
|
641
|
+
ENTRY entry = { VAL2SCORE(value), value };
|
642
|
+
dheap_push_entry(self, &entry);
|
643
|
+
return self;
|
644
|
+
}
|
645
|
+
|
646
|
+
#ifdef DHEAP_MAP
|
647
|
+
/* (see DHeap#<<) */
|
648
|
+
static VALUE
|
649
|
+
dheapmap_lshift(VALUE self, VALUE value)
|
650
|
+
{
|
651
|
+
ENTRY entry = { VAL2SCORE(value), value };
|
652
|
+
dheapmap_push_entry(self, &entry);
|
356
653
|
return self;
|
357
654
|
}
|
655
|
+
#endif
|
656
|
+
|
657
|
+
/********************************************************************
|
658
|
+
*
|
659
|
+
* DHeap pop and peek
|
660
|
+
*
|
661
|
+
********************************************************************/
|
662
|
+
|
663
|
+
#define PEEK_VALUE(heap) DHEAP_VALUE(heap, 0)
|
664
|
+
#define PEEK_SCORE(heap) DHEAP_SCORE(heap, 0)
|
665
|
+
#define PEEK_WITH_SCORE(heap) DHEAP_ENTRY_ARY(heap, 0)
|
666
|
+
#define PEEK_LT_P(heap, max_score) _PEEK_CMP_P(heap, CMP_LT, max_score)
|
667
|
+
#define PEEK_LTE_P(heap, max_score) _PEEK_CMP_P(heap, CMP_LTE, max_score)
|
668
|
+
#define _PEEK_CMP_P(heap, cmp, score) \
|
669
|
+
((!DHEAP_EMPTY_P(heap) && cmp(PEEK_SCORE(heap), (score))) \
|
670
|
+
? PEEK_VALUE(heap) \
|
671
|
+
: 0)
|
358
672
|
|
359
673
|
/*
|
360
|
-
|
361
|
-
|
362
|
-
|
363
|
-
|
364
|
-
|
365
|
-
|
674
|
+
* Returns the next value on the heap, and its score, without popping it
|
675
|
+
*
|
676
|
+
* Time complexity: <b>O(1)</b> <i>(worst-case)</i>
|
677
|
+
* @return [nil,Array<(Object, Numeric)>] the next value and its score
|
678
|
+
*
|
679
|
+
* @see #peek
|
680
|
+
* @see #peek_score
|
681
|
+
*/
|
682
|
+
static VALUE
|
683
|
+
dheap_peek_with_score(VALUE self)
|
684
|
+
{
|
685
|
+
dheap_t *heap = get_dheap_struct(self);
|
686
|
+
return PEEK_WITH_SCORE(heap);
|
687
|
+
}
|
366
688
|
|
367
|
-
|
368
|
-
|
689
|
+
/*
|
690
|
+
* Returns the next score on the heap, without the value and without popping
|
691
|
+
* it.
|
692
|
+
*
|
693
|
+
* Time complexity: <b>O(1)</b> <i>(worst-case)</i>
|
694
|
+
* @return [nil, Numeric] the next score, if there is one
|
695
|
+
*
|
696
|
+
* @see #peek
|
697
|
+
* @see #peek_with_score
|
698
|
+
*/
|
699
|
+
static VALUE
|
700
|
+
dheap_peek_score(VALUE self)
|
369
701
|
{
|
370
|
-
|
371
|
-
|
372
|
-
|
373
|
-
else
|
374
|
-
{
|
375
|
-
VALUE sift_value = DHEAP_VALUE(heap, last_index);
|
376
|
-
VALUE sift_score = DHEAP_SCORE(heap, last_index);
|
377
|
-
DHEAP_ASSIGN(heap, 0, sift_score, sift_value);
|
378
|
-
DHEAP_DROP_LAST(heap);
|
379
|
-
dheap_ary_sift_down(heap, 0);
|
380
|
-
}
|
702
|
+
dheap_t *heap = get_dheap_struct(self);
|
703
|
+
if (DHEAP_EMPTY_P(heap)) return Qnil;
|
704
|
+
return SCORE2NUM(PEEK_SCORE(heap));
|
381
705
|
}
|
382
706
|
|
383
707
|
/*
|
384
708
|
* Returns the next value on the heap to be popped without popping it.
|
385
709
|
*
|
386
710
|
* Time complexity: <b>O(1)</b> <i>(worst-case)</i>
|
387
|
-
* @return [Object] the next value to be popped without popping it.
|
711
|
+
* @return [nil, Object] the next value to be popped without popping it.
|
712
|
+
*
|
713
|
+
* @see #peek_score
|
714
|
+
* @see #peek_with_score
|
388
715
|
*/
|
389
716
|
static VALUE
|
390
|
-
dheap_peek(VALUE self)
|
717
|
+
dheap_peek(VALUE self)
|
718
|
+
{
|
391
719
|
dheap_t *heap = get_dheap_struct(self);
|
392
|
-
if (
|
393
|
-
return
|
720
|
+
if (DHEAP_EMPTY_P(heap)) return Qnil;
|
721
|
+
return PEEK_VALUE(heap);
|
394
722
|
}
|
395
723
|
|
724
|
+
#define DHEAP_POP_COMMON_INIT(self) \
|
725
|
+
dheap_t *heap = get_dheap_struct_unfrozen(self); \
|
726
|
+
if (DHEAP_EMPTY_P(heap)) return Qnil;
|
727
|
+
|
728
|
+
#define DHEAP_DELETE_0(T, heap) \
|
729
|
+
do { \
|
730
|
+
_DELETE_ENTRY(T, heap, 0); \
|
731
|
+
if (0 < --(heap)->size) { \
|
732
|
+
DHEAP_SET(T, (heap), 0, (heap)->entries[(heap)->size]); \
|
733
|
+
DHEAP_SIFT_DOWN(T, (heap), 0); \
|
734
|
+
} \
|
735
|
+
} while (0)
|
736
|
+
|
737
|
+
#define _DELETE_ENTRY(T, heap, idx) _DELETE_ENTRY_##T(heap, idx)
|
738
|
+
#define _DELETE_ENTRY_dheap(heap, idx) /* noop */
|
739
|
+
#define _DELETE_ENTRY_dheapmap(heap, idx) \
|
740
|
+
rb_hash_delete(heap->indexes, DHEAP_VALUE(heap, idx))
|
741
|
+
|
742
|
+
#define POP(T, heap, popped) _POP(T, VALUE, heap, popped)
|
743
|
+
#define POP_WITH_SCORE(T, heap, popped) _POP(T, WITH_SCORE, heap, popped)
|
744
|
+
#define POP_LT(T, heap, max, popped) _POP_CMP(T, heap, LT, max, popped)
|
745
|
+
#define POP_LTE(T, heap, max, popped) _POP_CMP(T, heap, LTE, max, popped)
|
746
|
+
|
747
|
+
#define _POP_CMP(T, heap, cmp, cmp_score, popped) \
|
748
|
+
do { \
|
749
|
+
if ((*(popped) = PEEK_##cmp##_P(heap, cmp_score))) { \
|
750
|
+
DHEAP_DELETE_0(T, heap); \
|
751
|
+
} else { \
|
752
|
+
*(popped) = Qnil; \
|
753
|
+
} \
|
754
|
+
} while (0)
|
755
|
+
|
756
|
+
#define _POP(T, peek_type, heap, popped) \
|
757
|
+
do { \
|
758
|
+
if (DHEAP_EMPTY_P(heap)) { \
|
759
|
+
*(popped) = Qnil; \
|
760
|
+
} else { \
|
761
|
+
*(popped) = PEEK_##peek_type(heap); \
|
762
|
+
DHEAP_DELETE_0(T, heap); \
|
763
|
+
} \
|
764
|
+
} while (0)
|
765
|
+
|
396
766
|
/*
|
397
767
|
* Pops the minimum value from the top of the heap
|
398
768
|
*
|
399
769
|
* Time complexity: <b>O(d log n / log d)</b> <i>(worst-case)</i>
|
770
|
+
*
|
771
|
+
* @return [Object] the value with the minimum score
|
772
|
+
*
|
773
|
+
* @see #peek
|
774
|
+
* @see #pop_lt
|
775
|
+
* @see #pop_lte
|
776
|
+
* @see #pop_with_score
|
400
777
|
*/
|
401
778
|
static VALUE
|
402
|
-
dheap_pop(VALUE self)
|
403
|
-
|
404
|
-
|
405
|
-
|
406
|
-
|
407
|
-
|
779
|
+
dheap_pop(VALUE self)
|
780
|
+
{
|
781
|
+
dheap_t *heap = get_dheap_struct_unfrozen(self);
|
782
|
+
VALUE popped;
|
783
|
+
POP(dheap, heap, &popped);
|
784
|
+
return popped;
|
785
|
+
}
|
408
786
|
|
409
|
-
|
410
|
-
|
787
|
+
#ifdef DHEAP_MAP
|
788
|
+
/* (see DHeap#pop) */
|
789
|
+
static VALUE
|
790
|
+
dheapmap_pop(VALUE self)
|
791
|
+
{
|
792
|
+
dheap_t *heap = get_dheap_struct_unfrozen(self);
|
793
|
+
VALUE popped;
|
794
|
+
POP(dheapmap, heap, &popped);
|
795
|
+
return popped;
|
411
796
|
}
|
797
|
+
#endif
|
412
798
|
|
413
799
|
/*
|
414
|
-
* Pops the minimum value
|
800
|
+
* Pops the minimum value from the top of the heap, along with its score.
|
801
|
+
*
|
802
|
+
* Time complexity: <b>O(d log n / log d)</b> <i>(worst-case)</i>
|
415
803
|
*
|
416
|
-
* @
|
804
|
+
* @return [nil,Array<(Object, Numeric)>] the next value and its score
|
417
805
|
*
|
418
806
|
* @see #pop
|
807
|
+
* @see #peek_with_score
|
419
808
|
*/
|
420
809
|
static VALUE
|
421
|
-
|
422
|
-
|
423
|
-
|
810
|
+
dheap_pop_with_score(VALUE self)
|
811
|
+
{
|
812
|
+
dheap_t *heap = get_dheap_struct_unfrozen(self);
|
813
|
+
VALUE popped;
|
814
|
+
POP_WITH_SCORE(dheap, heap, &popped);
|
815
|
+
return popped;
|
816
|
+
}
|
424
817
|
|
425
|
-
|
426
|
-
|
818
|
+
#ifdef DHEAP_MAP
|
819
|
+
/* (see DHeap#pop_with_score) */
|
820
|
+
static VALUE
|
821
|
+
dheapmap_pop_with_score(VALUE self)
|
822
|
+
{
|
823
|
+
dheap_t *heap = get_dheap_struct_unfrozen(self);
|
824
|
+
VALUE popped;
|
825
|
+
POP_WITH_SCORE(dheapmap, heap, &popped);
|
826
|
+
return popped;
|
827
|
+
}
|
828
|
+
#endif
|
427
829
|
|
428
|
-
|
429
|
-
if (
|
830
|
+
#define DHEAP_POP_IF(heap, cmp, max_score) \
|
831
|
+
if (!cmp(PEEK_SCORE(heap), VAL2SCORE(max_score))) return Qnil
|
832
|
+
|
833
|
+
/*
|
834
|
+
* Pops the minimum value only if it is less than or equal to a max score.
|
835
|
+
*
|
836
|
+
* @param max_score [Integer,#to_f] the maximum score to be popped
|
837
|
+
*
|
838
|
+
* Time complexity: <b>O(d log n / log d)</b> <i>(worst-case)</i>
|
839
|
+
*
|
840
|
+
* @return [Object] the value with the minimum score
|
841
|
+
*
|
842
|
+
* @see #peek
|
843
|
+
* @see #pop
|
844
|
+
* @see #pop_lt
|
845
|
+
* @see #pop_all_below
|
846
|
+
*/
|
847
|
+
static VALUE
|
848
|
+
dheap_pop_lte(VALUE self, VALUE max_score)
|
849
|
+
{
|
850
|
+
dheap_t *heap = get_dheap_struct_unfrozen(self);
|
851
|
+
VALUE popped;
|
852
|
+
POP_LTE(dheap, heap, VAL2SCORE(max_score), &popped);
|
853
|
+
return popped;
|
854
|
+
}
|
430
855
|
|
431
|
-
|
432
|
-
|
856
|
+
#ifdef DHEAP_MAP
|
857
|
+
/* (see DHeap#pop_lte) */
|
858
|
+
static VALUE
|
859
|
+
dheapmap_pop_lte(VALUE self, VALUE max_score)
|
860
|
+
{
|
861
|
+
dheap_t *heap = get_dheap_struct_unfrozen(self);
|
862
|
+
VALUE popped;
|
863
|
+
POP_LTE(dheapmap, heap, VAL2SCORE(max_score), &popped);
|
864
|
+
return popped;
|
433
865
|
}
|
866
|
+
#endif
|
434
867
|
|
435
868
|
/*
|
436
869
|
* Pops the minimum value only if it is less than a max score.
|
437
870
|
*
|
438
|
-
* @param max_score [
|
871
|
+
* @param max_score [Integer,#to_f] the maximum score to be popped
|
439
872
|
*
|
440
873
|
* Time complexity: <b>O(d log n / log d)</b> <i>(worst-case)</i>
|
874
|
+
*
|
875
|
+
* @return [Object] the value with the minimum score
|
876
|
+
*
|
877
|
+
* @see #peek
|
878
|
+
* @see #pop
|
879
|
+
* @see #pop_lte
|
880
|
+
* @see #pop_all_below
|
441
881
|
*/
|
442
882
|
static VALUE
|
443
|
-
dheap_pop_lt(VALUE self, VALUE max_score)
|
444
|
-
|
445
|
-
|
883
|
+
dheap_pop_lt(VALUE self, VALUE max_score)
|
884
|
+
{
|
885
|
+
dheap_t *heap = get_dheap_struct_unfrozen(self);
|
886
|
+
VALUE popped;
|
887
|
+
POP_LT(dheap, heap, VAL2SCORE(max_score), &popped);
|
888
|
+
return popped;
|
889
|
+
}
|
446
890
|
|
447
|
-
|
448
|
-
|
891
|
+
#ifdef DHEAP_MAP
|
892
|
+
/* (see DHeap#pop_lt) */
|
893
|
+
static VALUE
|
894
|
+
dheapmap_pop_lt(VALUE self, VALUE max_score)
|
895
|
+
{
|
896
|
+
dheap_t *heap = get_dheap_struct_unfrozen(self);
|
897
|
+
VALUE popped;
|
898
|
+
POP_LT(dheapmap, heap, VAL2SCORE(max_score), &popped);
|
899
|
+
return popped;
|
900
|
+
}
|
901
|
+
#endif
|
902
|
+
|
903
|
+
#define POP_ALL_BELOW(T, heap, max_score, array) \
|
904
|
+
do { \
|
905
|
+
VALUE val = Qnil; \
|
906
|
+
if (RB_TYPE_P(array, T_ARRAY)) { \
|
907
|
+
while ((val = PEEK_LT_P(heap, max_score))) { \
|
908
|
+
DHEAP_DELETE_0(T, heap); \
|
909
|
+
rb_ary_push(array, val); \
|
910
|
+
} \
|
911
|
+
} else { \
|
912
|
+
while ((val = PEEK_LT_P(heap, max_score))) { \
|
913
|
+
DHEAP_DELETE_0(T, heap); \
|
914
|
+
rb_funcall(array, id_lshift, 1, val); \
|
915
|
+
} \
|
916
|
+
} \
|
917
|
+
} while (0)
|
918
|
+
|
919
|
+
/*
|
920
|
+
* @overload pop_all_below(max_score, receiver = [])
|
921
|
+
*
|
922
|
+
* Pops all value with score less than max score.
|
923
|
+
*
|
924
|
+
* Time complexity: <b>O(m * d log n / log d)</b>, <i>m = number popped</i>
|
925
|
+
*
|
926
|
+
* @param max_score [Integer,#to_f] the maximum score to be popped
|
927
|
+
* @param receiver [Array,#<<] object onto which the values will be pushed,
|
928
|
+
* in order by score.
|
929
|
+
*
|
930
|
+
* @return [Object] the object onto which the values were pushed
|
931
|
+
*
|
932
|
+
* @see #pop
|
933
|
+
* @see #pop_lt
|
934
|
+
*/
|
935
|
+
static VALUE
|
936
|
+
dheap_pop_all_below(int argc, VALUE *argv, VALUE self)
|
937
|
+
{
|
938
|
+
dheap_t *heap = get_dheap_struct_unfrozen(self);
|
939
|
+
SCORE max_score = (argc) ? VAL2SCORE(argv[0]) : 0.0;
|
940
|
+
VALUE array = (argc == 1) ? rb_ary_new() : argv[1];
|
941
|
+
rb_check_arity(argc, 1, 2);
|
942
|
+
DHEAP_DISPATCH_STMT(heap, POP_ALL_BELOW, max_score, array);
|
943
|
+
return array;
|
944
|
+
}
|
449
945
|
|
450
|
-
|
451
|
-
|
946
|
+
/********************************************************************
|
947
|
+
*
|
948
|
+
* DHeap, misc methods
|
949
|
+
*
|
950
|
+
********************************************************************/
|
452
951
|
|
453
|
-
|
454
|
-
|
952
|
+
static VALUE
|
953
|
+
dheap_to_a(VALUE self)
|
954
|
+
{
|
955
|
+
dheap_t *heap = get_dheap_struct(self);
|
956
|
+
VALUE array = rb_ary_new_capa(heap->size);
|
957
|
+
for (size_t i = 0; i < heap->size; i++) {
|
958
|
+
rb_ary_push(array, DHEAP_ENTRY_ARY(heap, i));
|
959
|
+
}
|
960
|
+
return array;
|
455
961
|
}
|
456
962
|
|
963
|
+
/*
|
964
|
+
* Clears all values from the heap, leaving it empty.
|
965
|
+
*
|
966
|
+
* @return [self]
|
967
|
+
*/
|
968
|
+
static VALUE
|
969
|
+
dheap_clear(VALUE self)
|
970
|
+
{
|
971
|
+
dheap_t *heap = get_dheap_struct_unfrozen(self);
|
972
|
+
if (!DHEAP_EMPTY_P(heap)) {
|
973
|
+
heap->size = 0;
|
974
|
+
#ifdef DHEAP_MAP
|
975
|
+
if (DHEAPMAP_P(heap)) rb_hash_clear(heap->indexes);
|
976
|
+
#endif
|
977
|
+
}
|
978
|
+
return self;
|
979
|
+
}
|
980
|
+
|
981
|
+
/********************************************************************
|
982
|
+
*
|
983
|
+
* DHeap::Map methods
|
984
|
+
*
|
985
|
+
********************************************************************/
|
986
|
+
|
987
|
+
#ifdef DHEAP_MAP
|
988
|
+
|
989
|
+
/*
|
990
|
+
* Retrieves the score that has been assigned to a heap member.
|
991
|
+
*
|
992
|
+
* Time complexity: <b>O(1)</b>
|
993
|
+
*
|
994
|
+
* @param object [Object] an object to lookup
|
995
|
+
* @return [Float,Integer,nil] the score associated with the object,
|
996
|
+
* or nil if the object isn't a member
|
997
|
+
*/
|
998
|
+
static VALUE
|
999
|
+
dheapmap_aref(VALUE self, VALUE object)
|
1000
|
+
{
|
1001
|
+
dheap_t *heap = get_dheap_struct(self);
|
1002
|
+
VALUE idxval = rb_hash_lookup2(heap->indexes, object, Qfalse);
|
1003
|
+
if (idxval) {
|
1004
|
+
size_t index = NUM2ULONG(idxval);
|
1005
|
+
return SCORE2NUM(DHEAP_SCORE(heap, index));
|
1006
|
+
}
|
1007
|
+
return Qnil;
|
1008
|
+
}
|
1009
|
+
|
1010
|
+
/*
|
1011
|
+
* Assign a score to an object, adding it to the heap or updating as necessary.
|
1012
|
+
*
|
1013
|
+
* Time complexity: <b>O(log n)</b> (score decrease will be faster than
|
1014
|
+
* score increase)
|
1015
|
+
*
|
1016
|
+
* @param object [Object] an object to lookup
|
1017
|
+
* @param score [Integer,#to_f] the score to set
|
1018
|
+
* @return [Float,Integer] the score
|
1019
|
+
*/
|
1020
|
+
static VALUE
|
1021
|
+
dheapmap_aset(VALUE self, VALUE object, VALUE score)
|
1022
|
+
{
|
1023
|
+
dheapmap_insert(self, score, object);
|
1024
|
+
return score;
|
1025
|
+
}
|
1026
|
+
|
1027
|
+
#endif
|
1028
|
+
|
1029
|
+
/********************************************************************
|
1030
|
+
*
|
1031
|
+
* DHeap setup
|
1032
|
+
*
|
1033
|
+
********************************************************************/
|
1034
|
+
|
457
1035
|
void
|
458
1036
|
Init_d_heap(void)
|
459
1037
|
{
|
460
|
-
|
461
|
-
|
462
|
-
|
463
|
-
|
1038
|
+
VALUE rb_cDHeap = rb_define_class("DHeap", rb_cObject);
|
1039
|
+
#ifdef DHEAP_MAP
|
1040
|
+
VALUE rb_cDHeapMap = rb_define_class_under(rb_cDHeap, "Map", rb_cDHeap);
|
1041
|
+
#endif
|
1042
|
+
|
1043
|
+
id_cmp = rb_intern_const("<=>");
|
1044
|
+
id_abs = rb_intern_const("abs");
|
1045
|
+
id_lshift = rb_intern_const("<<");
|
1046
|
+
id_uminus = rb_intern_const("-@");
|
464
1047
|
|
465
|
-
rb_cDHeap = rb_define_class("DHeap", rb_cObject);
|
466
1048
|
rb_define_alloc_func(rb_cDHeap, dheap_s_alloc);
|
467
1049
|
|
1050
|
+
#ifdef DHEAP_MAP
|
1051
|
+
# define def_override_inherited(rb_name, c_name, argc) \
|
1052
|
+
rb_define_method(rb_cDHeap, rb_name, dheap_##c_name, argc); \
|
1053
|
+
rb_define_method(rb_cDHeapMap, rb_name, dheapmap_##c_name, argc);
|
1054
|
+
#else
|
1055
|
+
# define def_override_inherited(rb_name, c_name, argc) \
|
1056
|
+
rb_define_method(rb_cDHeap, rb_name, dheap_##c_name, argc);
|
1057
|
+
#endif
|
1058
|
+
|
1059
|
+
/*
|
1060
|
+
* This is based on INT_MAX. But it is very very unlikely you will want a
|
1061
|
+
* large value for d. The tradeoff is that higher d values give faster push
|
1062
|
+
* and slower pop. If you expect pushes and pops to be balanced, then just
|
1063
|
+
* stick with the default. If you expect more pushes than pops, it might be
|
1064
|
+
* worthwhile to increase d.
|
1065
|
+
*/
|
468
1066
|
rb_define_const(rb_cDHeap, "MAX_D", INT2NUM(DHEAP_MAX_D));
|
1067
|
+
|
1068
|
+
/*
|
1069
|
+
* d=4 uses the fewest comparisons for (worst case) insert + delete-min.
|
1070
|
+
*/
|
469
1071
|
rb_define_const(rb_cDHeap, "DEFAULT_D", INT2NUM(DHEAP_DEFAULT_D));
|
470
1072
|
|
471
|
-
|
472
|
-
|
473
|
-
|
1073
|
+
/*
|
1074
|
+
* The default heap capacity. The heap grows automatically as necessary, so
|
1075
|
+
* you shouldn't need to worry about this.
|
1076
|
+
*/
|
1077
|
+
rb_define_const(rb_cDHeap, "DEFAULT_CAPA", INT2NUM(DHEAP_DEFAULT_CAPA));
|
1078
|
+
|
1079
|
+
rb_define_private_method(rb_cDHeap, "__init_without_kw__", dheap_init, 3);
|
1080
|
+
rb_define_method(rb_cDHeap, "initialize_copy", dheap_initialize_copy, 1);
|
474
1081
|
|
1082
|
+
rb_define_method(rb_cDHeap, "d", dheap_attr_d, 0);
|
475
1083
|
rb_define_method(rb_cDHeap, "size", dheap_size, 0);
|
476
1084
|
rb_define_method(rb_cDHeap, "empty?", dheap_empty_p, 0);
|
477
|
-
|
478
|
-
|
479
|
-
rb_define_method(rb_cDHeap, "
|
480
|
-
rb_define_method(rb_cDHeap, "
|
481
|
-
rb_define_method(rb_cDHeap, "
|
482
|
-
rb_define_method(rb_cDHeap, "
|
483
|
-
rb_define_method(rb_cDHeap, "
|
1085
|
+
rb_define_method(rb_cDHeap, "to_a", dheap_to_a, 0);
|
1086
|
+
|
1087
|
+
rb_define_method(rb_cDHeap, "clear", dheap_clear, 0);
|
1088
|
+
rb_define_method(rb_cDHeap, "peek", dheap_peek, 0);
|
1089
|
+
rb_define_method(rb_cDHeap, "peek_score", dheap_peek_score, 0);
|
1090
|
+
rb_define_method(rb_cDHeap, "peek_with_score", dheap_peek_with_score, 0);
|
1091
|
+
rb_define_method(rb_cDHeap, "pop_all_below", dheap_pop_all_below, -1);
|
1092
|
+
|
1093
|
+
def_override_inherited("insert", insert, 2);
|
1094
|
+
def_override_inherited("push", push, -1);
|
1095
|
+
def_override_inherited("<<", lshift, 1);
|
1096
|
+
|
1097
|
+
def_override_inherited("pop", pop, 0);
|
1098
|
+
def_override_inherited("pop_lt", pop_lt, 1);
|
1099
|
+
def_override_inherited("pop_lte", pop_lte, 1);
|
1100
|
+
def_override_inherited("pop_with_score", pop_with_score, 0);
|
1101
|
+
|
1102
|
+
#ifdef DHEAP_MAP
|
1103
|
+
rb_define_method(rb_cDHeapMap, "[]", dheapmap_aref, 1);
|
1104
|
+
rb_define_method(rb_cDHeapMap, "[]=", dheapmap_aset, 2);
|
1105
|
+
#endif
|
484
1106
|
}
|