d_heap 0.3.0 → 0.7.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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
|
}
|