d_heap 0.5.0 → 0.6.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -5,27 +5,31 @@ prelude: |
5
5
  include DHeap::Benchmarks
6
6
  fill_random_vals
7
7
 
8
- n = ENV.fetch("BENCH_N", 1000).to_i
8
+ N = ENV.fetch("BENCH_N", 1000).to_i
9
9
 
10
10
  benchmark:
11
11
  - script: &script |
12
- q.clear
13
- i = 0
14
- while i < n
15
- q << random_val
16
- i += 1
12
+ if __bmdv_i % N == 0
13
+ q.clear
17
14
  end
15
+
16
+ q << random_val
18
17
  name: "push N (findmin)"
19
18
  prelude: "q = initq FindMin"
19
+ loop_count: 24000000
20
20
  - script: *script
21
21
  name: "push N (bsearch)"
22
22
  prelude: "q = initq BSearch"
23
+ loop_count: 2300000
23
24
  - script: *script
24
25
  name: "push N (rb_heap)"
25
26
  prelude: "q = initq RbHeap"
27
+ loop_count: 9800000
26
28
  - script: *script
27
29
  name: "push N (c++ stl)"
28
30
  prelude: "q = initq CppSTL"
31
+ loop_count: 18700000
29
32
  - script: *script
30
33
  name: "push N (c_dheap)"
31
34
  prelude: "q = initq DHeap"
35
+ loop_count: 25100000
@@ -5,31 +5,48 @@ prelude: |
5
5
  include DHeap::Benchmarks
6
6
  fill_random_vals
7
7
 
8
- n = ENV.fetch("BENCH_N", 1000).to_i
8
+ N = ENV.fetch("BENCH_N", 1000).to_i
9
+ N2 = N * 2
9
10
 
10
- i = 0
11
+ i = j = 0
11
12
 
12
13
  benchmark:
13
14
  - script: &script |
14
- while i < n
15
- q << random_val
16
- i += 1
17
- end
18
- while 0 < i
19
- q.pop
20
- i -= 1
21
- end
15
+ if i < N
16
+ q.clear if __bmdv_i == 0
17
+ q << random_val
18
+ i += 1
19
+
20
+ elsif j < N
21
+ q.pop
22
+ j += 1
23
+
24
+ elsif q.empty?
25
+ i = 1
26
+ j = 0
27
+ q.clear
28
+ q << random_val
29
+
30
+ else
31
+ raise "q not empty!"
32
+ end
33
+
22
34
  name: "push N + pop N (findmin)"
23
35
  prelude: "q = initq FindMin"
36
+ loop_count: 200000
24
37
  - script: *script
25
38
  name: "push N + pop N (bsearch)"
26
39
  prelude: "q = initq BSearch"
40
+ loop_count: 4000000
27
41
  - script: *script
28
42
  name: "push N + pop N (rb_heap)"
29
43
  prelude: "q = initq RbHeap"
44
+ loop_count: 4000000
30
45
  - script: *script
31
46
  name: "push N + pop N (c++ stl)"
32
47
  prelude: "q = initq CppSTL"
48
+ loop_count: 16000000
33
49
  - script: *script
34
50
  name: "push N + pop N (c_dheap)"
35
51
  prelude: "q = initq DHeap"
52
+ loop_count: 16000000
@@ -13,15 +13,20 @@ benchmark:
13
13
  q.pop
14
14
  name: "push + pop (findmin)"
15
15
  prelude: "q = FindMin.new(n) { random_val }"
16
+ loop_count: 250000
16
17
  - script: *script
17
18
  name: "push + pop (bsearch)"
18
19
  prelude: "q = BSearch.new(n) { random_val }"
20
+ loop_count: 5000000
19
21
  - script: *script
20
22
  name: "push + pop (rb_heap)"
21
23
  prelude: "q = RbHeap.new(n) { random_val }"
24
+ loop_count: 2000000
22
25
  - script: *script
23
26
  name: "push + pop (c++ stl)"
24
27
  prelude: "q = initq CppSTL, n"
28
+ loop_count: 13000000
25
29
  - script: *script
26
30
  name: "push + pop (c_dheap)"
27
31
  prelude: "q = initq DHeap, n"
32
+ loop_count: 20000000
@@ -0,0 +1,13 @@
1
+ #!/bin/bash
2
+ set -eu
3
+ SCRIPT_DIR=$(cd "$(dirname "${BASH_SOURCE[0]}")" > /dev/null; pwd -P)
4
+ PROJECT_DIR=$(cd "$SCRIPT_DIR" > /dev/null; cd .. > /dev/null; pwd -P)
5
+ cd "$PROJECT_DIR"
6
+
7
+ opts=( --bundle -e 'N 100000;N 10000;N 1000;N 100;N 10' -o gruff )
8
+
9
+ for bm in push_n push_n_pop_n push_pop; do
10
+ bin/benchmark-driver "${opts[@]}" "benchmarks/${bm}.yml"
11
+ mv graph.png "images/${bm}.png"
12
+ done
13
+
@@ -15,7 +15,7 @@ Gem::Specification.new do |spec|
15
15
  DESC
16
16
  spec.homepage = "https://github.com/nevans/#{spec.name}"
17
17
  spec.license = "MIT"
18
- spec.required_ruby_version = Gem::Requirement.new(">= 2.5.0")
18
+ spec.required_ruby_version = Gem::Requirement.new(">= 2.4.0")
19
19
 
20
20
  spec.metadata["homepage_uri"] = spec.homepage
21
21
  spec.metadata["source_code_uri"] = spec.homepage
@@ -1,36 +1,96 @@
1
1
  #include <float.h>
2
- #include "d_heap.h"
2
+ #include <math.h>
3
+ #include "ruby.h"
3
4
 
4
- ID id_cmp; // <=>
5
- ID id_abs; // abs
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;
6
16
 
7
- typedef struct dheap_struct {
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 {
8
33
  int d;
9
34
  long size;
10
35
  long capa;
11
36
  ENTRY *entries;
12
- } dheap_t;
37
+ };
13
38
 
14
- #define DHEAP_SCORE(heap, idx) ((heap)->entries[idx].score)
15
- #define DHEAP_VALUE(heap, idx) ((heap)->entries[idx].value)
16
- #define DHEAP_IDX_LAST(heap) (DHEAP_SIZE((heap)) - 1)
17
- #define DHEAP_IDX_PARENT(heap, idx) (((idx) - 1) / (heap)->d)
18
- #define DHEAP_IDX_CHILD_0(heap, idx) (((idx) * (heap)->d) + 1)
19
- #define DHEAP_IDX_CHILD_D(heap, idx) (((idx) * (heap)->d) + (heap)->d)
39
+ struct dheap_entry {
40
+ SCORE score;
41
+ VALUE value;
42
+ };
20
43
 
21
- #define DHEAP_SIZE(heap) ((heap)->size)
44
+ /********************************************************************
45
+ *
46
+ * Constant definitions
47
+ *
48
+ ********************************************************************/
22
49
 
23
- #define CMP_LT(a, b) ((a) < (b))
24
- #define CMP_LTE(a, b) ((a) <= (b))
25
- #define CMP_GT(a, b) ((a) > (b))
26
- #define CMP_GTE(a, b) ((a) >= (b))
50
+ #define DHEAP_DEFAULT_D 4
51
+ #define DHEAP_MAX_D INT_MAX
27
52
 
28
- #if LDBL_MANT_DIG < SIZEOF_UNSIGNED_LONG_LONG * 8
29
- #error 'unsigned long long' should fit into 'long double' mantissa
30
- #endif
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
+ }
31
93
 
32
- // copied and modified from ruby's object.c
33
- #define FIX2SCORE(x) (long double)FIX2LONG(x)
34
94
  // We could translate a much wider range of values to long double by
35
95
  // implementing a new `rb_big2ldbl(x)` function. But requires reaching into
36
96
  // T_BIGNUM internals.
@@ -43,18 +103,27 @@ BIG2SCORE(VALUE x)
43
103
  } else {
44
104
  unsigned long long ull;
45
105
  long double ldbl;
46
- x = rb_funcall(x, id_abs, 0);
106
+ x = rb_funcall(x, id_abs, 0, Qundef);
47
107
  ull = rb_big2ull(x);
48
108
  ldbl = (long double) ull;
49
109
  return -ldbl;
50
110
  }
51
111
  }
52
- #define INT2SCORE(x) \
53
- (FIXNUM_P(x) ? FIX2SCORE(x) : BIG2SCORE(x))
54
- #define NUM2SCORE(x) \
55
- (FIXNUM_P(x) ? FIX2SCORE(x) : \
56
- RB_TYPE_P(x, T_BIGNUM) ? BIG2SCORE(x) : \
57
- (Check_Type(x, T_FLOAT), (long double)RFLOAT_VALUE(x)))
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
+
58
127
  static inline long double
59
128
  RAT2SCORE(VALUE x)
60
129
  {
@@ -73,14 +142,13 @@ RAT2SCORE(VALUE x)
73
142
  * * reduced to long double (should be 80 or 128 bit) if it is Rational
74
143
  * * reduced to double precision if the value is convertable by Float(x)
75
144
  */
76
- static inline long double
145
+ static inline SCORE
77
146
  VAL2SCORE(VALUE score)
78
147
  {
79
148
  // assert that long double can hold 'unsigned long long':
80
149
  // static_assert(sizeof(unsigned long long) * 8 <= LDBL_MANT_DIG);
81
150
  // assert that long double can hold T_FLOAT
82
151
  // static_assert(sizeof(double) <= sizeof(long double));
83
-
84
152
  switch (TYPE(score)) {
85
153
  case T_FIXNUM:
86
154
  return FIX2SCORE(score);
@@ -93,59 +161,37 @@ VAL2SCORE(VALUE score)
93
161
  }
94
162
  }
95
163
 
96
- #ifdef ORIG_SCORE_CMP_CODE
97
-
98
- #define VAL2SCORE(score) (score)
99
-
100
- #define CMP_LT(a, b) (optimized_cmp(a, b) < 0)
101
- #define CMP_LTE(a, b) (optimized_cmp(a, b) <= 0)
102
- #define CMP_GT(a, b) (optimized_cmp(a, b) > 0)
103
- #define CMP_GTE(a, b) (optimized_cmp(a, b) >= 0)
104
-
105
- // from internal/compar.h
106
- #define STRING_P(s) (RB_TYPE_P((s), T_STRING) && CLASS_OF(s) == rb_cString)
107
- /*
108
- * short-circuit evaluation for a few basic types.
164
+ /********************************************************************
109
165
  *
110
- * Only Integer, Float, and String are optimized,
111
- * and only when both arguments are the same type.
112
- */
113
- static inline int
114
- optimized_cmp(SCORE a, SCORE b) {
115
- if (a == b) // Fixnum equality and object equality
116
- return 0;
117
- if (FIXNUM_P(a) && FIXNUM_P(b))
118
- return (FIX2LONG(a) < FIX2LONG(b)) ? -1 : 1;
119
- if (RB_FLOAT_TYPE_P(a) && RB_FLOAT_TYPE_P(b))
120
- {
121
- double x, y;
122
- x = RFLOAT_VALUE(a);
123
- y = RFLOAT_VALUE(b);
124
- if (isnan(x) || isnan(y)) rb_cmperr(a, b); // raise ArgumentError
125
- return (x < y) ? -1 : ((x == y) ? 0 : 1);
126
- }
127
- if (RB_TYPE_P(a, T_BIGNUM) && RB_TYPE_P(b, T_BIGNUM))
128
- return FIX2INT(rb_big_cmp(a, b));
129
- if (STRING_P(a) && STRING_P(b))
130
- return rb_str_cmp(a, b);
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)
131
172
 
132
- // give up on an optimized version and just call (a <=> b)
133
- return rb_cmpint(rb_funcallv(a, id_cmp, 1, &b), a, b);
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)));
134
180
  }
135
181
 
136
- #endif
182
+ /********************************************************************
183
+ *
184
+ * DHeap index math
185
+ *
186
+ ********************************************************************/
137
187
 
138
- #define DHEAP_Check_d_size(d) do { \
139
- if (d < 2) { \
140
- rb_raise(rb_eArgError, "DHeap d=%d is too small", d); \
141
- } \
142
- if (d > DHEAP_MAX_D) { \
143
- rb_raise(rb_eArgError, "DHeap d=%d is too large", d); \
144
- } \
145
- } while (0)
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)
146
192
 
147
193
  #ifdef __D_HEAP_DEBUG
148
- #define ASSERT_DHEAP_INDEX(heap, index) do { \
194
+ #define ASSERT_DHEAP_IDX_OK(heap, index) do { \
149
195
  if (index < 0) { \
150
196
  rb_raise(rb_eIndexError, "DHeap index %ld too small", index); \
151
197
  } \
@@ -154,18 +200,28 @@ optimized_cmp(SCORE a, SCORE b) {
154
200
  } \
155
201
  } while (0)
156
202
  #else
157
- #define ASSERT_DHEAP_INDEX(heap, index)
203
+ #define ASSERT_DHEAP_IDX_OK(heap, index)
158
204
  #endif
159
205
 
206
+ /********************************************************************
207
+ *
208
+ * rb_data_type_t definitions
209
+ *
210
+ ********************************************************************/
211
+
212
+ #ifdef HAVE_RB_GC_MARK_MOVABLE
160
213
  static void
161
214
  dheap_compact(void *ptr)
162
215
  {
163
216
  dheap_t *heap = ptr;
164
217
  for (long i = 0; i < heap->size; ++i) {
165
218
  if (DHEAP_VALUE(heap, i))
166
- dheap_gc_location(DHEAP_VALUE(heap, i));
219
+ rb_gc_location(DHEAP_VALUE(heap, i));
167
220
  }
168
221
  }
222
+ #else
223
+ #define rb_gc_mark_movable(x) rb_gc_mark(x)
224
+ #endif
169
225
 
170
226
  static void
171
227
  dheap_mark(void *ptr)
@@ -200,25 +256,88 @@ dheap_memsize(const void *ptr)
200
256
  return size;
201
257
  }
202
258
 
259
+
203
260
  static const rb_data_type_t dheap_data_type = {
204
261
  "DHeap",
205
262
  {
206
263
  (void (*)(void*))dheap_mark,
207
264
  (void (*)(void*))dheap_free,
208
265
  (size_t (*)(const void *))dheap_memsize,
209
- dheap_compact_callback(dheap_compact),
266
+ #ifdef HAVE_RB_GC_MARK_MOVABLE
267
+ (void (*)(void*))dheap_compact, {0}
268
+ #else
269
+ {0}
270
+ #endif
210
271
  },
211
272
  0, 0,
212
273
  RUBY_TYPED_FREE_IMMEDIATELY,
213
274
  };
214
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
+
215
330
  static VALUE
216
331
  dheap_s_alloc(VALUE klass)
217
332
  {
218
333
  VALUE obj;
219
334
  dheap_t *heap;
220
335
 
336
+ // TypedData_Make_Struct uses a non-std "statement expression"
337
+ #pragma GCC diagnostic push
338
+ #pragma GCC diagnostic ignored "-Wpedantic"
221
339
  obj = TypedData_Make_Struct(klass, dheap_t, &dheap_data_type, heap);
340
+ #pragma GCC diagnostic pop
222
341
  heap->d = DHEAP_DEFAULT_D;
223
342
 
224
343
  heap->size = 0;
@@ -257,7 +376,7 @@ dheap_ensure_room_for_push(dheap_t *heap, long incr_by)
257
376
  long new_size = heap->size + incr_by;
258
377
 
259
378
  // check for overflow of new_size
260
- if (DHEAP_MAX_SIZE - incr_by < heap->size)
379
+ if (DHEAP_MAX_CAPA - incr_by < heap->size)
261
380
  rb_raise(rb_eIndexError, "index %ld too big", new_size);
262
381
 
263
382
  // if it existing capacity is too small
@@ -267,7 +386,7 @@ dheap_ensure_room_for_push(dheap_t *heap, long incr_by)
267
386
  if (DHEAP_CAPA_INCR_MAX < new_size)
268
387
  new_size = new_size + DHEAP_CAPA_INCR_MAX;
269
388
  // check for overflow of new_capa
270
- if (DHEAP_MAX_SIZE / 2 < new_size) new_capa = DHEAP_MAX_SIZE;
389
+ if (DHEAP_MAX_CAPA / 2 < new_size) new_capa = DHEAP_MAX_CAPA;
271
390
  // cap max incr_by
272
391
  if (heap->capa + DHEAP_CAPA_INCR_MAX < new_capa)
273
392
  new_capa = heap->capa + DHEAP_CAPA_INCR_MAX;
@@ -276,25 +395,38 @@ dheap_ensure_room_for_push(dheap_t *heap, long incr_by)
276
395
  }
277
396
  }
278
397
 
279
- /*
280
- * @overload initialize(d = DHeap::DEFAULT_D)
281
- * Initialize a _d_-ary min-heap.
282
- *
283
- * @param d [Integer] maximum number of children per parent
284
- */
285
- static VALUE
286
- dheap_initialize(int argc, VALUE *argv, VALUE self) {
287
- dheap_t *heap;
288
- int d;
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
+ }
289
410
 
290
- rb_check_arity(argc, 0, 1);
291
- TypedData_Get_Struct(self, dheap_t, &dheap_data_type, heap);
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
+ }
292
420
 
293
- d = argc ? NUM2INT(argv[0]) : DHEAP_DEFAULT_D;
294
- DHEAP_Check_d_size(d);
295
- heap->d = d;
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.");
296
427
 
297
- dheap_set_capa(heap, DHEAP_DEFAULT_SIZE);
428
+ heap->d = dheap_value_to_int_d(d);
429
+ dheap_set_capa(heap, dheap_value_to_long_capa(capa));
298
430
 
299
431
  return self;
300
432
  }
@@ -319,11 +451,17 @@ dheap_initialize_copy(VALUE copy, VALUE orig)
319
451
  return copy;
320
452
  }
321
453
 
454
+ /********************************************************************
455
+ *
456
+ * DHeap sift up/down
457
+ *
458
+ ********************************************************************/
459
+
322
460
  VALUE
323
461
  dheap_sift_up(dheap_t *heap, long index) {
324
462
  ENTRY entry = heap->entries[index];
325
463
 
326
- ASSERT_DHEAP_INDEX(heap, index);
464
+ ASSERT_DHEAP_IDX_OK(heap, index);
327
465
 
328
466
  // sift it up to where it belongs
329
467
  for (long parent_index; 0 < index; index = parent_index) {
@@ -370,7 +508,7 @@ dheap_sift_down(dheap_t *heap, long index, long last_index) {
370
508
 
371
509
  long last_parent = DHEAP_IDX_PARENT(heap, last_index);
372
510
 
373
- ASSERT_DHEAP_INDEX(heap, index);
511
+ ASSERT_DHEAP_IDX_OK(heap, index);
374
512
 
375
513
  // iteratively sift it down to where it belongs
376
514
  while (index <= last_parent) {
@@ -389,6 +527,12 @@ dheap_sift_down(dheap_t *heap, long index, long last_index) {
389
527
  }
390
528
  }
391
529
 
530
+ /********************************************************************
531
+ *
532
+ * DHeap attributes
533
+ *
534
+ ********************************************************************/
535
+
392
536
  /*
393
537
  * @return [Integer] the number of elements in the heap
394
538
  */
@@ -396,8 +540,7 @@ static VALUE
396
540
  dheap_size(VALUE self)
397
541
  {
398
542
  dheap_t *heap = get_dheap_struct(self);
399
- long size = DHEAP_SIZE(heap);
400
- return LONG2NUM(size);
543
+ return LONG2NUM(heap->size);
401
544
  }
402
545
 
403
546
  /*
@@ -407,8 +550,7 @@ static VALUE
407
550
  dheap_empty_p(VALUE self)
408
551
  {
409
552
  dheap_t *heap = get_dheap_struct(self);
410
- long size = DHEAP_SIZE(heap);
411
- return size == 0 ? Qtrue : Qfalse;
553
+ return heap->size ? Qfalse : Qtrue;
412
554
  }
413
555
 
414
556
  /*
@@ -421,16 +563,11 @@ dheap_attr_d(VALUE self)
421
563
  return INT2FIX(heap->d);
422
564
  }
423
565
 
424
- /* @!visibility private */
425
- static VALUE
426
- dheap_init_clone(VALUE clone, VALUE orig, VALUE kwfreeze)
427
- {
428
- dheap_initialize_copy(clone, orig);
429
- if (RTEST(kwfreeze) || (kwfreeze == Qnil && OBJ_FROZEN(orig))) {
430
- rb_funcall(clone, rb_intern("freeze"), 0);
431
- }
432
- return clone;
433
- }
566
+ /********************************************************************
567
+ *
568
+ * DHeap push
569
+ *
570
+ ********************************************************************/
434
571
 
435
572
  static inline void
436
573
  dheap_push_entry(dheap_t *heap, ENTRY *entry) {
@@ -520,47 +657,77 @@ dheap_left_shift(VALUE self, VALUE value) {
520
657
  return self;
521
658
  }
522
659
 
523
- static const ENTRY EmptyDheapEntry;
660
+ /********************************************************************
661
+ *
662
+ * DHeap pop and peek
663
+ *
664
+ ********************************************************************/
524
665
 
525
- static inline VALUE
526
- dheap_pop0(dheap_t *heap)
666
+ static inline void
667
+ dheap_del0(dheap_t *heap)
527
668
  {
528
- VALUE popped = DHEAP_VALUE(heap, 0);
529
669
  if (0 < --heap->size) {
530
670
  heap->entries[0] = heap->entries[heap->size];
531
671
  heap->entries[heap->size] = EmptyDheapEntry; // unnecessary to zero?
532
672
  dheap_sift_down(heap, 0, heap->size - 1);
533
673
  }
674
+ }
675
+
676
+ static inline VALUE
677
+ dheap_pop0(dheap_t *heap)
678
+ {
679
+ VALUE popped = DHEAP_VALUE(heap, 0);
680
+ dheap_del0(heap);
534
681
  return popped;
535
682
  }
536
683
 
537
684
  /*
538
- * Clears all values from the heap, leaving it empty.
685
+ * Returns the next value on the heap, and its score, without popping it
539
686
  *
540
- * @return [self]
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
541
692
  */
542
693
  static VALUE
543
- dheap_clear(VALUE self) {
694
+ dheap_peek_with_score(VALUE self)
695
+ {
544
696
  dheap_t *heap = get_dheap_struct(self);
545
- rb_check_frozen(self);
546
- if (0 < DHEAP_SIZE(heap)) {
547
- heap->size = 0;
548
- }
549
- return self;
697
+ return DHEAP_ENTRY_ARY(heap, 0);
698
+ }
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
+ */
709
+ static VALUE
710
+ dheap_peek_score(VALUE self)
711
+ {
712
+ dheap_t *heap = get_dheap_struct(self);
713
+ if (DHEAP_IDX_LAST(heap) < 0) return Qnil;
714
+ return SCORE2NUM(DHEAP_SCORE(heap, 0));
550
715
  }
551
716
 
552
717
  /*
553
718
  * Returns the next value on the heap to be popped without popping it.
554
719
  *
555
720
  * Time complexity: <b>O(1)</b> <i>(worst-case)</i>
556
- * @return [Object] the next value to be popped without popping it.
721
+ * @return [nil, Object] the next value to be popped without popping it.
722
+ *
723
+ * @see #peek_score
724
+ * @see #peek_with_score
557
725
  */
558
726
  static VALUE
559
727
  dheap_peek(VALUE self)
560
728
  {
561
729
  dheap_t *heap = get_dheap_struct(self);
562
- if (DHEAP_IDX_LAST(heap) < 0) return Qnil;
563
- return DHEAP_VALUE(heap, 0);
730
+ return heap->size ? DHEAP_VALUE(heap, 0) : Qnil;
564
731
  }
565
732
 
566
733
  /*
@@ -573,14 +740,34 @@ dheap_peek(VALUE self)
573
740
  * @see #peek
574
741
  * @see #pop_lt
575
742
  * @see #pop_lte
743
+ * @see #pop_with_score
576
744
  */
577
745
  static VALUE
578
746
  dheap_pop(VALUE self)
579
747
  {
580
748
  dheap_t *heap = get_dheap_struct(self);
581
749
  rb_check_frozen(self);
582
- if (DHEAP_SIZE(heap) <= 0) return Qnil;
583
- return dheap_pop0(heap);
750
+ return heap->size ? dheap_pop0(heap) : Qnil;
751
+ }
752
+
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
+ */
763
+ static VALUE
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;
584
771
  }
585
772
 
586
773
  /*
@@ -595,13 +782,14 @@ dheap_pop(VALUE self)
595
782
  * @see #peek
596
783
  * @see #pop
597
784
  * @see #pop_lt
785
+ * @see #pop_all_below
598
786
  */
599
787
  static VALUE
600
788
  dheap_pop_lte(VALUE self, VALUE max_score)
601
789
  {
602
790
  dheap_t *heap = get_dheap_struct(self);
603
791
  rb_check_frozen(self);
604
- if (DHEAP_SIZE(heap) <= 0) return Qnil;
792
+ if (heap->size <= 0) return Qnil;
605
793
  if (!CMP_LTE(DHEAP_SCORE(heap, 0), VAL2SCORE(max_score))) return Qnil;
606
794
  return dheap_pop0(heap);
607
795
  }
@@ -618,32 +806,132 @@ dheap_pop_lte(VALUE self, VALUE max_score)
618
806
  * @see #peek
619
807
  * @see #pop
620
808
  * @see #pop_lte
809
+ * @see #pop_all_below
621
810
  */
622
811
  static VALUE
623
812
  dheap_pop_lt(VALUE self, VALUE max_score)
624
813
  {
625
814
  dheap_t *heap = get_dheap_struct(self);
626
815
  rb_check_frozen(self);
627
- if (DHEAP_SIZE(heap) <= 0) return Qnil;
816
+ if (heap->size <= 0) return Qnil;
628
817
  if (!CMP_LT(DHEAP_SCORE(heap, 0), VAL2SCORE(max_score))) return Qnil;
629
818
  return dheap_pop0(heap);
630
819
  }
631
820
 
821
+ #define DHEAP_PEEK_LT_P(heap, max_score) \
822
+ (heap->size && CMP_LT(DHEAP_SCORE(heap, 0), max_score))
823
+
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;
838
+ }
839
+
840
+ /*
841
+ * @overload pop_all_below(max_score, receiver = [])
842
+ *
843
+ * Pops all value with score less than max score.
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
855
+ */
856
+ static VALUE
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
+ }
872
+
873
+ /********************************************************************
874
+ *
875
+ * DHeap, misc methods
876
+ *
877
+ ********************************************************************/
878
+
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;
893
+ }
894
+
895
+ /********************************************************************
896
+ *
897
+ * DHeap setup
898
+ *
899
+ ********************************************************************/
900
+
632
901
  void
633
902
  Init_d_heap(void)
634
903
  {
904
+ VALUE rb_cDHeap = rb_define_class("DHeap", rb_cObject);
905
+
635
906
  id_cmp = rb_intern_const("<=>");
636
907
  id_abs = rb_intern_const("abs");
908
+ id_lshift = rb_intern_const("<<");
909
+ id_unary_minus = rb_intern_const("-@");
637
910
 
638
- rb_cDHeap = rb_define_class("DHeap", rb_cObject);
639
911
  rb_define_alloc_func(rb_cDHeap, dheap_s_alloc);
640
912
 
641
- rb_define_const(rb_cDHeap, "MAX_D", INT2NUM(DHEAP_MAX_D));
642
- rb_define_const(rb_cDHeap, "DEFAULT_D", INT2NUM(DHEAP_DEFAULT_D));
643
-
644
- rb_define_method(rb_cDHeap, "initialize", dheap_initialize, -1);
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);
645
934
  rb_define_method(rb_cDHeap, "initialize_copy", dheap_initialize_copy, 1);
646
- rb_define_private_method(rb_cDHeap, "__init_clone__", dheap_init_clone, 2);
647
935
 
648
936
  rb_define_method(rb_cDHeap, "d", dheap_attr_d, 0);
649
937
  rb_define_method(rb_cDHeap, "size", dheap_size, 0);
@@ -655,6 +943,13 @@ Init_d_heap(void)
655
943
  rb_define_method(rb_cDHeap, "push", dheap_push, -1);
656
944
  rb_define_method(rb_cDHeap, "<<", dheap_left_shift, 1);
657
945
  rb_define_method(rb_cDHeap, "pop", dheap_pop, 0);
658
- rb_define_method(rb_cDHeap, "pop_lt", dheap_pop_lt, 1);
659
- 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
+
660
955
  }