d_heap 0.6.1 → 0.7.0

Sign up to get free protection for your applications and to get access to all the features.
data/docs/profile.txt CHANGED
@@ -1,29 +1,28 @@
1
- Profiling run at 2021-01-10 21:34:56 -0500
2
- ruby v2.7.2, DHeap v0.3.0
1
+ Profiling run at 2021-02-01 00:43:34 -0500
2
+ ruby v2.7.2, DHeap v0.6.1
3
3
 
4
4
  ~~~~~~ filling @dheap_bm_random_vals with 1.0M ~~~~~~
5
5
  ########################################################################
6
- # Profile w/ N=5 (i=1000000)
6
+ # Profile w/ N=10 (i=1000000)
7
7
  # (n.b. RubyProf & tracepoint can change relative performance.
8
8
  # A sampling profiler can provide more accurate relative metrics.
9
9
  ########################################################################
10
10
 
11
- Filling push and resort ---------------------------
12
- Profiling push and resort ---------------------------
11
+ Filling ruby binary heap ---------------------------
12
+ Profiling ruby binary heap ---------------------------
13
13
  Measure Mode: wall_time
14
- Thread ID: 1360
15
- Fiber ID: 1340
16
- Total: 2.639873
14
+ Thread ID: 1400
15
+ Fiber ID: 1380
16
+ Total: 5.194751
17
17
  Sort by: self_time
18
18
 
19
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
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
27
26
 
28
27
  * recursively called methods
29
28
 
@@ -43,22 +42,19 @@ The interpretation of method names is:
43
42
  * MyObject#test - An instance method "test" of the class "MyObject"
44
43
  * <Object:MyObject>#test - The <> characters indicate a method on a singleton class.
45
44
 
46
- Filling bsearch + insert ---------------------------
47
- Profiling bsearch + insert ---------------------------
45
+ Filling quaternary DHeap ---------------------------
46
+ Profiling quaternary DHeap ---------------------------
48
47
  Measure Mode: wall_time
49
- Thread ID: 1360
50
- Fiber ID: 1340
51
- Total: 2.634233
48
+ Thread ID: 1400
49
+ Fiber ID: 1380
50
+ Total: 1.103473
52
51
  Sort by: self_time
53
52
 
54
53
  %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
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
62
58
 
63
59
  * recursively called methods
64
60
 
@@ -78,23 +74,27 @@ The interpretation of method names is:
78
74
  * MyObject#test - An instance method "test" of the class "MyObject"
79
75
  * <Object:MyObject>#test - The <> characters indicate a method on a singleton class.
80
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
+
81
83
  Filling ruby binary heap ---------------------------
82
84
  Profiling ruby binary heap ---------------------------
83
85
  Measure Mode: wall_time
84
- Thread ID: 1360
85
- Fiber ID: 1340
86
- Total: 4.940103
86
+ Thread ID: 1400
87
+ Fiber ID: 1380
88
+ Total: 7.422039
87
89
  Sort by: self_time
88
90
 
89
91
  %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
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
98
 
99
99
  * recursively called methods
100
100
 
@@ -117,16 +117,16 @@ The interpretation of method names is:
117
117
  Filling quaternary DHeap ---------------------------
118
118
  Profiling quaternary DHeap ---------------------------
119
119
  Measure Mode: wall_time
120
- Thread ID: 1360
121
- Fiber ID: 1340
122
- Total: 1.231464
120
+ Thread ID: 1400
121
+ Fiber ID: 1380
122
+ Total: 1.163539
123
123
  Sort by: self_time
124
124
 
125
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
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
130
 
131
131
  * recursively called methods
132
132
 
@@ -147,23 +147,26 @@ The interpretation of method names is:
147
147
  * <Object:MyObject>#test - The <> characters indicate a method on a singleton class.
148
148
 
149
149
  ########################################################################
150
- # Profile w/ N=1365 (i=1000000)
150
+ # Profile w/ N=1000 (i=1000000)
151
151
  # (n.b. RubyProf & tracepoint can change relative performance.
152
152
  # A sampling profiler can provide more accurate relative metrics.
153
153
  ########################################################################
154
154
 
155
- Filling push and resort ---------------------------
156
- Profiling push and resort ---------------------------
155
+ Filling ruby binary heap ---------------------------
156
+ Profiling ruby binary heap ---------------------------
157
157
  Measure Mode: wall_time
158
- Thread ID: 1360
159
- Fiber ID: 1340
160
- Total: 41.950612
158
+ Thread ID: 1400
159
+ Fiber ID: 1380
160
+ Total: 9.693127
161
161
  Sort by: self_time
162
162
 
163
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
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
167
170
 
168
171
  * recursively called methods
169
172
 
@@ -183,22 +186,19 @@ The interpretation of method names is:
183
186
  * MyObject#test - An instance method "test" of the class "MyObject"
184
187
  * <Object:MyObject>#test - The <> characters indicate a method on a singleton class.
185
188
 
186
- Filling bsearch + insert ---------------------------
187
- Profiling bsearch + insert ---------------------------
189
+ Filling quaternary DHeap ---------------------------
190
+ Profiling quaternary DHeap ---------------------------
188
191
  Measure Mode: wall_time
189
- Thread ID: 1360
190
- Fiber ID: 1340
191
- Total: 3.559064
192
+ Thread ID: 1400
193
+ Fiber ID: 1380
194
+ Total: 1.125575
192
195
  Sort by: self_time
193
196
 
194
197
  %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
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
202
 
203
203
  * recursively called methods
204
204
 
@@ -218,23 +218,24 @@ The interpretation of method names is:
218
218
  * MyObject#test - An instance method "test" of the class "MyObject"
219
219
  * <Object:MyObject>#test - The <> characters indicate a method on a singleton class.
220
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
+
221
227
  Filling ruby binary heap ---------------------------
222
228
  Profiling ruby binary heap ---------------------------
223
229
  Measure Mode: wall_time
224
- Thread ID: 1360
225
- Fiber ID: 1340
226
- Total: 11.581886
230
+ Thread ID: 1400
231
+ Fiber ID: 1380
232
+ Total: 13.737007
227
233
  Sort by: self_time
228
234
 
229
235
  %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
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
238
239
 
239
240
  * recursively called methods
240
241
 
@@ -257,16 +258,16 @@ The interpretation of method names is:
257
258
  Filling quaternary DHeap ---------------------------
258
259
  Profiling quaternary DHeap ---------------------------
259
260
  Measure Mode: wall_time
260
- Thread ID: 1360
261
- Fiber ID: 1340
262
- Total: 1.431426
261
+ Thread ID: 1400
262
+ Fiber ID: 1380
263
+ Total: 1.179968
263
264
  Sort by: self_time
264
265
 
265
266
  %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
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
270
271
 
271
272
  * recursively called methods
272
273
 
@@ -287,58 +288,23 @@ The interpretation of method names is:
287
288
  * <Object:MyObject>#test - The <> characters indicate a method on a singleton class.
288
289
 
289
290
  ########################################################################
290
- # Profile w/ N=87381 (i=1000000)
291
+ # Profile w/ N=100000 (i=1000000)
291
292
  # (n.b. RubyProf & tracepoint can change relative performance.
292
293
  # A sampling profiler can provide more accurate relative metrics.
293
294
  ########################################################################
294
295
 
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
296
  Filling ruby binary heap ---------------------------
331
297
  Profiling ruby binary heap ---------------------------
332
298
  Measure Mode: wall_time
333
- Thread ID: 1360
334
- Fiber ID: 1340
335
- Total: 16.514635
299
+ Thread ID: 1400
300
+ Fiber ID: 1380
301
+ Total: 16.425915
336
302
  Sort by: self_time
337
303
 
338
304
  %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
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
342
308
 
343
309
  * recursively called methods
344
310
 
@@ -361,16 +327,16 @@ The interpretation of method names is:
361
327
  Filling quaternary DHeap ---------------------------
362
328
  Profiling quaternary DHeap ---------------------------
363
329
  Measure Mode: wall_time
364
- Thread ID: 1360
365
- Fiber ID: 1340
366
- Total: 1.622729
330
+ Thread ID: 1400
331
+ Fiber ID: 1380
332
+ Total: 1.205573
367
333
  Sort by: self_time
368
334
 
369
335
  %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
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
374
340
 
375
341
  * recursively called methods
376
342
 
@@ -0,0 +1,7 @@
1
+ ---
2
+ inherit_from:
3
+ - "../../.rubocop.yml"
4
+
5
+ Style/GlobalVars:
6
+ AllowedVariables:
7
+ - "$defs"
data/ext/d_heap/d_heap.c CHANGED
@@ -1,165 +1,126 @@
1
+ #include "ruby.h"
1
2
  #include <float.h>
2
3
  #include <math.h>
3
- #include "ruby.h"
4
4
 
5
- /********************************************************************
6
- *
7
- * Type definitions
8
- *
9
- ********************************************************************/
10
-
11
- typedef struct dheap_struct dheap_t;
12
- typedef struct dheap_entry ENTRY;
5
+ #if CHAR_BIT != 8
6
+ # error "DHeap assumes 8-bit bytes"
7
+ #endif
13
8
 
14
- // TODO: convert SCORE to a union, and use an ENTRY flag for its type
15
- typedef long double SCORE;
9
+ #if SIZE_MAX != ULONG_MAX
10
+ # error "DHeap assumes 'size_t' is 'unsigned long'"
11
+ #endif
16
12
 
17
13
  #if LDBL_MANT_DIG < SIZEOF_UNSIGNED_LONG_LONG * 8
18
- #error 'unsigned long long' must fit into 'long double' mantissa
14
+ # error "DHeap assumes 'long double' mantissa can store 'unsigned long long'"
19
15
  #endif
20
16
 
21
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"!
22
19
  #if SIZEOF_UNSIGNED_LONG_LONG * 8 != 64
23
- #error 'unsigned long long' must be 64bits
20
+ # error "DHeap assumes 64-bit 'unsigned long long'"
24
21
  #endif
25
22
 
23
+ /********************************************************************
24
+ *
25
+ * Type definitions
26
+ *
27
+ ********************************************************************/
28
+
29
+ typedef struct dheap_struct dheap_t;
30
+ typedef struct dheap_entry ENTRY;
31
+
32
+ typedef double SCORE;
33
+
26
34
  /********************************************************************
27
35
  *
28
36
  * Struct definitions
29
37
  *
30
38
  ********************************************************************/
31
39
 
32
- struct dheap_struct {
33
- int d;
34
- long size;
35
- long capa;
40
+ struct dheap_struct
41
+ {
42
+ int d;
43
+ size_t size;
44
+ size_t capa;
36
45
  ENTRY *entries;
46
+ #ifdef DHEAP_MAP
47
+ VALUE indexes; // Hash
48
+ #endif
37
49
  };
38
50
 
39
- struct dheap_entry {
51
+ struct dheap_entry
52
+ {
40
53
  SCORE score;
41
54
  VALUE value;
42
55
  };
43
56
 
57
+ #define DHEAPMAP_P(heap) UNLIKELY(RTEST((heap)->indexes))
58
+
44
59
  /********************************************************************
45
60
  *
46
61
  * Constant definitions
47
62
  *
48
63
  ********************************************************************/
49
64
 
50
- #define DHEAP_DEFAULT_D 4
51
- #define DHEAP_MAX_D INT_MAX
65
+ #define DHEAP_DEFAULT_D 6
66
+ #define DHEAP_MAX_D INT_MAX
52
67
 
53
- // sizeof(ENTRY) => 32 bytes
68
+ // sizeof(ENTRY) => 16 bytes, 128-bits
54
69
  // one kilobyte = 32 * 32 bytes
55
- #define DHEAP_DEFAULT_CAPA 32
56
- #define DHEAP_MAX_CAPA (LONG_MAX / (int)sizeof(ENTRY))
70
+ #define DHEAP_DEFAULT_CAPA 32
71
+ #define DHEAP_MAX_CAPA (SIZE_MAX / (int)sizeof(ENTRY))
57
72
  #define DHEAP_CAPA_INCR_MAX (10 * 1024 * 1024 / (int)sizeof(ENTRY))
58
73
 
59
- static ID id_cmp; // <=>
60
- static ID id_abs; // abs
74
+ static ID id_cmp; // <=>
75
+ static ID id_abs; // abs
61
76
  static ID id_lshift; // <<
62
- static ID id_unary_minus; // -@
63
- static const ENTRY EmptyDheapEntry; // 0 value for safety overwrite after pop
77
+ static ID id_uminus; // -@
78
+
64
79
  static const rb_data_type_t dheap_data_type;
65
80
 
66
81
  /********************************************************************
67
82
  *
68
- * SCORE: casting to and from VALUE
69
- * adapted from similar methods in ruby's object.c
83
+ * Metaprogramming macros
70
84
  *
71
85
  ********************************************************************/
72
86
 
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));
84
- }
85
- return rb_float_new((double)(s));
86
- }
87
-
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;
110
- }
111
- }
87
+ #define LIKELY RB_LIKELY
88
+ #define UNLIKELY RB_UNLIKELY
112
89
 
113
- static inline SCORE
114
- INT2SCORE(VALUE x)
115
- {
116
- return (FIXNUM_P(x) ? FIX2SCORE(x) : BIG2SCORE(x));
117
- }
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
118
98
 
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
- }
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
126
112
 
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
- }
113
+ /********************************************************************
114
+ *
115
+ * SCORE: casting to and from VALUE
116
+ * adapted from similar methods in ruby's object.c
117
+ *
118
+ ********************************************************************/
134
119
 
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)));
161
- }
162
- }
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))
163
124
 
164
125
  /********************************************************************
165
126
  *
@@ -167,17 +128,28 @@ VAL2SCORE(VALUE score)
167
128
  *
168
129
  ********************************************************************/
169
130
 
170
- #define DHEAP_SCORE(heap, idx) ((heap)->entries[idx].score)
171
- #define DHEAP_VALUE(heap, idx) ((heap)->entries[idx].value)
131
+ #define DHEAP_SCORE(heap, idx) (DHEAP_GET(heap, idx).score)
132
+ #define DHEAP_VALUE(heap, idx) (DHEAP_GET(heap, idx).value)
172
133
 
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
- }
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
181
153
 
182
154
  /********************************************************************
183
155
  *
@@ -185,22 +157,20 @@ DHEAP_ENTRY_ARY(dheap_t *heap, long idx)
185
157
  *
186
158
  ********************************************************************/
187
159
 
188
- #define DHEAP_IDX_LAST(heap) ((heap)->size - 1)
189
- #define DHEAP_IDX_PARENT(heap, idx) (((idx) - 1) / (heap)->d)
160
+ #define DHEAP_IDX_LAST(heap) ((heap)->size - 1)
161
+ #define DHEAP_IDX_PARENT(heap, idx) (((idx)-1) / (heap)->d)
190
162
  #define DHEAP_IDX_CHILD_0(heap, idx) (((idx) * (heap)->d) + 1)
191
163
  #define DHEAP_IDX_CHILD_D(heap, idx) (((idx) * (heap)->d) + (heap)->d)
192
164
 
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)
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)
202
172
  #else
203
- #define ASSERT_DHEAP_IDX_OK(heap, index)
173
+ # define ASSERT_DHEAP_IDX_OK(heap, index)
204
174
  #endif
205
175
 
206
176
  /********************************************************************
@@ -214,113 +184,79 @@ static void
214
184
  dheap_compact(void *ptr)
215
185
  {
216
186
  dheap_t *heap = ptr;
217
- for (long i = 0; i < heap->size; ++i) {
187
+ for (size_t i = 0; i < heap->size; ++i) {
218
188
  if (DHEAP_VALUE(heap, i))
219
- rb_gc_location(DHEAP_VALUE(heap, i));
189
+ DHEAP_VALUE(heap, i) = rb_gc_location(DHEAP_VALUE(heap, i));
220
190
  }
191
+ # ifdef DHEAP_MAP
192
+ if (DHEAPMAP_P(heap)) heap->indexes = rb_gc_location(heap->indexes);
193
+ # endif
221
194
  }
222
195
  #else
223
- #define rb_gc_mark_movable(x) rb_gc_mark(x)
196
+ # define rb_gc_mark_movable(x) rb_gc_mark(x)
224
197
  #endif
225
198
 
226
199
  static void
227
200
  dheap_mark(void *ptr)
228
201
  {
229
202
  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));
203
+ for (size_t i = 0; i < heap->size; ++i) {
204
+ if (DHEAP_VALUE(heap, i)) rb_gc_mark_movable(DHEAP_VALUE(heap, i));
233
205
  }
206
+ #ifdef DHEAP_MAP
207
+ if (DHEAPMAP_P(heap)) rb_gc_mark_movable(heap->indexes);
208
+ #endif
234
209
  }
235
210
 
236
211
  static void
237
212
  dheap_free(void *ptr)
238
213
  {
239
214
  dheap_t *heap = ptr;
240
- heap->size = 0;
215
+ heap->size = 0;
241
216
  if (heap->entries) {
242
217
  xfree(heap->entries);
243
218
  heap->entries = NULL;
244
219
  }
245
220
  heap->capa = 0;
246
221
  xfree(ptr);
222
+ #ifdef DHEAP_MAP
223
+ heap->indexes = Qnil;
224
+ #endif
247
225
  }
248
226
 
249
227
  static size_t
250
228
  dheap_memsize(const void *ptr)
251
229
  {
252
230
  const dheap_t *heap = ptr;
253
- size_t size = 0;
231
+ size_t size = 0;
254
232
  size += sizeof(*heap);
255
233
  size += sizeof(ENTRY) * heap->capa;
256
234
  return size;
257
235
  }
258
236
 
259
-
260
237
  static const rb_data_type_t dheap_data_type = {
261
238
  "DHeap",
262
- {
263
- (void (*)(void*))dheap_mark,
264
- (void (*)(void*))dheap_free,
265
- (size_t (*)(const void *))dheap_memsize,
239
+ { (void (*)(void *))dheap_mark,
240
+ (void (*)(void *))dheap_free,
241
+ (size_t(*)(const void *))dheap_memsize,
266
242
  #ifdef HAVE_RB_GC_MARK_MOVABLE
267
- (void (*)(void*))dheap_compact, {0}
243
+ (void (*)(void *))dheap_compact,
244
+ { 0 }
268
245
  #else
269
- {0}
246
+ { 0 }
270
247
  #endif
271
248
  },
272
- 0, 0,
249
+ 0,
250
+ 0,
273
251
  RUBY_TYPED_FREE_IMMEDIATELY,
274
252
  };
275
253
 
276
254
  /********************************************************************
277
255
  *
278
256
  * DHeap comparisons
279
- * TODO: bring back comparisons for score types other than `long double`.
280
257
  *
281
258
  ********************************************************************/
282
259
 
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.
295
- *
296
- * Only Integer, Float, and String are optimized,
297
- * and only when both arguments are the same type.
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
260
  /********************************************************************
325
261
  *
326
262
  * DHeap allocation and initialization and resizing
@@ -330,19 +266,21 @@ static const rb_data_type_t dheap_data_type = {
330
266
  static VALUE
331
267
  dheap_s_alloc(VALUE klass)
332
268
  {
333
- VALUE obj;
269
+ VALUE obj;
334
270
  dheap_t *heap;
335
271
 
336
- // TypedData_Make_Struct uses a non-std "statement expression"
337
272
  #pragma GCC diagnostic push
338
273
  #pragma GCC diagnostic ignored "-Wpedantic"
274
+ // TypedData_Make_Struct uses a non-std "statement expression"
339
275
  obj = TypedData_Make_Struct(klass, dheap_t, &dheap_data_type, heap);
340
276
  #pragma GCC diagnostic pop
341
- heap->d = DHEAP_DEFAULT_D;
342
-
343
- heap->size = 0;
344
- heap->capa = 0;
277
+ heap->d = DHEAP_DEFAULT_D;
278
+ heap->size = 0;
279
+ heap->capa = 0;
345
280
  heap->entries = NULL;
281
+ #ifdef DHEAP_MAP
282
+ heap->indexes = Qnil;
283
+ #endif
346
284
 
347
285
  return obj;
348
286
  }
@@ -355,8 +293,15 @@ get_dheap_struct(VALUE self)
355
293
  return heap;
356
294
  }
357
295
 
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
+ }
302
+
358
303
  void
359
- dheap_set_capa(dheap_t *heap, long new_capa)
304
+ dheap_set_capa(dheap_t *heap, size_t new_capa)
360
305
  {
361
306
  // Do nothing if we already have the capacity or are resizing too small
362
307
  if (new_capa <= heap->capa || new_capa <= heap->size) return;
@@ -371,27 +316,36 @@ dheap_set_capa(dheap_t *heap, long new_capa)
371
316
  }
372
317
 
373
318
  static void
374
- dheap_ensure_room_for_push(dheap_t *heap, long incr_by)
319
+ dheap_incr_capa(dheap_t *heap, size_t new_size)
375
320
  {
376
- long new_size = heap->size + incr_by;
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);
334
+ }
377
335
 
336
+ static void
337
+ dheap_ensure_room_for_push(dheap_t *heap, size_t incr_by)
338
+ {
378
339
  // 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);
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);
395
349
  }
396
350
  }
397
351
 
@@ -399,34 +353,34 @@ static inline int
399
353
  dheap_value_to_int_d(VALUE num)
400
354
  {
401
355
  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
- }
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);
408
358
  return d;
409
359
  }
410
360
 
411
- static inline long
412
- dheap_value_to_long_capa(VALUE num)
361
+ static inline size_t
362
+ dheap_value_to_capa(VALUE num)
413
363
  {
414
- long capa = NUM2LONG(num);
364
+ size_t capa = NUM2ULONG(num);
415
365
  if (capa < 1) {
416
- rb_raise(rb_eArgError, "DHeap capa=%lu must be positive", capa);
366
+ rb_raise(rb_eArgError, "DHeap capa=%zu must be positive", capa);
417
367
  }
418
368
  return capa;
419
369
  }
420
370
 
421
371
  static VALUE
422
- dheap_initialize(VALUE self, VALUE d, VALUE capa) {
372
+ dheap_init(VALUE self, VALUE d, VALUE capa, VALUE map)
373
+ {
423
374
  dheap_t *heap = get_dheap_struct(self);
424
375
 
425
- if(heap->entries || heap->size || heap->capa)
376
+ if (heap->entries || heap->size || heap->capa)
426
377
  rb_raise(rb_eScriptError, "DHeap already initialized.");
427
378
 
428
379
  heap->d = dheap_value_to_int_d(d);
429
- dheap_set_capa(heap, dheap_value_to_long_capa(capa));
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
430
384
 
431
385
  return self;
432
386
  }
@@ -435,18 +389,19 @@ dheap_initialize(VALUE self, VALUE d, VALUE capa) {
435
389
  static VALUE
436
390
  dheap_initialize_copy(VALUE copy, VALUE orig)
437
391
  {
438
- dheap_t *heap_copy;
392
+ dheap_t *heap_copy = get_dheap_struct_unfrozen(copy);
439
393
  dheap_t *heap_orig = get_dheap_struct(orig);
440
394
 
441
- rb_check_frozen(copy);
442
- TypedData_Get_Struct(copy, dheap_t, &dheap_data_type, heap_copy);
443
-
444
395
  heap_copy->d = heap_orig->d;
445
396
 
446
397
  dheap_set_capa(heap_copy, heap_orig->capa);
447
398
  heap_copy->size = heap_orig->size;
448
399
  if (heap_copy->size)
449
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
450
405
 
451
406
  return copy;
452
407
  }
@@ -457,75 +412,54 @@ dheap_initialize_copy(VALUE copy, VALUE orig)
457
412
  *
458
413
  ********************************************************************/
459
414
 
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);
480
- }
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;
481
434
 
482
- /*
483
- * this is a tiny bit more complicated than the binary heap version
484
- */
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))) {
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))) {
494
437
  min_child = sibidx;
495
438
  }
496
439
  }
497
440
  return min_child;
498
441
  }
499
442
 
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
- }
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)
529
463
 
530
464
  /********************************************************************
531
465
  *
@@ -540,9 +474,11 @@ static VALUE
540
474
  dheap_size(VALUE self)
541
475
  {
542
476
  dheap_t *heap = get_dheap_struct(self);
543
- return LONG2NUM(heap->size);
477
+ return ULONG2NUM(heap->size);
544
478
  }
545
479
 
480
+ #define DHEAP_EMPTY_P(heap) UNLIKELY((heap)->size <= 0)
481
+
546
482
  /*
547
483
  * @return [Boolean] if the heap is empty
548
484
  */
@@ -550,7 +486,7 @@ static VALUE
550
486
  dheap_empty_p(VALUE self)
551
487
  {
552
488
  dheap_t *heap = get_dheap_struct(self);
553
- return heap->size ? Qfalse : Qtrue;
489
+ return DHEAP_EMPTY_P(heap) ? Qtrue : Qfalse;
554
490
  }
555
491
 
556
492
  /*
@@ -569,13 +505,47 @@ dheap_attr_d(VALUE self)
569
505
  *
570
506
  ********************************************************************/
571
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
+
572
516
  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);
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);
578
547
  }
548
+ #endif
579
549
 
580
550
  /*
581
551
  * Inserts a value into the heap, using a score to determine sort-order.
@@ -590,26 +560,42 @@ dheap_push_entry(dheap_t *heap, ENTRY *entry) {
590
560
  * @return [self]
591
561
  */
592
562
  static VALUE
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);
597
-
598
- entry.score = VAL2SCORE(score);
599
- entry.value = value;
600
- dheap_push_entry(heap, &entry);
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
+ }
601
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);
602
577
  return self;
603
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;
589
+ }
604
590
 
605
591
  /*
606
592
  * @overload push(value, score = value)
607
593
  *
608
594
  * Push a value onto heap, using a score to determine sort-order.
609
595
  *
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
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
613
599
  * +Float(value)+.
614
600
  *
615
601
  * Time complexity: <b>O(log n / log d)</b> <i>(worst-case)</i>
@@ -620,18 +606,23 @@ dheap_insert(VALUE self, VALUE score, VALUE value) {
620
606
  * @return [self]
621
607
  */
622
608
  static VALUE
623
- dheap_push(int argc, VALUE *argv, VALUE self) {
624
- dheap_t *heap = get_dheap_struct(self);
625
- ENTRY entry;
626
- rb_check_frozen(self);
627
-
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);
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
+ }
632
615
 
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);
633
623
  return self;
634
624
  }
625
+ #endif
635
626
 
636
627
  /*
637
628
  * Pushes a value onto the heap.
@@ -645,17 +636,23 @@ dheap_push(int argc, VALUE *argv, VALUE self) {
645
636
  * @return [self]
646
637
  */
647
638
  static VALUE
648
- dheap_left_shift(VALUE self, VALUE value) {
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);
639
+ dheap_lshift(VALUE self, VALUE value)
640
+ {
641
+ ENTRY entry = { VAL2SCORE(value), value };
642
+ dheap_push_entry(self, &entry);
643
+ return self;
644
+ }
656
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);
657
653
  return self;
658
654
  }
655
+ #endif
659
656
 
660
657
  /********************************************************************
661
658
  *
@@ -663,23 +660,15 @@ dheap_left_shift(VALUE self, VALUE value) {
663
660
  *
664
661
  ********************************************************************/
665
662
 
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
- }
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)
683
672
 
684
673
  /*
685
674
  * Returns the next value on the heap, and its score, without popping it
@@ -694,11 +683,12 @@ static VALUE
694
683
  dheap_peek_with_score(VALUE self)
695
684
  {
696
685
  dheap_t *heap = get_dheap_struct(self);
697
- return DHEAP_ENTRY_ARY(heap, 0);
686
+ return PEEK_WITH_SCORE(heap);
698
687
  }
699
688
 
700
689
  /*
701
- * Returns the next score on the heap, without the value and without popping it.
690
+ * Returns the next score on the heap, without the value and without popping
691
+ * it.
702
692
  *
703
693
  * Time complexity: <b>O(1)</b> <i>(worst-case)</i>
704
694
  * @return [nil, Numeric] the next score, if there is one
@@ -710,8 +700,8 @@ static VALUE
710
700
  dheap_peek_score(VALUE self)
711
701
  {
712
702
  dheap_t *heap = get_dheap_struct(self);
713
- if (DHEAP_IDX_LAST(heap) < 0) return Qnil;
714
- return SCORE2NUM(DHEAP_SCORE(heap, 0));
703
+ if (DHEAP_EMPTY_P(heap)) return Qnil;
704
+ return SCORE2NUM(PEEK_SCORE(heap));
715
705
  }
716
706
 
717
707
  /*
@@ -727,9 +717,52 @@ static VALUE
727
717
  dheap_peek(VALUE self)
728
718
  {
729
719
  dheap_t *heap = get_dheap_struct(self);
730
- return heap->size ? DHEAP_VALUE(heap, 0) : Qnil;
720
+ if (DHEAP_EMPTY_P(heap)) return Qnil;
721
+ return PEEK_VALUE(heap);
731
722
  }
732
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
+
733
766
  /*
734
767
  * Pops the minimum value from the top of the heap
735
768
  *
@@ -745,10 +778,23 @@ dheap_peek(VALUE self)
745
778
  static VALUE
746
779
  dheap_pop(VALUE self)
747
780
  {
748
- dheap_t *heap = get_dheap_struct(self);
749
- rb_check_frozen(self);
750
- return heap->size ? dheap_pop0(heap) : Qnil;
781
+ dheap_t *heap = get_dheap_struct_unfrozen(self);
782
+ VALUE popped;
783
+ POP(dheap, heap, &popped);
784
+ return popped;
785
+ }
786
+
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;
751
796
  }
797
+ #endif
752
798
 
753
799
  /*
754
800
  * Pops the minimum value from the top of the heap, along with its score.
@@ -763,13 +809,27 @@ dheap_pop(VALUE self)
763
809
  static VALUE
764
810
  dheap_pop_with_score(VALUE self)
765
811
  {
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;
812
+ dheap_t *heap = get_dheap_struct_unfrozen(self);
813
+ VALUE popped;
814
+ POP_WITH_SCORE(dheap, heap, &popped);
815
+ return popped;
771
816
  }
772
817
 
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
829
+
830
+ #define DHEAP_POP_IF(heap, cmp, max_score) \
831
+ if (!cmp(PEEK_SCORE(heap), VAL2SCORE(max_score))) return Qnil
832
+
773
833
  /*
774
834
  * Pops the minimum value only if it is less than or equal to a max score.
775
835
  *
@@ -787,13 +847,24 @@ dheap_pop_with_score(VALUE self)
787
847
  static VALUE
788
848
  dheap_pop_lte(VALUE self, VALUE max_score)
789
849
  {
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);
850
+ dheap_t *heap = get_dheap_struct_unfrozen(self);
851
+ VALUE popped;
852
+ POP_LTE(dheap, heap, VAL2SCORE(max_score), &popped);
853
+ return popped;
795
854
  }
796
855
 
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;
865
+ }
866
+ #endif
867
+
797
868
  /*
798
869
  * Pops the minimum value only if it is less than a max score.
799
870
  *
@@ -811,31 +882,39 @@ dheap_pop_lte(VALUE self, VALUE max_score)
811
882
  static VALUE
812
883
  dheap_pop_lt(VALUE self, VALUE max_score)
813
884
  {
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);
885
+ dheap_t *heap = get_dheap_struct_unfrozen(self);
886
+ VALUE popped;
887
+ POP_LT(dheap, heap, VAL2SCORE(max_score), &popped);
888
+ return popped;
819
889
  }
820
890
 
821
- #define DHEAP_PEEK_LT_P(heap, max_score) \
822
- (heap->size && CMP_LT(DHEAP_SCORE(heap, 0), max_score))
823
-
891
+ #ifdef DHEAP_MAP
892
+ /* (see DHeap#pop_lt) */
824
893
  static VALUE
825
- dheap_pop_all_below0(dheap_t *heap, SCORE max_score, VALUE array)
894
+ dheapmap_pop_lt(VALUE self, VALUE max_score)
826
895
  {
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;
896
+ dheap_t *heap = get_dheap_struct_unfrozen(self);
897
+ VALUE popped;
898
+ POP_LT(dheapmap, heap, VAL2SCORE(max_score), &popped);
899
+ return popped;
838
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)
839
918
 
840
919
  /*
841
920
  * @overload pop_all_below(max_score, receiver = [])
@@ -856,18 +935,12 @@ dheap_pop_all_below0(dheap_t *heap, SCORE max_score, VALUE array)
856
935
  static VALUE
857
936
  dheap_pop_all_below(int argc, VALUE *argv, VALUE self)
858
937
  {
859
- dheap_t *heap = get_dheap_struct(self);
860
- SCORE max_score;
861
- VALUE array;
862
- rb_check_frozen(self);
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];
863
941
  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);
942
+ DHEAP_DISPATCH_STMT(heap, POP_ALL_BELOW, max_score, array);
943
+ return array;
871
944
  }
872
945
 
873
946
  /********************************************************************
@@ -876,6 +949,17 @@ dheap_pop_all_below(int argc, VALUE *argv, VALUE self)
876
949
  *
877
950
  ********************************************************************/
878
951
 
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;
961
+ }
962
+
879
963
  /*
880
964
  * Clears all values from the heap, leaving it empty.
881
965
  *
@@ -884,14 +968,64 @@ dheap_pop_all_below(int argc, VALUE *argv, VALUE self)
884
968
  static VALUE
885
969
  dheap_clear(VALUE self)
886
970
  {
887
- dheap_t *heap = get_dheap_struct(self);
888
- rb_check_frozen(self);
889
- if (0 < heap->size) {
971
+ dheap_t *heap = get_dheap_struct_unfrozen(self);
972
+ if (!DHEAP_EMPTY_P(heap)) {
890
973
  heap->size = 0;
974
+ #ifdef DHEAP_MAP
975
+ if (DHEAPMAP_P(heap)) rb_hash_clear(heap->indexes);
976
+ #endif
891
977
  }
892
978
  return self;
893
979
  }
894
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
+
895
1029
  /********************************************************************
896
1030
  *
897
1031
  * DHeap setup
@@ -902,14 +1036,26 @@ void
902
1036
  Init_d_heap(void)
903
1037
  {
904
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
905
1042
 
906
- id_cmp = rb_intern_const("<=>");
907
- id_abs = rb_intern_const("abs");
1043
+ id_cmp = rb_intern_const("<=>");
1044
+ id_abs = rb_intern_const("abs");
908
1045
  id_lshift = rb_intern_const("<<");
909
- id_unary_minus = rb_intern_const("-@");
1046
+ id_uminus = rb_intern_const("-@");
910
1047
 
911
1048
  rb_define_alloc_func(rb_cDHeap, dheap_s_alloc);
912
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
+
913
1059
  /*
914
1060
  * This is based on INT_MAX. But it is very very unlikely you will want a
915
1061
  * large value for d. The tradeoff is that higher d values give faster push
@@ -917,12 +1063,12 @@ Init_d_heap(void)
917
1063
  * stick with the default. If you expect more pushes than pops, it might be
918
1064
  * worthwhile to increase d.
919
1065
  */
920
- rb_define_const(rb_cDHeap, "MAX_D", INT2NUM(DHEAP_MAX_D));
1066
+ rb_define_const(rb_cDHeap, "MAX_D", INT2NUM(DHEAP_MAX_D));
921
1067
 
922
1068
  /*
923
1069
  * d=4 uses the fewest comparisons for (worst case) insert + delete-min.
924
1070
  */
925
- rb_define_const(rb_cDHeap, "DEFAULT_D", INT2NUM(DHEAP_DEFAULT_D));
1071
+ rb_define_const(rb_cDHeap, "DEFAULT_D", INT2NUM(DHEAP_DEFAULT_D));
926
1072
 
927
1073
  /*
928
1074
  * The default heap capacity. The heap grows automatically as necessary, so
@@ -930,26 +1076,31 @@ Init_d_heap(void)
930
1076
  */
931
1077
  rb_define_const(rb_cDHeap, "DEFAULT_CAPA", INT2NUM(DHEAP_DEFAULT_CAPA));
932
1078
 
933
- rb_define_private_method(rb_cDHeap, "__init_without_kw__", dheap_initialize, 2);
1079
+ rb_define_private_method(rb_cDHeap, "__init_without_kw__", dheap_init, 3);
934
1080
  rb_define_method(rb_cDHeap, "initialize_copy", dheap_initialize_copy, 1);
935
1081
 
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);
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);
943
- rb_define_method(rb_cDHeap, "push", dheap_push, -1);
944
- rb_define_method(rb_cDHeap, "<<", dheap_left_shift, 1);
945
- rb_define_method(rb_cDHeap, "pop", dheap_pop, 0);
1082
+ rb_define_method(rb_cDHeap, "d", dheap_attr_d, 0);
1083
+ rb_define_method(rb_cDHeap, "size", dheap_size, 0);
1084
+ rb_define_method(rb_cDHeap, "empty?", dheap_empty_p, 0);
1085
+ rb_define_method(rb_cDHeap, "to_a", dheap_to_a, 0);
946
1086
 
947
- rb_define_method(rb_cDHeap, "pop_lt", dheap_pop_lt, 1);
948
- rb_define_method(rb_cDHeap, "pop_lte", dheap_pop_lte, 1);
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);
949
1091
  rb_define_method(rb_cDHeap, "pop_all_below", dheap_pop_all_below, -1);
950
1092
 
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);
1093
+ def_override_inherited("insert", insert, 2);
1094
+ def_override_inherited("push", push, -1);
1095
+ def_override_inherited("<<", lshift, 1);
954
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
955
1106
  }