d_heap 0.2.1 → 0.6.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -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
  }