d_heap 0.2.2 → 0.6.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.github/workflows/main.yml +2 -2
- data/.gitignore +1 -0
- data/.rubocop.yml +40 -1
- data/.yardopts +10 -0
- data/CHANGELOG.md +76 -0
- data/Gemfile +9 -0
- data/Gemfile.lock +26 -1
- data/N +7 -0
- data/README.md +358 -147
- data/benchmarks/perf.rb +29 -0
- data/benchmarks/push_n.yml +35 -0
- data/benchmarks/push_n_pop_n.yml +52 -0
- data/benchmarks/push_pop.yml +32 -0
- data/benchmarks/stackprof.rb +31 -0
- data/bin/bench_charts +13 -0
- data/bin/bench_n +7 -0
- data/bin/benchmark-driver +29 -0
- data/bin/benchmarks +10 -0
- data/bin/profile +10 -0
- data/d_heap.gemspec +5 -2
- data/docs/benchmarks-2.txt +75 -0
- data/docs/benchmarks-mem.txt +39 -0
- data/docs/benchmarks.txt +515 -0
- data/docs/profile.txt +392 -0
- data/ext/d_heap/d_heap.c +824 -246
- data/ext/d_heap/extconf.rb +16 -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/benchmark_driver/runner/ips_zero_fail.rb +158 -0
- data/lib/d_heap.rb +92 -3
- data/lib/d_heap/benchmarks.rb +112 -0
- data/lib/d_heap/benchmarks/benchmarker.rb +116 -0
- data/lib/d_heap/benchmarks/implementations.rb +224 -0
- data/lib/d_heap/benchmarks/profiler.rb +71 -0
- data/lib/d_heap/benchmarks/rspec_matchers.rb +352 -0
- data/lib/d_heap/version.rb +1 -1
- metadata +60 -6
- data/ext/d_heap/d_heap.h +0 -65
data/docs/profile.txt
ADDED
@@ -0,0 +1,392 @@
|
|
1
|
+
Profiling run at 2021-01-10 21:34:56 -0500
|
2
|
+
ruby v2.7.2, DHeap v0.3.0
|
3
|
+
|
4
|
+
~~~~~~ filling @dheap_bm_random_vals with 1.0M ~~~~~~
|
5
|
+
########################################################################
|
6
|
+
# Profile w/ N=5 (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 push and resort ---------------------------
|
12
|
+
Profiling push and resort ---------------------------
|
13
|
+
Measure Mode: wall_time
|
14
|
+
Thread ID: 1360
|
15
|
+
Fiber ID: 1340
|
16
|
+
Total: 2.639873
|
17
|
+
Sort by: self_time
|
18
|
+
|
19
|
+
%self total self wait child calls name location
|
20
|
+
31.49 2.640 0.831 0.000 1.808 1 DHeap::Benchmarks::Scenarios#repeated_push_pop /home/nick/src/d_heap/lib/d_heap/benchmarks.rb:81
|
21
|
+
25.69 1.219 0.678 0.000 0.541 1000000 DHeap::Benchmarks::Sorting#<< /home/nick/src/d_heap/lib/d_heap/benchmarks/implementations.rb:35
|
22
|
+
15.00 0.396 0.396 0.000 0.000 1000000 Array#sort!
|
23
|
+
12.38 0.462 0.327 0.000 0.136 1000000 DHeap::Benchmarks::Sorting#pop /home/nick/src/d_heap/lib/d_heap/benchmarks/implementations.rb:42
|
24
|
+
5.50 0.145 0.145 0.000 0.000 1000000 Array#push
|
25
|
+
5.13 0.136 0.136 0.000 0.000 1000000 Array#shift
|
26
|
+
4.81 0.127 0.127 0.000 0.000 1000000 Array#fetch
|
27
|
+
|
28
|
+
* recursively called methods
|
29
|
+
|
30
|
+
Columns are:
|
31
|
+
|
32
|
+
%self - The percentage of time spent in this method, derived from self_time/total_time.
|
33
|
+
total - The time spent in this method and its children.
|
34
|
+
self - The time spent in this method.
|
35
|
+
wait - The amount of time this method waited for other threads.
|
36
|
+
child - The time spent in this method's children.
|
37
|
+
calls - The number of times this method was called.
|
38
|
+
name - The name of the method.
|
39
|
+
location - The location of the method.
|
40
|
+
|
41
|
+
The interpretation of method names is:
|
42
|
+
|
43
|
+
* MyObject#test - An instance method "test" of the class "MyObject"
|
44
|
+
* <Object:MyObject>#test - The <> characters indicate a method on a singleton class.
|
45
|
+
|
46
|
+
Filling bsearch + insert ---------------------------
|
47
|
+
Profiling bsearch + insert ---------------------------
|
48
|
+
Measure Mode: wall_time
|
49
|
+
Thread ID: 1360
|
50
|
+
Fiber ID: 1340
|
51
|
+
Total: 2.634233
|
52
|
+
Sort by: self_time
|
53
|
+
|
54
|
+
%self total self wait child calls name location
|
55
|
+
30.73 2.634 0.810 0.000 1.825 1 DHeap::Benchmarks::Scenarios#repeated_push_pop /home/nick/src/d_heap/lib/d_heap/benchmarks.rb:81
|
56
|
+
25.94 1.228 0.683 0.000 0.545 1000000 DHeap::Benchmarks::BSearch#<< /home/nick/src/d_heap/lib/d_heap/benchmarks/implementations.rb:61
|
57
|
+
14.83 0.391 0.391 0.000 0.000 1000000 Array#bsearch_index
|
58
|
+
12.79 0.462 0.337 0.000 0.125 1000000 DHeap::Benchmarks::BSearch#pop /home/nick/src/d_heap/lib/d_heap/benchmarks/implementations.rb:70
|
59
|
+
5.85 0.154 0.154 0.000 0.000 1000000 Array#insert
|
60
|
+
5.10 0.134 0.134 0.000 0.000 1000000 Array#fetch
|
61
|
+
4.76 0.125 0.125 0.000 0.000 1000000 Array#pop
|
62
|
+
|
63
|
+
* recursively called methods
|
64
|
+
|
65
|
+
Columns are:
|
66
|
+
|
67
|
+
%self - The percentage of time spent in this method, derived from self_time/total_time.
|
68
|
+
total - The time spent in this method and its children.
|
69
|
+
self - The time spent in this method.
|
70
|
+
wait - The amount of time this method waited for other threads.
|
71
|
+
child - The time spent in this method's children.
|
72
|
+
calls - The number of times this method was called.
|
73
|
+
name - The name of the method.
|
74
|
+
location - The location of the method.
|
75
|
+
|
76
|
+
The interpretation of method names is:
|
77
|
+
|
78
|
+
* MyObject#test - An instance method "test" of the class "MyObject"
|
79
|
+
* <Object:MyObject>#test - The <> characters indicate a method on a singleton class.
|
80
|
+
|
81
|
+
Filling ruby binary heap ---------------------------
|
82
|
+
Profiling ruby binary heap ---------------------------
|
83
|
+
Measure Mode: wall_time
|
84
|
+
Thread ID: 1360
|
85
|
+
Fiber ID: 1340
|
86
|
+
Total: 4.940103
|
87
|
+
Sort by: self_time
|
88
|
+
|
89
|
+
%self total self wait child calls name location
|
90
|
+
37.38 1.973 1.846 0.000 0.126 1000000 DHeap::Benchmarks::RbHeap#<< /home/nick/src/d_heap/lib/d_heap/benchmarks/implementations.rb:80
|
91
|
+
33.66 2.042 1.663 0.000 0.380 1000000 DHeap::Benchmarks::RbHeap#pop /home/nick/src/d_heap/lib/d_heap/benchmarks/implementations.rb:96
|
92
|
+
16.10 4.940 0.795 0.000 4.145 1 DHeap::Benchmarks::Scenarios#repeated_push_pop /home/nick/src/d_heap/lib/d_heap/benchmarks.rb:81
|
93
|
+
2.64 0.130 0.130 0.000 0.000 1000000 Array#pop
|
94
|
+
2.63 0.130 0.130 0.000 0.000 1000000 Array#first
|
95
|
+
2.62 0.130 0.130 0.000 0.000 1000000 Array#fetch
|
96
|
+
2.56 0.126 0.126 0.000 0.000 1000000 Array#push
|
97
|
+
2.43 0.120 0.120 0.000 0.000 1000000 Array#last
|
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: 1360
|
121
|
+
Fiber ID: 1340
|
122
|
+
Total: 1.231464
|
123
|
+
Sort by: self_time
|
124
|
+
|
125
|
+
%self total self wait child calls name location
|
126
|
+
60.13 1.231 0.741 0.000 0.491 1 DHeap::Benchmarks::Scenarios#repeated_push_pop /home/nick/src/d_heap/lib/d_heap/benchmarks.rb:81
|
127
|
+
16.72 0.206 0.206 0.000 0.000 1000000 DHeap#<<
|
128
|
+
12.85 0.158 0.158 0.000 0.000 1000000 DHeap#pop
|
129
|
+
10.30 0.127 0.127 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=1365 (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 push and resort ---------------------------
|
156
|
+
Profiling push and resort ---------------------------
|
157
|
+
Measure Mode: wall_time
|
158
|
+
Thread ID: 1360
|
159
|
+
Fiber ID: 1340
|
160
|
+
Total: 41.950612
|
161
|
+
Sort by: self_time
|
162
|
+
|
163
|
+
%self total self wait child calls name location
|
164
|
+
94.10 39.478 39.478 0.000 0.000 1000000 Array#sort!
|
165
|
+
2.11 41.951 0.884 0.000 41.066 1 DHeap::Benchmarks::Scenarios#repeated_push_pop /home/nick/src/d_heap/lib/d_heap/benchmarks.rb:81
|
166
|
+
1.68 40.328 0.707 0.000 39.621 1000000 DHeap::Benchmarks::Sorting#<< /home/nick/src/d_heap/lib/d_heap/benchmarks/implementations.rb:35
|
167
|
+
|
168
|
+
* recursively called methods
|
169
|
+
|
170
|
+
Columns are:
|
171
|
+
|
172
|
+
%self - The percentage of time spent in this method, derived from self_time/total_time.
|
173
|
+
total - The time spent in this method and its children.
|
174
|
+
self - The time spent in this method.
|
175
|
+
wait - The amount of time this method waited for other threads.
|
176
|
+
child - The time spent in this method's children.
|
177
|
+
calls - The number of times this method was called.
|
178
|
+
name - The name of the method.
|
179
|
+
location - The location of the method.
|
180
|
+
|
181
|
+
The interpretation of method names is:
|
182
|
+
|
183
|
+
* MyObject#test - An instance method "test" of the class "MyObject"
|
184
|
+
* <Object:MyObject>#test - The <> characters indicate a method on a singleton class.
|
185
|
+
|
186
|
+
Filling bsearch + insert ---------------------------
|
187
|
+
Profiling bsearch + insert ---------------------------
|
188
|
+
Measure Mode: wall_time
|
189
|
+
Thread ID: 1360
|
190
|
+
Fiber ID: 1340
|
191
|
+
Total: 3.559064
|
192
|
+
Sort by: self_time
|
193
|
+
|
194
|
+
%self total self wait child calls name location
|
195
|
+
37.92 1.349 1.349 0.000 0.000 1000000 Array#bsearch_index
|
196
|
+
22.76 3.559 0.810 0.000 2.749 1 DHeap::Benchmarks::Scenarios#repeated_push_pop /home/nick/src/d_heap/lib/d_heap/benchmarks.rb:81
|
197
|
+
18.47 2.157 0.657 0.000 1.499 1000000 DHeap::Benchmarks::BSearch#<< /home/nick/src/d_heap/lib/d_heap/benchmarks/implementations.rb:61
|
198
|
+
9.45 0.462 0.336 0.000 0.125 1000000 DHeap::Benchmarks::BSearch#pop /home/nick/src/d_heap/lib/d_heap/benchmarks/implementations.rb:70
|
199
|
+
4.21 0.150 0.150 0.000 0.000 1000000 Array#insert
|
200
|
+
3.67 0.131 0.131 0.000 0.000 1000000 Array#fetch
|
201
|
+
3.53 0.125 0.125 0.000 0.000 1000000 Array#pop
|
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
|
+
Filling ruby binary heap ---------------------------
|
222
|
+
Profiling ruby binary heap ---------------------------
|
223
|
+
Measure Mode: wall_time
|
224
|
+
Thread ID: 1360
|
225
|
+
Fiber ID: 1340
|
226
|
+
Total: 11.581886
|
227
|
+
Sort by: self_time
|
228
|
+
|
229
|
+
%self total self wait child calls name location
|
230
|
+
52.04 6.160 6.027 0.000 0.132 1000000 DHeap::Benchmarks::RbHeap#<< /home/nick/src/d_heap/lib/d_heap/benchmarks/implementations.rb:80
|
231
|
+
35.10 4.453 4.065 0.000 0.388 1000000 DHeap::Benchmarks::RbHeap#pop /home/nick/src/d_heap/lib/d_heap/benchmarks/implementations.rb:96
|
232
|
+
7.21 11.582 0.835 0.000 10.747 1 DHeap::Benchmarks::Scenarios#repeated_push_pop /home/nick/src/d_heap/lib/d_heap/benchmarks.rb:81
|
233
|
+
1.16 0.134 0.134 0.000 0.000 1000000 Array#fetch
|
234
|
+
1.14 0.132 0.132 0.000 0.000 1000000 Array#push
|
235
|
+
1.14 0.132 0.132 0.000 0.000 1000000 Array#pop
|
236
|
+
1.13 0.131 0.131 0.000 0.000 1000000 Array#first
|
237
|
+
1.08 0.125 0.125 0.000 0.000 1000000 Array#last
|
238
|
+
|
239
|
+
* recursively called methods
|
240
|
+
|
241
|
+
Columns are:
|
242
|
+
|
243
|
+
%self - The percentage of time spent in this method, derived from self_time/total_time.
|
244
|
+
total - The time spent in this method and its children.
|
245
|
+
self - The time spent in this method.
|
246
|
+
wait - The amount of time this method waited for other threads.
|
247
|
+
child - The time spent in this method's children.
|
248
|
+
calls - The number of times this method was called.
|
249
|
+
name - The name of the method.
|
250
|
+
location - The location of the method.
|
251
|
+
|
252
|
+
The interpretation of method names is:
|
253
|
+
|
254
|
+
* MyObject#test - An instance method "test" of the class "MyObject"
|
255
|
+
* <Object:MyObject>#test - The <> characters indicate a method on a singleton class.
|
256
|
+
|
257
|
+
Filling quaternary DHeap ---------------------------
|
258
|
+
Profiling quaternary DHeap ---------------------------
|
259
|
+
Measure Mode: wall_time
|
260
|
+
Thread ID: 1360
|
261
|
+
Fiber ID: 1340
|
262
|
+
Total: 1.431426
|
263
|
+
Sort by: self_time
|
264
|
+
|
265
|
+
%self total self wait child calls name location
|
266
|
+
50.61 1.431 0.724 0.000 0.707 1 DHeap::Benchmarks::Scenarios#repeated_push_pop /home/nick/src/d_heap/lib/d_heap/benchmarks.rb:81
|
267
|
+
21.61 0.309 0.309 0.000 0.000 1000000 DHeap#<<
|
268
|
+
19.18 0.275 0.275 0.000 0.000 1000000 DHeap#pop
|
269
|
+
8.59 0.123 0.123 0.000 0.000 1000000 Array#fetch
|
270
|
+
|
271
|
+
* recursively called methods
|
272
|
+
|
273
|
+
Columns are:
|
274
|
+
|
275
|
+
%self - The percentage of time spent in this method, derived from self_time/total_time.
|
276
|
+
total - The time spent in this method and its children.
|
277
|
+
self - The time spent in this method.
|
278
|
+
wait - The amount of time this method waited for other threads.
|
279
|
+
child - The time spent in this method's children.
|
280
|
+
calls - The number of times this method was called.
|
281
|
+
name - The name of the method.
|
282
|
+
location - The location of the method.
|
283
|
+
|
284
|
+
The interpretation of method names is:
|
285
|
+
|
286
|
+
* MyObject#test - An instance method "test" of the class "MyObject"
|
287
|
+
* <Object:MyObject>#test - The <> characters indicate a method on a singleton class.
|
288
|
+
|
289
|
+
########################################################################
|
290
|
+
# Profile w/ N=87381 (i=1000000)
|
291
|
+
# (n.b. RubyProf & tracepoint can change relative performance.
|
292
|
+
# A sampling profiler can provide more accurate relative metrics.
|
293
|
+
########################################################################
|
294
|
+
|
295
|
+
Filling bsearch + insert ---------------------------
|
296
|
+
Profiling bsearch + insert ---------------------------
|
297
|
+
Measure Mode: wall_time
|
298
|
+
Thread ID: 1360
|
299
|
+
Fiber ID: 1340
|
300
|
+
Total: 5.894803
|
301
|
+
Sort by: self_time
|
302
|
+
|
303
|
+
%self total self wait child calls name location
|
304
|
+
34.53 2.035 2.035 0.000 0.000 1000000 Array#bsearch_index
|
305
|
+
30.22 1.782 1.782 0.000 0.000 1000000 Array#insert
|
306
|
+
13.74 5.895 0.810 0.000 5.085 1 DHeap::Benchmarks::Scenarios#repeated_push_pop /home/nick/src/d_heap/lib/d_heap/benchmarks.rb:81
|
307
|
+
11.52 4.496 0.679 0.000 3.817 1000000 DHeap::Benchmarks::BSearch#<< /home/nick/src/d_heap/lib/d_heap/benchmarks/implementations.rb:61
|
308
|
+
5.70 0.459 0.336 0.000 0.124 1000000 DHeap::Benchmarks::BSearch#pop /home/nick/src/d_heap/lib/d_heap/benchmarks/implementations.rb:70
|
309
|
+
2.20 0.130 0.130 0.000 0.000 1000000 Array#fetch
|
310
|
+
2.10 0.124 0.124 0.000 0.000 1000000 Array#pop
|
311
|
+
|
312
|
+
* recursively called methods
|
313
|
+
|
314
|
+
Columns are:
|
315
|
+
|
316
|
+
%self - The percentage of time spent in this method, derived from self_time/total_time.
|
317
|
+
total - The time spent in this method and its children.
|
318
|
+
self - The time spent in this method.
|
319
|
+
wait - The amount of time this method waited for other threads.
|
320
|
+
child - The time spent in this method's children.
|
321
|
+
calls - The number of times this method was called.
|
322
|
+
name - The name of the method.
|
323
|
+
location - The location of the method.
|
324
|
+
|
325
|
+
The interpretation of method names is:
|
326
|
+
|
327
|
+
* MyObject#test - An instance method "test" of the class "MyObject"
|
328
|
+
* <Object:MyObject>#test - The <> characters indicate a method on a singleton class.
|
329
|
+
|
330
|
+
Filling ruby binary heap ---------------------------
|
331
|
+
Profiling ruby binary heap ---------------------------
|
332
|
+
Measure Mode: wall_time
|
333
|
+
Thread ID: 1360
|
334
|
+
Fiber ID: 1340
|
335
|
+
Total: 16.514635
|
336
|
+
Sort by: self_time
|
337
|
+
|
338
|
+
%self total self wait child calls name location
|
339
|
+
45.67 7.926 7.542 0.000 0.384 1000000 DHeap::Benchmarks::RbHeap#pop /home/nick/src/d_heap/lib/d_heap/benchmarks/implementations.rb:96
|
340
|
+
45.43 7.630 7.502 0.000 0.128 1000000 DHeap::Benchmarks::RbHeap#<< /home/nick/src/d_heap/lib/d_heap/benchmarks/implementations.rb:80
|
341
|
+
5.00 16.515 0.826 0.000 15.688 1 DHeap::Benchmarks::Scenarios#repeated_push_pop /home/nick/src/d_heap/lib/d_heap/benchmarks.rb:81
|
342
|
+
|
343
|
+
* recursively called methods
|
344
|
+
|
345
|
+
Columns are:
|
346
|
+
|
347
|
+
%self - The percentage of time spent in this method, derived from self_time/total_time.
|
348
|
+
total - The time spent in this method and its children.
|
349
|
+
self - The time spent in this method.
|
350
|
+
wait - The amount of time this method waited for other threads.
|
351
|
+
child - The time spent in this method's children.
|
352
|
+
calls - The number of times this method was called.
|
353
|
+
name - The name of the method.
|
354
|
+
location - The location of the method.
|
355
|
+
|
356
|
+
The interpretation of method names is:
|
357
|
+
|
358
|
+
* MyObject#test - An instance method "test" of the class "MyObject"
|
359
|
+
* <Object:MyObject>#test - The <> characters indicate a method on a singleton class.
|
360
|
+
|
361
|
+
Filling quaternary DHeap ---------------------------
|
362
|
+
Profiling quaternary DHeap ---------------------------
|
363
|
+
Measure Mode: wall_time
|
364
|
+
Thread ID: 1360
|
365
|
+
Fiber ID: 1340
|
366
|
+
Total: 1.622729
|
367
|
+
Sort by: self_time
|
368
|
+
|
369
|
+
%self total self wait child calls name location
|
370
|
+
44.66 1.623 0.725 0.000 0.898 1 DHeap::Benchmarks::Scenarios#repeated_push_pop /home/nick/src/d_heap/lib/d_heap/benchmarks.rb:81
|
371
|
+
27.42 0.445 0.445 0.000 0.000 1000000 DHeap#pop
|
372
|
+
20.41 0.331 0.331 0.000 0.000 1000000 DHeap#<<
|
373
|
+
7.51 0.122 0.122 0.000 0.000 1000000 Array#fetch
|
374
|
+
|
375
|
+
* recursively called methods
|
376
|
+
|
377
|
+
Columns are:
|
378
|
+
|
379
|
+
%self - The percentage of time spent in this method, derived from self_time/total_time.
|
380
|
+
total - The time spent in this method and its children.
|
381
|
+
self - The time spent in this method.
|
382
|
+
wait - The amount of time this method waited for other threads.
|
383
|
+
child - The time spent in this method's children.
|
384
|
+
calls - The number of times this method was called.
|
385
|
+
name - The name of the method.
|
386
|
+
location - The location of the method.
|
387
|
+
|
388
|
+
The interpretation of method names is:
|
389
|
+
|
390
|
+
* MyObject#test - An instance method "test" of the class "MyObject"
|
391
|
+
* <Object:MyObject>#test - The <> characters indicate a method on a singleton class.
|
392
|
+
|
data/ext/d_heap/d_heap.c
CHANGED
@@ -1,377 +1,955 @@
|
|
1
|
-
#include
|
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
|
-
|
1
|
+
#include <float.h>
|
2
|
+
#include <math.h>
|
3
|
+
#include "ruby.h"
|
4
|
+
|
5
|
+
/********************************************************************
|
6
|
+
*
|
7
|
+
* Type definitions
|
8
|
+
*
|
9
|
+
********************************************************************/
|
10
|
+
|
11
|
+
typedef struct dheap_struct dheap_t;
|
12
|
+
typedef struct dheap_entry ENTRY;
|
13
|
+
|
14
|
+
// TODO: convert SCORE to a union, and use an ENTRY flag for its type
|
15
|
+
typedef long double SCORE;
|
16
|
+
|
17
|
+
#if LDBL_MANT_DIG < SIZEOF_UNSIGNED_LONG_LONG * 8
|
18
|
+
#error 'unsigned long long' must fit into 'long double' mantissa
|
19
|
+
#endif
|
20
|
+
|
21
|
+
// TODO: test this code on a 32 bit system (it MUST have some bugs!)
|
22
|
+
#if SIZEOF_UNSIGNED_LONG_LONG * 8 != 64
|
23
|
+
#error 'unsigned long long' must be 64bits
|
24
|
+
#endif
|
25
|
+
|
26
|
+
/********************************************************************
|
27
|
+
*
|
28
|
+
* Struct definitions
|
29
|
+
*
|
30
|
+
********************************************************************/
|
31
|
+
|
32
|
+
struct dheap_struct {
|
33
|
+
int d;
|
34
|
+
long size;
|
35
|
+
long capa;
|
36
|
+
ENTRY *entries;
|
37
|
+
};
|
38
|
+
|
39
|
+
struct dheap_entry {
|
40
|
+
SCORE score;
|
41
|
+
VALUE value;
|
42
|
+
};
|
43
|
+
|
44
|
+
/********************************************************************
|
45
|
+
*
|
46
|
+
* Constant definitions
|
47
|
+
*
|
48
|
+
********************************************************************/
|
49
|
+
|
50
|
+
#define DHEAP_DEFAULT_D 4
|
51
|
+
#define DHEAP_MAX_D INT_MAX
|
52
|
+
|
53
|
+
// sizeof(ENTRY) => 32 bytes
|
54
|
+
// one kilobyte = 32 * 32 bytes
|
55
|
+
#define DHEAP_DEFAULT_CAPA 32
|
56
|
+
#define DHEAP_MAX_CAPA (LONG_MAX / (int)sizeof(ENTRY))
|
57
|
+
#define DHEAP_CAPA_INCR_MAX (10 * 1024 * 1024 / (int)sizeof(ENTRY))
|
58
|
+
|
59
|
+
static ID id_cmp; // <=>
|
60
|
+
static ID id_abs; // abs
|
61
|
+
static ID id_lshift; // <<
|
62
|
+
static ID id_unary_minus; // -@
|
63
|
+
static const ENTRY EmptyDheapEntry; // 0 value for safety overwrite after pop
|
64
|
+
static const rb_data_type_t dheap_data_type;
|
65
|
+
|
66
|
+
/********************************************************************
|
67
|
+
*
|
68
|
+
* SCORE: casting to and from VALUE
|
69
|
+
* adapted from similar methods in ruby's object.c
|
70
|
+
*
|
71
|
+
********************************************************************/
|
72
|
+
|
73
|
+
// ruby doesn't have a LDBL2NUM. :(
|
74
|
+
// So this only accomplishes a subset of what that ought to do.
|
75
|
+
static inline VALUE
|
76
|
+
SCORE2NUM(SCORE s)
|
77
|
+
{
|
78
|
+
if (floorl((long double) s) == s) {
|
79
|
+
if (s < 0) {
|
80
|
+
unsigned long long ull = (unsigned long long)(-s);
|
81
|
+
return rb_funcall(ULL2NUM(ull), id_unary_minus, 0, Qundef);
|
82
|
+
}
|
83
|
+
return ULL2NUM((unsigned long long)(s));
|
32
84
|
}
|
85
|
+
return rb_float_new((double)(s));
|
86
|
+
}
|
33
87
|
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
88
|
+
static inline SCORE
|
89
|
+
FIX2SCORE(VALUE x)
|
90
|
+
{
|
91
|
+
return (long double)FIX2LONG(x);
|
92
|
+
}
|
93
|
+
|
94
|
+
// We could translate a much wider range of values to long double by
|
95
|
+
// implementing a new `rb_big2ldbl(x)` function. But requires reaching into
|
96
|
+
// T_BIGNUM internals.
|
97
|
+
static inline long double
|
98
|
+
BIG2SCORE(VALUE x)
|
99
|
+
{
|
100
|
+
if (RBIGNUM_POSITIVE_P(x)) {
|
101
|
+
unsigned long long ull = rb_big2ull(x);
|
102
|
+
return (long double)ull;
|
103
|
+
} else {
|
104
|
+
unsigned long long ull;
|
105
|
+
long double ldbl;
|
106
|
+
x = rb_funcall(x, id_abs, 0, Qundef);
|
107
|
+
ull = rb_big2ull(x);
|
108
|
+
ldbl = (long double) ull;
|
109
|
+
return -ldbl;
|
40
110
|
}
|
111
|
+
}
|
41
112
|
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
\
|
48
|
-
VALUE sift_value = DHEAP_VALUE(heap_array, sift_index); \
|
49
|
-
VALUE sift_score = DHEAP_SCORE(heap_array, sift_index);
|
113
|
+
static inline SCORE
|
114
|
+
INT2SCORE(VALUE x)
|
115
|
+
{
|
116
|
+
return (FIXNUM_P(x) ? FIX2SCORE(x) : BIG2SCORE(x));
|
117
|
+
}
|
50
118
|
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
VALUE parent_score = DHEAP_SCORE(heap_array, parent_index);
|
119
|
+
static inline SCORE
|
120
|
+
NUM2SCORE(VALUE x)
|
121
|
+
{
|
122
|
+
return (FIXNUM_P(x) ? FIX2SCORE(x) :
|
123
|
+
RB_TYPE_P(x, T_BIGNUM) ? BIG2SCORE(x) :
|
124
|
+
(Check_Type(x, T_FLOAT), (long double)RFLOAT_VALUE(x)));
|
125
|
+
}
|
59
126
|
|
60
|
-
|
61
|
-
|
127
|
+
static inline long double
|
128
|
+
RAT2SCORE(VALUE x)
|
129
|
+
{
|
130
|
+
VALUE num = rb_rational_num(x);
|
131
|
+
VALUE den = rb_rational_den(x);
|
132
|
+
return NUM2SCORE(num) / NUM2SCORE(den);
|
133
|
+
}
|
62
134
|
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
135
|
+
/*
|
136
|
+
* Convert both T_FIXNUM and T_FLOAT (and sometimes T_BIGNUM, T_RATIONAL,
|
137
|
+
* String, etc) to SCORE
|
138
|
+
* * with no loss of precision (where possible for Integer and Float),
|
139
|
+
* * raises an exception if
|
140
|
+
* * a positive integer is too large for unsigned long long (should be 64bit)
|
141
|
+
* * a negative integer is too small for signed long long (should be 64bit)
|
142
|
+
* * reduced to long double (should be 80 or 128 bit) if it is Rational
|
143
|
+
* * reduced to double precision if the value is convertable by Float(x)
|
144
|
+
*/
|
145
|
+
static inline SCORE
|
146
|
+
VAL2SCORE(VALUE score)
|
147
|
+
{
|
148
|
+
// assert that long double can hold 'unsigned long long':
|
149
|
+
// static_assert(sizeof(unsigned long long) * 8 <= LDBL_MANT_DIG);
|
150
|
+
// assert that long double can hold T_FLOAT
|
151
|
+
// static_assert(sizeof(double) <= sizeof(long double));
|
152
|
+
switch (TYPE(score)) {
|
153
|
+
case T_FIXNUM:
|
154
|
+
return FIX2SCORE(score);
|
155
|
+
case T_BIGNUM:
|
156
|
+
return BIG2SCORE(score);
|
157
|
+
case T_RATIONAL:
|
158
|
+
return RAT2SCORE(score);
|
159
|
+
default:
|
160
|
+
return (long double)(NUM2DBL(rb_Float(score)));
|
67
161
|
}
|
68
|
-
debug(rb_sprintf("sifted (%"PRIsVALUE", %d, %ld)", heap_array, d, sift_index));
|
69
|
-
return LONG2NUM(sift_index);
|
70
162
|
}
|
71
163
|
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
if (last_index < sibling_index) break;
|
90
|
-
|
91
|
-
VALUE sibling_score = DHEAP_SCORE(heap_array, sibling_index);
|
92
|
-
|
93
|
-
if (CMP_LT(sibling_score, child_score)) {
|
94
|
-
child_score = sibling_score;
|
95
|
-
child_index = sibling_index;
|
96
|
-
}
|
97
|
-
}
|
164
|
+
/********************************************************************
|
165
|
+
*
|
166
|
+
* DHeap ENTRY accessors
|
167
|
+
*
|
168
|
+
********************************************************************/
|
169
|
+
|
170
|
+
#define DHEAP_SCORE(heap, idx) ((heap)->entries[idx].score)
|
171
|
+
#define DHEAP_VALUE(heap, idx) ((heap)->entries[idx].value)
|
172
|
+
|
173
|
+
static inline VALUE
|
174
|
+
DHEAP_ENTRY_ARY(dheap_t *heap, long idx)
|
175
|
+
{
|
176
|
+
if (idx < 0 || heap->size <= idx) { return Qnil; }
|
177
|
+
return rb_ary_new_from_args(2,
|
178
|
+
DHEAP_VALUE(heap, 0),
|
179
|
+
SCORE2NUM(DHEAP_SCORE(heap, 0)));
|
180
|
+
}
|
98
181
|
|
99
|
-
|
100
|
-
|
182
|
+
/********************************************************************
|
183
|
+
*
|
184
|
+
* DHeap index math
|
185
|
+
*
|
186
|
+
********************************************************************/
|
187
|
+
|
188
|
+
#define DHEAP_IDX_LAST(heap) ((heap)->size - 1)
|
189
|
+
#define DHEAP_IDX_PARENT(heap, idx) (((idx) - 1) / (heap)->d)
|
190
|
+
#define DHEAP_IDX_CHILD_0(heap, idx) (((idx) * (heap)->d) + 1)
|
191
|
+
#define DHEAP_IDX_CHILD_D(heap, idx) (((idx) * (heap)->d) + (heap)->d)
|
192
|
+
|
193
|
+
#ifdef __D_HEAP_DEBUG
|
194
|
+
#define ASSERT_DHEAP_IDX_OK(heap, index) do { \
|
195
|
+
if (index < 0) { \
|
196
|
+
rb_raise(rb_eIndexError, "DHeap index %ld too small", index); \
|
197
|
+
} \
|
198
|
+
else if (DHEAP_IDX_LAST(heap) < index) { \
|
199
|
+
rb_raise(rb_eIndexError, "DHeap index %ld too large", index); \
|
200
|
+
} \
|
201
|
+
} while (0)
|
202
|
+
#else
|
203
|
+
#define ASSERT_DHEAP_IDX_OK(heap, index)
|
204
|
+
#endif
|
205
|
+
|
206
|
+
/********************************************************************
|
207
|
+
*
|
208
|
+
* rb_data_type_t definitions
|
209
|
+
*
|
210
|
+
********************************************************************/
|
101
211
|
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
212
|
+
#ifdef HAVE_RB_GC_MARK_MOVABLE
|
213
|
+
static void
|
214
|
+
dheap_compact(void *ptr)
|
215
|
+
{
|
216
|
+
dheap_t *heap = ptr;
|
217
|
+
for (long i = 0; i < heap->size; ++i) {
|
218
|
+
if (DHEAP_VALUE(heap, i))
|
219
|
+
rb_gc_location(DHEAP_VALUE(heap, i));
|
106
220
|
}
|
107
|
-
debug(rb_sprintf("sifted (%"PRIsVALUE", %d, %ld)", heap_array, d, sift_index));
|
108
|
-
return LONG2NUM(sift_index);
|
109
221
|
}
|
222
|
+
#else
|
223
|
+
#define rb_gc_mark_movable(x) rb_gc_mark(x)
|
224
|
+
#endif
|
110
225
|
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
|
226
|
+
static void
|
227
|
+
dheap_mark(void *ptr)
|
228
|
+
{
|
229
|
+
dheap_t *heap = ptr;
|
230
|
+
for (long i = 0; i < heap->size; ++i) {
|
231
|
+
if (DHEAP_VALUE(heap, i))
|
232
|
+
rb_gc_mark_movable(DHEAP_VALUE(heap,i));
|
233
|
+
}
|
234
|
+
}
|
115
235
|
|
116
|
-
|
117
|
-
*
|
118
|
-
|
236
|
+
static void
|
237
|
+
dheap_free(void *ptr)
|
238
|
+
{
|
239
|
+
dheap_t *heap = ptr;
|
240
|
+
heap->size = 0;
|
241
|
+
if (heap->entries) {
|
242
|
+
xfree(heap->entries);
|
243
|
+
heap->entries = NULL;
|
244
|
+
}
|
245
|
+
heap->capa = 0;
|
246
|
+
xfree(ptr);
|
247
|
+
}
|
248
|
+
|
249
|
+
static size_t
|
250
|
+
dheap_memsize(const void *ptr)
|
251
|
+
{
|
252
|
+
const dheap_t *heap = ptr;
|
253
|
+
size_t size = 0;
|
254
|
+
size += sizeof(*heap);
|
255
|
+
size += sizeof(ENTRY) * heap->capa;
|
256
|
+
return size;
|
257
|
+
}
|
258
|
+
|
259
|
+
|
260
|
+
static const rb_data_type_t dheap_data_type = {
|
261
|
+
"DHeap",
|
262
|
+
{
|
263
|
+
(void (*)(void*))dheap_mark,
|
264
|
+
(void (*)(void*))dheap_free,
|
265
|
+
(size_t (*)(const void *))dheap_memsize,
|
266
|
+
#ifdef HAVE_RB_GC_MARK_MOVABLE
|
267
|
+
(void (*)(void*))dheap_compact, {0}
|
268
|
+
#else
|
269
|
+
{0}
|
270
|
+
#endif
|
271
|
+
},
|
272
|
+
0, 0,
|
273
|
+
RUBY_TYPED_FREE_IMMEDIATELY,
|
274
|
+
};
|
275
|
+
|
276
|
+
/********************************************************************
|
119
277
|
*
|
120
|
-
*
|
121
|
-
*
|
122
|
-
* values in every odd numbered index. The array is flat, not an array of
|
123
|
-
* length=2 arrays.
|
278
|
+
* DHeap comparisons
|
279
|
+
* TODO: bring back comparisons for score types other than `long double`.
|
124
280
|
*
|
125
|
-
|
281
|
+
********************************************************************/
|
282
|
+
|
283
|
+
#define CMP_LT(a, b) ((a) < (b))
|
284
|
+
#define CMP_LTE(a, b) ((a) <= (b))
|
285
|
+
|
286
|
+
/* #ifdef ORIG_SCORE_CMP_CODE */
|
287
|
+
|
288
|
+
/* #define CMP_LT(a, b) (optimized_cmp(a, b) < 0) */
|
289
|
+
/* #define CMP_LTE(a, b) (optimized_cmp(a, b) <= 0) */
|
290
|
+
|
291
|
+
/* // from internal/compar.h */
|
292
|
+
/* #define STRING_P(s) (RB_TYPE_P((s), T_STRING) && CLASS_OF(s) == rb_cString) */
|
293
|
+
/*
|
294
|
+
* short-circuit evaluation for a few basic types.
|
126
295
|
*
|
127
|
-
*
|
128
|
-
*
|
129
|
-
* @param sift_index [Integer] the index to start sifting from
|
130
|
-
* @return [Integer] the new index for the object that starts at +sift_index+.
|
296
|
+
* Only Integer, Float, and String are optimized,
|
297
|
+
* and only when both arguments are the same type.
|
131
298
|
*/
|
299
|
+
/* static inline int */
|
300
|
+
/* optimized_cmp(SCORE a, SCORE b) { */
|
301
|
+
/* if (a == b) // Fixnum equality and object equality */
|
302
|
+
/* return 0; */
|
303
|
+
/* if (FIXNUM_P(a) && FIXNUM_P(b)) */
|
304
|
+
/* return (FIX2LONG(a) < FIX2LONG(b)) ? -1 : 1; */
|
305
|
+
/* if (RB_FLOAT_TYPE_P(a) && RB_FLOAT_TYPE_P(b)) */
|
306
|
+
/* { */
|
307
|
+
/* double x, y; */
|
308
|
+
/* x = RFLOAT_VALUE(a); */
|
309
|
+
/* y = RFLOAT_VALUE(b); */
|
310
|
+
/* if (isnan(x) || isnan(y)) rb_cmperr(a, b); // raise ArgumentError */
|
311
|
+
/* return (x < y) ? -1 : ((x == y) ? 0 : 1); */
|
312
|
+
/* } */
|
313
|
+
/* if (RB_TYPE_P(a, T_BIGNUM) && RB_TYPE_P(b, T_BIGNUM)) */
|
314
|
+
/* return FIX2INT(rb_big_cmp(a, b)); */
|
315
|
+
/* if (STRING_P(a) && STRING_P(b)) */
|
316
|
+
/* return rb_str_cmp(a, b); */
|
317
|
+
|
318
|
+
/* // give up on an optimized version and just call (a <=> b) */
|
319
|
+
/* return rb_cmpint(rb_funcallv(a, id_cmp, 1, &b), a, b); */
|
320
|
+
/* } */
|
321
|
+
|
322
|
+
/* #endif */
|
323
|
+
|
324
|
+
/********************************************************************
|
325
|
+
*
|
326
|
+
* DHeap allocation and initialization and resizing
|
327
|
+
*
|
328
|
+
********************************************************************/
|
329
|
+
|
132
330
|
static VALUE
|
133
|
-
|
134
|
-
|
135
|
-
|
331
|
+
dheap_s_alloc(VALUE klass)
|
332
|
+
{
|
333
|
+
VALUE obj;
|
334
|
+
dheap_t *heap;
|
335
|
+
|
336
|
+
// TypedData_Make_Struct uses a non-std "statement expression"
|
337
|
+
#pragma GCC diagnostic push
|
338
|
+
#pragma GCC diagnostic ignored "-Wpedantic"
|
339
|
+
obj = TypedData_Make_Struct(klass, dheap_t, &dheap_data_type, heap);
|
340
|
+
#pragma GCC diagnostic pop
|
341
|
+
heap->d = DHEAP_DEFAULT_D;
|
342
|
+
|
343
|
+
heap->size = 0;
|
344
|
+
heap->capa = 0;
|
345
|
+
heap->entries = NULL;
|
346
|
+
|
347
|
+
return obj;
|
136
348
|
}
|
137
349
|
|
138
|
-
|
139
|
-
|
140
|
-
|
141
|
-
*
|
142
|
-
|
143
|
-
|
144
|
-
|
145
|
-
|
146
|
-
|
147
|
-
*
|
350
|
+
static inline dheap_t *
|
351
|
+
get_dheap_struct(VALUE self)
|
352
|
+
{
|
353
|
+
dheap_t *heap;
|
354
|
+
TypedData_Get_Struct(self, dheap_t, &dheap_data_type, heap);
|
355
|
+
return heap;
|
356
|
+
}
|
357
|
+
|
358
|
+
void
|
359
|
+
dheap_set_capa(dheap_t *heap, long new_capa)
|
360
|
+
{
|
361
|
+
// Do nothing if we already have the capacity or are resizing too small
|
362
|
+
if (new_capa <= heap->capa || new_capa <= heap->size) return;
|
363
|
+
|
364
|
+
// allocate
|
365
|
+
if (heap->entries) {
|
366
|
+
RB_REALLOC_N(heap->entries, ENTRY, new_capa);
|
367
|
+
} else {
|
368
|
+
heap->entries = RB_ZALLOC_N(ENTRY, new_capa);
|
369
|
+
}
|
370
|
+
heap->capa = new_capa;
|
371
|
+
}
|
372
|
+
|
373
|
+
static void
|
374
|
+
dheap_ensure_room_for_push(dheap_t *heap, long incr_by)
|
375
|
+
{
|
376
|
+
long new_size = heap->size + incr_by;
|
377
|
+
|
378
|
+
// check for overflow of new_size
|
379
|
+
if (DHEAP_MAX_CAPA - incr_by < heap->size)
|
380
|
+
rb_raise(rb_eIndexError, "index %ld too big", new_size);
|
381
|
+
|
382
|
+
// if it existing capacity is too small
|
383
|
+
if (heap->capa < new_size) {
|
384
|
+
// double it...
|
385
|
+
long new_capa = new_size * 2;
|
386
|
+
if (DHEAP_CAPA_INCR_MAX < new_size)
|
387
|
+
new_size = new_size + DHEAP_CAPA_INCR_MAX;
|
388
|
+
// check for overflow of new_capa
|
389
|
+
if (DHEAP_MAX_CAPA / 2 < new_size) new_capa = DHEAP_MAX_CAPA;
|
390
|
+
// cap max incr_by
|
391
|
+
if (heap->capa + DHEAP_CAPA_INCR_MAX < new_capa)
|
392
|
+
new_capa = heap->capa + DHEAP_CAPA_INCR_MAX;
|
393
|
+
|
394
|
+
dheap_set_capa(heap, new_capa);
|
395
|
+
}
|
396
|
+
}
|
397
|
+
|
398
|
+
static inline int
|
399
|
+
dheap_value_to_int_d(VALUE num)
|
400
|
+
{
|
401
|
+
int d = NUM2INT(num);
|
402
|
+
if (d < 2) {
|
403
|
+
rb_raise(rb_eArgError, "DHeap d=%u is too small", d);
|
404
|
+
}
|
405
|
+
if (d > DHEAP_MAX_D) {
|
406
|
+
rb_raise(rb_eArgError, "DHeap d=%u is too large", d);
|
407
|
+
}
|
408
|
+
return d;
|
409
|
+
}
|
410
|
+
|
411
|
+
static inline long
|
412
|
+
dheap_value_to_long_capa(VALUE num)
|
413
|
+
{
|
414
|
+
long capa = NUM2LONG(num);
|
415
|
+
if (capa < 1) {
|
416
|
+
rb_raise(rb_eArgError, "DHeap capa=%lu must be positive", capa);
|
417
|
+
}
|
418
|
+
return capa;
|
419
|
+
}
|
420
|
+
|
421
|
+
static VALUE
|
422
|
+
dheap_initialize(VALUE self, VALUE d, VALUE capa) {
|
423
|
+
dheap_t *heap = get_dheap_struct(self);
|
424
|
+
|
425
|
+
if(heap->entries || heap->size || heap->capa)
|
426
|
+
rb_raise(rb_eScriptError, "DHeap already initialized.");
|
427
|
+
|
428
|
+
heap->d = dheap_value_to_int_d(d);
|
429
|
+
dheap_set_capa(heap, dheap_value_to_long_capa(capa));
|
430
|
+
|
431
|
+
return self;
|
432
|
+
}
|
433
|
+
|
434
|
+
/* @!visibility private */
|
435
|
+
static VALUE
|
436
|
+
dheap_initialize_copy(VALUE copy, VALUE orig)
|
437
|
+
{
|
438
|
+
dheap_t *heap_copy;
|
439
|
+
dheap_t *heap_orig = get_dheap_struct(orig);
|
440
|
+
|
441
|
+
rb_check_frozen(copy);
|
442
|
+
TypedData_Get_Struct(copy, dheap_t, &dheap_data_type, heap_copy);
|
443
|
+
|
444
|
+
heap_copy->d = heap_orig->d;
|
445
|
+
|
446
|
+
dheap_set_capa(heap_copy, heap_orig->capa);
|
447
|
+
heap_copy->size = heap_orig->size;
|
448
|
+
if (heap_copy->size)
|
449
|
+
MEMCPY(heap_copy->entries, heap_orig->entries, ENTRY, heap_orig->size);
|
450
|
+
|
451
|
+
return copy;
|
452
|
+
}
|
453
|
+
|
454
|
+
/********************************************************************
|
148
455
|
*
|
149
|
-
*
|
456
|
+
* DHeap sift up/down
|
150
457
|
*
|
151
|
-
|
152
|
-
|
153
|
-
|
154
|
-
*
|
155
|
-
|
156
|
-
|
157
|
-
|
158
|
-
|
159
|
-
|
458
|
+
********************************************************************/
|
459
|
+
|
460
|
+
VALUE
|
461
|
+
dheap_sift_up(dheap_t *heap, long index) {
|
462
|
+
ENTRY entry = heap->entries[index];
|
463
|
+
|
464
|
+
ASSERT_DHEAP_IDX_OK(heap, index);
|
465
|
+
|
466
|
+
// sift it up to where it belongs
|
467
|
+
for (long parent_index; 0 < index; index = parent_index) {
|
468
|
+
/* debug(rb_sprintf("sift up(%"PRIsVALUE", %d, %ld)", heap->values, heap->d, index)); */
|
469
|
+
parent_index = DHEAP_IDX_PARENT(heap, index);
|
470
|
+
|
471
|
+
// parent is smaller: heap is restored
|
472
|
+
if (CMP_LTE(DHEAP_SCORE(heap, parent_index), entry.score)) break;
|
473
|
+
|
474
|
+
// parent is larger: swap and continue sifting up
|
475
|
+
heap->entries[index] = heap->entries[parent_index];
|
476
|
+
}
|
477
|
+
heap->entries[index] = entry;
|
478
|
+
/* debug(rb_sprintf("sifted (%"PRIsVALUE", %d, %ld)", heap->values, heap->d, index)); */
|
479
|
+
return LONG2NUM(index);
|
160
480
|
}
|
161
481
|
|
162
482
|
/*
|
163
|
-
*
|
164
|
-
* Initialize a _d_-ary min-heap.
|
165
|
-
*
|
166
|
-
* @param d [Integer] maximum number of children per parent
|
483
|
+
* this is a tiny bit more complicated than the binary heap version
|
167
484
|
*/
|
168
|
-
static
|
169
|
-
|
170
|
-
|
171
|
-
|
172
|
-
if (
|
173
|
-
|
485
|
+
static inline long
|
486
|
+
dheap_find_min_child(dheap_t *heap, long parent, long last_index) {
|
487
|
+
long min_child = DHEAP_IDX_CHILD_0(heap, parent);
|
488
|
+
long last_sib = DHEAP_IDX_CHILD_D(heap, parent);
|
489
|
+
if (last_index < last_sib) last_sib = last_index;
|
490
|
+
|
491
|
+
for (long sibidx = min_child + 1; sibidx <= last_sib; ++sibidx) {
|
492
|
+
if (CMP_LT(DHEAP_SCORE(heap, sibidx),
|
493
|
+
DHEAP_SCORE(heap, min_child))) {
|
494
|
+
min_child = sibidx;
|
495
|
+
}
|
174
496
|
}
|
175
|
-
|
176
|
-
rb_ivar_set(self, id_ivar_d, INT2FIX(d));
|
177
|
-
rb_ivar_set(self, id_ivar_a, rb_ary_new());
|
178
|
-
return self;
|
497
|
+
return min_child;
|
179
498
|
}
|
180
499
|
|
500
|
+
void
|
501
|
+
dheap_sift_down(dheap_t *heap, long index, long last_index) {
|
502
|
+
if (last_index < 1 || DHEAP_IDX_PARENT(heap, last_index) < index) {
|
503
|
+
// short-circuit: no chance for a child
|
504
|
+
return;
|
505
|
+
|
506
|
+
} else {
|
507
|
+
ENTRY entry = heap->entries[index];
|
508
|
+
|
509
|
+
long last_parent = DHEAP_IDX_PARENT(heap, last_index);
|
510
|
+
|
511
|
+
ASSERT_DHEAP_IDX_OK(heap, index);
|
512
|
+
|
513
|
+
// iteratively sift it down to where it belongs
|
514
|
+
while (index <= last_parent) {
|
515
|
+
// find min child
|
516
|
+
long min_child = dheap_find_min_child(heap, index, last_index);
|
517
|
+
|
518
|
+
// child is larger: heap is restored
|
519
|
+
if (CMP_LTE(entry.score, DHEAP_SCORE(heap, min_child))) break;
|
520
|
+
|
521
|
+
// child is smaller: swap and continue sifting down
|
522
|
+
heap->entries[index] = heap->entries[min_child];
|
523
|
+
index = min_child;
|
524
|
+
}
|
525
|
+
heap->entries[index] = entry;
|
526
|
+
// debug(rb_sprintf("sifted (%"PRIsVALUE", %d, %ld)", heap->values, heap->d, index));
|
527
|
+
}
|
528
|
+
}
|
529
|
+
|
530
|
+
/********************************************************************
|
531
|
+
*
|
532
|
+
* DHeap attributes
|
533
|
+
*
|
534
|
+
********************************************************************/
|
535
|
+
|
181
536
|
/*
|
182
537
|
* @return [Integer] the number of elements in the heap
|
183
538
|
*/
|
184
|
-
static VALUE
|
185
|
-
|
186
|
-
|
187
|
-
|
539
|
+
static VALUE
|
540
|
+
dheap_size(VALUE self)
|
541
|
+
{
|
542
|
+
dheap_t *heap = get_dheap_struct(self);
|
543
|
+
return LONG2NUM(heap->size);
|
188
544
|
}
|
189
545
|
|
190
546
|
/*
|
191
|
-
* @return [Boolean]
|
547
|
+
* @return [Boolean] if the heap is empty
|
192
548
|
*/
|
193
|
-
static VALUE
|
194
|
-
|
195
|
-
|
196
|
-
|
549
|
+
static VALUE
|
550
|
+
dheap_empty_p(VALUE self)
|
551
|
+
{
|
552
|
+
dheap_t *heap = get_dheap_struct(self);
|
553
|
+
return heap->size ? Qfalse : Qtrue;
|
197
554
|
}
|
198
555
|
|
199
556
|
/*
|
200
557
|
* @return [Integer] the maximum number of children per parent
|
201
558
|
*/
|
202
|
-
static VALUE
|
559
|
+
static VALUE
|
560
|
+
dheap_attr_d(VALUE self)
|
561
|
+
{
|
562
|
+
dheap_t *heap = get_dheap_struct(self);
|
563
|
+
return INT2FIX(heap->d);
|
564
|
+
}
|
565
|
+
|
566
|
+
/********************************************************************
|
567
|
+
*
|
568
|
+
* DHeap push
|
569
|
+
*
|
570
|
+
********************************************************************/
|
571
|
+
|
572
|
+
static inline void
|
573
|
+
dheap_push_entry(dheap_t *heap, ENTRY *entry) {
|
574
|
+
dheap_ensure_room_for_push(heap, 1);
|
575
|
+
heap->entries[heap->size] = *entry;
|
576
|
+
++heap->size;
|
577
|
+
dheap_sift_up(heap, heap->size - 1);
|
578
|
+
}
|
203
579
|
|
204
580
|
/*
|
205
|
-
*
|
206
|
-
*
|
581
|
+
* Inserts a value into the heap, using a score to determine sort-order.
|
582
|
+
*
|
583
|
+
* Score comes first, as an analogy with the +Array#insert+ index.
|
584
|
+
*
|
585
|
+
* Time complexity: <b>O(log n / log d)</b> <i>(worst-case)</i>
|
586
|
+
*
|
587
|
+
* @param score [Integer,Float,#to_f] a score to compare against other scores.
|
588
|
+
* @param value [Object] an object that is associated with the score.
|
207
589
|
*
|
208
590
|
* @return [self]
|
209
591
|
*/
|
210
592
|
static VALUE
|
211
|
-
|
212
|
-
|
213
|
-
|
214
|
-
|
215
|
-
return rb_call_super(0, NULL);
|
216
|
-
}
|
593
|
+
dheap_insert(VALUE self, VALUE score, VALUE value) {
|
594
|
+
ENTRY entry;
|
595
|
+
dheap_t *heap = get_dheap_struct(self);
|
596
|
+
rb_check_frozen(self);
|
217
597
|
|
218
|
-
|
219
|
-
|
220
|
-
|
221
|
-
|
222
|
-
|
223
|
-
return dheap_ary_sift_up(ary, d, last_index);
|
598
|
+
entry.score = VAL2SCORE(score);
|
599
|
+
entry.value = value;
|
600
|
+
dheap_push_entry(heap, &entry);
|
601
|
+
|
602
|
+
return self;
|
224
603
|
}
|
225
604
|
|
226
605
|
/*
|
227
|
-
* @overload push(
|
606
|
+
* @overload push(value, score = value)
|
228
607
|
*
|
229
608
|
* Push a value onto heap, using a score to determine sort-order.
|
230
609
|
*
|
231
|
-
*
|
232
|
-
*
|
610
|
+
* Value comes first because the separate score is optional, and because it feels
|
611
|
+
* like a more natural variation on +Array#push+ or +Queue#enq+. If a score
|
612
|
+
* isn't provided, the value must be an Integer or can be cast with
|
613
|
+
* +Float(value)+.
|
233
614
|
*
|
234
615
|
* Time complexity: <b>O(log n / log d)</b> <i>(worst-case)</i>
|
235
616
|
*
|
236
|
-
* @param score [#<=>] a value that can be compared to other scores.
|
237
617
|
* @param value [Object] an object that is associated with the score.
|
618
|
+
* @param score [Integer,Float,#to_f] a score to compare against other scores.
|
238
619
|
*
|
239
|
-
* @return [
|
620
|
+
* @return [self]
|
240
621
|
*/
|
241
622
|
static VALUE
|
242
623
|
dheap_push(int argc, VALUE *argv, VALUE self) {
|
243
|
-
|
244
|
-
|
245
|
-
|
624
|
+
dheap_t *heap = get_dheap_struct(self);
|
625
|
+
ENTRY entry;
|
626
|
+
rb_check_frozen(self);
|
246
627
|
|
247
|
-
|
248
|
-
|
249
|
-
|
628
|
+
rb_check_arity(argc, 1, 2);
|
629
|
+
entry.value = argv[0];
|
630
|
+
entry.score = VAL2SCORE(argc < 2 ? entry.value : argv[1]);
|
631
|
+
dheap_push_entry(heap, &entry);
|
250
632
|
|
251
|
-
return
|
633
|
+
return self;
|
252
634
|
}
|
253
635
|
|
254
636
|
/*
|
255
|
-
* Pushes a
|
637
|
+
* Pushes a value onto the heap.
|
256
638
|
*
|
257
|
-
* The
|
639
|
+
* The score will be derived from the value, by using the value itself if it is
|
640
|
+
* an Integer, otherwise by casting it with +Float(value)+.
|
258
641
|
*
|
259
642
|
* Time complexity: <b>O(log n / log d)</b> <i>(worst-case)</i>
|
260
643
|
*
|
261
|
-
* @param value [
|
644
|
+
* @param value [Integer,#to_f] a value with an intrinsic numeric score
|
262
645
|
* @return [self]
|
263
646
|
*/
|
264
647
|
static VALUE
|
265
648
|
dheap_left_shift(VALUE self, VALUE value) {
|
266
|
-
|
649
|
+
dheap_t *heap = get_dheap_struct(self);
|
650
|
+
ENTRY entry;
|
651
|
+
rb_check_frozen(self);
|
652
|
+
|
653
|
+
entry.score = VAL2SCORE(value);
|
654
|
+
entry.value = value;
|
655
|
+
dheap_push_entry(heap, &entry);
|
656
|
+
|
267
657
|
return self;
|
268
658
|
}
|
269
659
|
|
270
|
-
|
271
|
-
|
272
|
-
|
273
|
-
|
660
|
+
/********************************************************************
|
661
|
+
*
|
662
|
+
* DHeap pop and peek
|
663
|
+
*
|
664
|
+
********************************************************************/
|
665
|
+
|
666
|
+
static inline void
|
667
|
+
dheap_del0(dheap_t *heap)
|
668
|
+
{
|
669
|
+
if (0 < --heap->size) {
|
670
|
+
heap->entries[0] = heap->entries[heap->size];
|
671
|
+
heap->entries[heap->size] = EmptyDheapEntry; // unnecessary to zero?
|
672
|
+
dheap_sift_down(heap, 0, heap->size - 1);
|
673
|
+
}
|
674
|
+
}
|
675
|
+
|
676
|
+
static inline VALUE
|
677
|
+
dheap_pop0(dheap_t *heap)
|
678
|
+
{
|
679
|
+
VALUE popped = DHEAP_VALUE(heap, 0);
|
680
|
+
dheap_del0(heap);
|
681
|
+
return popped;
|
682
|
+
}
|
683
|
+
|
684
|
+
/*
|
685
|
+
* Returns the next value on the heap, and its score, without popping it
|
686
|
+
*
|
687
|
+
* Time complexity: <b>O(1)</b> <i>(worst-case)</i>
|
688
|
+
* @return [nil,Array<(Object, Numeric)>] the next value and its score
|
689
|
+
*
|
690
|
+
* @see #peek
|
691
|
+
* @see #peek_score
|
692
|
+
*/
|
693
|
+
static VALUE
|
694
|
+
dheap_peek_with_score(VALUE self)
|
695
|
+
{
|
696
|
+
dheap_t *heap = get_dheap_struct(self);
|
697
|
+
return DHEAP_ENTRY_ARY(heap, 0);
|
698
|
+
}
|
274
699
|
|
275
|
-
|
276
|
-
|
277
|
-
|
278
|
-
|
279
|
-
|
280
|
-
|
281
|
-
|
700
|
+
/*
|
701
|
+
* Returns the next score on the heap, without the value and without popping it.
|
702
|
+
*
|
703
|
+
* Time complexity: <b>O(1)</b> <i>(worst-case)</i>
|
704
|
+
* @return [nil, Numeric] the next score, if there is one
|
705
|
+
*
|
706
|
+
* @see #peek
|
707
|
+
* @see #peek_with_score
|
708
|
+
*/
|
709
|
+
static VALUE
|
710
|
+
dheap_peek_score(VALUE self)
|
711
|
+
{
|
712
|
+
dheap_t *heap = get_dheap_struct(self);
|
713
|
+
if (DHEAP_IDX_LAST(heap) < 0) return Qnil;
|
714
|
+
return SCORE2NUM(DHEAP_SCORE(heap, 0));
|
715
|
+
}
|
282
716
|
|
283
717
|
/*
|
284
718
|
* Returns the next value on the heap to be popped without popping it.
|
285
719
|
*
|
286
720
|
* Time complexity: <b>O(1)</b> <i>(worst-case)</i>
|
287
|
-
* @return [Object] the next value to be popped without popping it.
|
721
|
+
* @return [nil, Object] the next value to be popped without popping it.
|
722
|
+
*
|
723
|
+
* @see #peek_score
|
724
|
+
* @see #peek_with_score
|
288
725
|
*/
|
289
726
|
static VALUE
|
290
|
-
dheap_peek(VALUE self)
|
291
|
-
|
292
|
-
|
727
|
+
dheap_peek(VALUE self)
|
728
|
+
{
|
729
|
+
dheap_t *heap = get_dheap_struct(self);
|
730
|
+
return heap->size ? DHEAP_VALUE(heap, 0) : Qnil;
|
293
731
|
}
|
294
732
|
|
295
733
|
/*
|
296
734
|
* Pops the minimum value from the top of the heap
|
297
735
|
*
|
298
736
|
* Time complexity: <b>O(d log n / log d)</b> <i>(worst-case)</i>
|
737
|
+
*
|
738
|
+
* @return [Object] the value with the minimum score
|
739
|
+
*
|
740
|
+
* @see #peek
|
741
|
+
* @see #pop_lt
|
742
|
+
* @see #pop_lte
|
743
|
+
* @see #pop_with_score
|
299
744
|
*/
|
300
745
|
static VALUE
|
301
|
-
dheap_pop(VALUE self)
|
302
|
-
|
303
|
-
|
304
|
-
|
746
|
+
dheap_pop(VALUE self)
|
747
|
+
{
|
748
|
+
dheap_t *heap = get_dheap_struct(self);
|
749
|
+
rb_check_frozen(self);
|
750
|
+
return heap->size ? dheap_pop0(heap) : Qnil;
|
751
|
+
}
|
305
752
|
|
306
|
-
|
307
|
-
|
753
|
+
/*
|
754
|
+
* Pops the minimum value from the top of the heap, along with its score.
|
755
|
+
*
|
756
|
+
* Time complexity: <b>O(d log n / log d)</b> <i>(worst-case)</i>
|
757
|
+
*
|
758
|
+
* @return [nil,Array<(Object, Numeric)>] the next value and its score
|
759
|
+
*
|
760
|
+
* @see #pop
|
761
|
+
* @see #peek_with_score
|
762
|
+
*/
|
763
|
+
static VALUE
|
764
|
+
dheap_pop_with_score(VALUE self)
|
765
|
+
{
|
766
|
+
dheap_t *heap = get_dheap_struct(self);
|
767
|
+
VALUE ary = DHEAP_ENTRY_ARY(heap, 0);
|
768
|
+
rb_check_frozen(self);
|
769
|
+
if (ary != Qnil) { dheap_pop0(heap); }
|
770
|
+
return ary;
|
308
771
|
}
|
309
772
|
|
310
773
|
/*
|
311
774
|
* Pops the minimum value only if it is less than or equal to a max score.
|
312
775
|
*
|
313
|
-
* @param max_score [
|
776
|
+
* @param max_score [Integer,#to_f] the maximum score to be popped
|
777
|
+
*
|
778
|
+
* Time complexity: <b>O(d log n / log d)</b> <i>(worst-case)</i>
|
779
|
+
*
|
780
|
+
* @return [Object] the value with the minimum score
|
314
781
|
*
|
782
|
+
* @see #peek
|
315
783
|
* @see #pop
|
784
|
+
* @see #pop_lt
|
785
|
+
* @see #pop_all_below
|
316
786
|
*/
|
317
787
|
static VALUE
|
318
|
-
dheap_pop_lte(VALUE self, VALUE max_score)
|
319
|
-
|
320
|
-
|
321
|
-
|
322
|
-
|
323
|
-
|
324
|
-
|
325
|
-
|
326
|
-
DHEAP_Pop_SwapLastAndSiftDown(ary, dval, last_index, sift_value);
|
327
|
-
return pop_value;
|
788
|
+
dheap_pop_lte(VALUE self, VALUE max_score)
|
789
|
+
{
|
790
|
+
dheap_t *heap = get_dheap_struct(self);
|
791
|
+
rb_check_frozen(self);
|
792
|
+
if (heap->size <= 0) return Qnil;
|
793
|
+
if (!CMP_LTE(DHEAP_SCORE(heap, 0), VAL2SCORE(max_score))) return Qnil;
|
794
|
+
return dheap_pop0(heap);
|
328
795
|
}
|
329
796
|
|
330
797
|
/*
|
331
798
|
* Pops the minimum value only if it is less than a max score.
|
332
799
|
*
|
333
|
-
* @param max_score [
|
800
|
+
* @param max_score [Integer,#to_f] the maximum score to be popped
|
334
801
|
*
|
335
802
|
* Time complexity: <b>O(d log n / log d)</b> <i>(worst-case)</i>
|
803
|
+
*
|
804
|
+
* @return [Object] the value with the minimum score
|
805
|
+
*
|
806
|
+
* @see #peek
|
807
|
+
* @see #pop
|
808
|
+
* @see #pop_lte
|
809
|
+
* @see #pop_all_below
|
336
810
|
*/
|
337
811
|
static VALUE
|
338
|
-
dheap_pop_lt(VALUE self, VALUE max_score)
|
339
|
-
|
340
|
-
|
341
|
-
|
812
|
+
dheap_pop_lt(VALUE self, VALUE max_score)
|
813
|
+
{
|
814
|
+
dheap_t *heap = get_dheap_struct(self);
|
815
|
+
rb_check_frozen(self);
|
816
|
+
if (heap->size <= 0) return Qnil;
|
817
|
+
if (!CMP_LT(DHEAP_SCORE(heap, 0), VAL2SCORE(max_score))) return Qnil;
|
818
|
+
return dheap_pop0(heap);
|
819
|
+
}
|
342
820
|
|
343
|
-
|
344
|
-
|
821
|
+
#define DHEAP_PEEK_LT_P(heap, max_score) \
|
822
|
+
(heap->size && CMP_LT(DHEAP_SCORE(heap, 0), max_score))
|
345
823
|
|
346
|
-
|
347
|
-
|
824
|
+
static VALUE
|
825
|
+
dheap_pop_all_below0(dheap_t *heap, SCORE max_score, VALUE array)
|
826
|
+
{
|
827
|
+
if (!RTEST(array)) { array = rb_ary_new(); }
|
828
|
+
if (RB_TYPE_P(array, T_ARRAY)) {
|
829
|
+
while (DHEAP_PEEK_LT_P(heap, max_score)) {
|
830
|
+
rb_ary_push(array, dheap_pop0(heap));
|
831
|
+
}
|
832
|
+
} else {
|
833
|
+
while (DHEAP_PEEK_LT_P(heap, max_score)) {
|
834
|
+
rb_funcall(array, id_lshift, 1, dheap_pop0(heap));
|
835
|
+
}
|
836
|
+
}
|
837
|
+
return array;
|
348
838
|
}
|
349
839
|
|
350
|
-
|
351
|
-
|
840
|
+
/*
|
841
|
+
* @overload pop_all_below(max_score, receiver = [])
|
842
|
+
*
|
843
|
+
* Pops all value with score less than max score.
|
844
|
+
*
|
845
|
+
* Time complexity: <b>O(m * d log n / log d)</b>, <i>m = number popped</i>
|
846
|
+
*
|
847
|
+
* @param max_score [Integer,#to_f] the maximum score to be popped
|
848
|
+
* @param receiver [Array,#<<] object onto which the values will be pushed,
|
849
|
+
* in order by score.
|
850
|
+
*
|
851
|
+
* @return [Object] the object onto which the values were pushed
|
852
|
+
*
|
853
|
+
* @see #pop
|
854
|
+
* @see #pop_lt
|
855
|
+
*/
|
856
|
+
static VALUE
|
857
|
+
dheap_pop_all_below(int argc, VALUE *argv, VALUE self)
|
352
858
|
{
|
353
|
-
|
354
|
-
|
355
|
-
|
859
|
+
dheap_t *heap = get_dheap_struct(self);
|
860
|
+
SCORE max_score;
|
861
|
+
VALUE array;
|
862
|
+
rb_check_frozen(self);
|
863
|
+
rb_check_arity(argc, 1, 2);
|
864
|
+
max_score = VAL2SCORE(argv[0]);
|
865
|
+
if (argc == 1) {
|
866
|
+
array = rb_ary_new();
|
867
|
+
} else {
|
868
|
+
array = argv[1];
|
869
|
+
}
|
870
|
+
return dheap_pop_all_below0(heap, max_score, array);
|
871
|
+
}
|
356
872
|
|
357
|
-
|
358
|
-
|
359
|
-
|
873
|
+
/********************************************************************
|
874
|
+
*
|
875
|
+
* DHeap, misc methods
|
876
|
+
*
|
877
|
+
********************************************************************/
|
360
878
|
|
361
|
-
|
362
|
-
|
879
|
+
/*
|
880
|
+
* Clears all values from the heap, leaving it empty.
|
881
|
+
*
|
882
|
+
* @return [self]
|
883
|
+
*/
|
884
|
+
static VALUE
|
885
|
+
dheap_clear(VALUE self)
|
886
|
+
{
|
887
|
+
dheap_t *heap = get_dheap_struct(self);
|
888
|
+
rb_check_frozen(self);
|
889
|
+
if (0 < heap->size) {
|
890
|
+
heap->size = 0;
|
891
|
+
}
|
892
|
+
return self;
|
893
|
+
}
|
363
894
|
|
364
|
-
|
365
|
-
|
366
|
-
|
895
|
+
/********************************************************************
|
896
|
+
*
|
897
|
+
* DHeap setup
|
898
|
+
*
|
899
|
+
********************************************************************/
|
367
900
|
|
368
|
-
|
369
|
-
|
901
|
+
void
|
902
|
+
Init_d_heap(void)
|
903
|
+
{
|
904
|
+
VALUE rb_cDHeap = rb_define_class("DHeap", rb_cObject);
|
370
905
|
|
906
|
+
id_cmp = rb_intern_const("<=>");
|
907
|
+
id_abs = rb_intern_const("abs");
|
908
|
+
id_lshift = rb_intern_const("<<");
|
909
|
+
id_unary_minus = rb_intern_const("-@");
|
910
|
+
|
911
|
+
rb_define_alloc_func(rb_cDHeap, dheap_s_alloc);
|
912
|
+
|
913
|
+
/*
|
914
|
+
* This is based on INT_MAX. But it is very very unlikely you will want a
|
915
|
+
* large value for d. The tradeoff is that higher d values give faster push
|
916
|
+
* and slower pop. If you expect pushes and pops to be balanced, then just
|
917
|
+
* stick with the default. If you expect more pushes than pops, it might be
|
918
|
+
* worthwhile to increase d.
|
919
|
+
*/
|
920
|
+
rb_define_const(rb_cDHeap, "MAX_D", INT2NUM(DHEAP_MAX_D));
|
921
|
+
|
922
|
+
/*
|
923
|
+
* d=4 uses the fewest comparisons for (worst case) insert + delete-min.
|
924
|
+
*/
|
925
|
+
rb_define_const(rb_cDHeap, "DEFAULT_D", INT2NUM(DHEAP_DEFAULT_D));
|
926
|
+
|
927
|
+
/*
|
928
|
+
* The default heap capacity. The heap grows automatically as necessary, so
|
929
|
+
* you shouldn't need to worry about this.
|
930
|
+
*/
|
931
|
+
rb_define_const(rb_cDHeap, "DEFAULT_CAPA", INT2NUM(DHEAP_DEFAULT_CAPA));
|
932
|
+
|
933
|
+
rb_define_private_method(rb_cDHeap, "__init_without_kw__", dheap_initialize, 2);
|
934
|
+
rb_define_method(rb_cDHeap, "initialize_copy", dheap_initialize_copy, 1);
|
935
|
+
|
936
|
+
rb_define_method(rb_cDHeap, "d", dheap_attr_d, 0);
|
937
|
+
rb_define_method(rb_cDHeap, "size", dheap_size, 0);
|
938
|
+
rb_define_method(rb_cDHeap, "empty?", dheap_empty_p, 0);
|
371
939
|
rb_define_method(rb_cDHeap, "peek", dheap_peek, 0);
|
940
|
+
|
941
|
+
rb_define_method(rb_cDHeap, "clear", dheap_clear, 0);
|
942
|
+
rb_define_method(rb_cDHeap, "insert", dheap_insert, 2);
|
372
943
|
rb_define_method(rb_cDHeap, "push", dheap_push, -1);
|
373
944
|
rb_define_method(rb_cDHeap, "<<", dheap_left_shift, 1);
|
374
945
|
rb_define_method(rb_cDHeap, "pop", dheap_pop, 0);
|
375
|
-
|
376
|
-
rb_define_method(rb_cDHeap, "
|
946
|
+
|
947
|
+
rb_define_method(rb_cDHeap, "pop_lt", dheap_pop_lt, 1);
|
948
|
+
rb_define_method(rb_cDHeap, "pop_lte", dheap_pop_lte, 1);
|
949
|
+
rb_define_method(rb_cDHeap, "pop_all_below", dheap_pop_all_below, -1);
|
950
|
+
|
951
|
+
rb_define_method(rb_cDHeap, "peek_score", dheap_peek_score, 0);
|
952
|
+
rb_define_method(rb_cDHeap, "peek_with_score", dheap_peek_with_score, 0);
|
953
|
+
rb_define_method(rb_cDHeap, "pop_with_score", dheap_pop_with_score, 0);
|
954
|
+
|
377
955
|
}
|