d_heap 0.6.1 → 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.
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
  }