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