d_heap 0.2.2 → 0.3.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: f549e01dd83eb6b48c1190443495a628ed1dd64ead7eb94851e661aef2607e14
4
- data.tar.gz: 492ce5c17ace9ecc9deaccf8fcd47883835da28d4e5f32328aef60989186b2ff
3
+ metadata.gz: 5a20fe814944fa8945bc2cc0ce87f810c8bf8d21102b8c454ae5d639f0548576
4
+ data.tar.gz: 65a1af345ae84c7f6e5da8af89a00730ffc4cbdb6bb2cac59461c3728eb0ad28
5
5
  SHA512:
6
- metadata.gz: 85521dee7f2a9992980935756571e87afbe8ae13347b5b3fbad17b501b5709111972b98ad0c9e1fca6d318c4be20ce2983086dfd84f7c0e73636ac9e4f11f253
7
- data.tar.gz: e1daac5b02fcc817b3c6c6a99395e3ca0b92f42bb14bd813fedbb3037eed698bda335c2898d5b0131b48fecd73e4ccf1615943a52287c36f73764b08bf8b1969
6
+ metadata.gz: 27c240634013397033925ee258de08135587c31c7a046a902c028842680dadec20e2ccc0f76570e6f7db34d2292e5dae2ae7d17449436f890498697de4352c91
7
+ data.tar.gz: 8eb2cba120747cc7788c42f264d2109ef1afee8a28caa7effb8bcb1ff36ed7c99a0556b48b7d9e9479f6e8be8945eea3bf1ddb7f608c13c63252684643154179
@@ -44,6 +44,7 @@ Layout/EmptyLineBetweenDefs:
44
44
  Layout/EmptyLinesAroundAttributeAccessor:
45
45
  inherit_mode:
46
46
  merge:
47
+ - Exclude
47
48
  - AllowedMethods
48
49
  Enabled: true
49
50
  AllowedMethods:
@@ -139,8 +140,17 @@ Style/TernaryParentheses:
139
140
  Enabled: false
140
141
 
141
142
  Style/BlockDelimiters:
143
+ inherit_mode:
144
+ merge:
145
+ - Exclude
146
+ - ProceduralMethods
147
+ - IgnoredMethods
148
+ - FunctionalMethods
142
149
  EnforcedStyle: semantic
143
150
  AllowBracesOnProceduralOneLiners: true
151
+ IgnoredMethods:
152
+ - expect
153
+
144
154
 
145
155
  Style/FormatString:
146
156
  EnforcedStyle: percent
@@ -1,12 +1,15 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- d_heap (0.2.2)
4
+ d_heap (0.3.0)
5
5
 
6
6
  GEM
7
7
  remote: https://rubygems.org/
8
8
  specs:
9
9
  ast (2.4.1)
10
+ benchmark-malloc (0.2.0)
11
+ benchmark-perf (0.6.0)
12
+ benchmark-trend (0.4.0)
10
13
  diff-lcs (1.4.4)
11
14
  parallel (1.19.2)
12
15
  parser (2.7.2.0)
@@ -21,6 +24,11 @@ GEM
21
24
  rspec-core (~> 3.10.0)
22
25
  rspec-expectations (~> 3.10.0)
23
26
  rspec-mocks (~> 3.10.0)
27
+ rspec-benchmark (0.6.0)
28
+ benchmark-malloc (~> 0.2)
29
+ benchmark-perf (~> 0.6)
30
+ benchmark-trend (~> 0.4)
31
+ rspec (>= 3.0)
24
32
  rspec-core (3.10.0)
25
33
  rspec-support (~> 3.10.0)
26
34
  rspec-expectations (3.10.0)
@@ -52,6 +60,7 @@ DEPENDENCIES
52
60
  rake (~> 13.0)
53
61
  rake-compiler
54
62
  rspec (~> 3.10)
63
+ rspec-benchmark
55
64
  rubocop (~> 1.0)
56
65
 
57
66
  BUNDLED WITH
data/README.md CHANGED
@@ -14,6 +14,48 @@ worst-case time complexity. In the worst case, a _d_-ary heap requires only
14
14
  Although you should probably just stick with the default _d_ value of `4`, it
15
15
  may be worthwhile to benchmark your specific scenario.
16
16
 
17
+ ## Usage
18
+
19
+ The simplest way to use it is simply with `#push` and `#pop`. Push takes a
20
+ score and a value, and pop returns the value with the current minimum score.
21
+
22
+ ```ruby
23
+ require "d_heap"
24
+
25
+ heap = DHeap.new # defaults to a 4-ary heap
26
+
27
+ # storing [score, value] tuples
28
+ heap.push Time.now + 5*60, Task.new(1)
29
+ heap.push Time.now + 30, Task.new(2)
30
+ heap.push Time.now + 60, Task.new(3)
31
+ heap.push Time.now + 5, Task.new(4)
32
+
33
+ # peeking and popping (using last to get the task and ignore the time)
34
+ heap.pop.last # => Task[4]
35
+ heap.pop.last # => Task[2]
36
+ heap.peak.last # => Task[3], but don't pop it
37
+ heap.pop.last # => Task[3]
38
+ heap.pop.last # => Task[1]
39
+ ```
40
+
41
+ Read the `rdoc` for more detailed documentation and examples.
42
+
43
+ ## Installation
44
+
45
+ Add this line to your application's Gemfile:
46
+
47
+ ```ruby
48
+ gem 'd_heap'
49
+ ```
50
+
51
+ And then execute:
52
+
53
+ $ bundle install
54
+
55
+ Or install it yourself as:
56
+
57
+ $ gem install d_heap
58
+
17
59
  ## Motivation
18
60
 
19
61
  Sometimes you just need a priority queue, right? With a regular queue, you
@@ -49,47 +91,6 @@ unnecessary. This is definitely hotspot code, and the basic ruby implementation
49
91
  would work fine, if not for that `<=>` overhead. Until then... this gem gets
50
92
  the job done.
51
93
 
52
- ## Installation
53
-
54
- Add this line to your application's Gemfile:
55
-
56
- ```ruby
57
- gem 'd_heap'
58
- ```
59
-
60
- And then execute:
61
-
62
- $ bundle install
63
-
64
- Or install it yourself as:
65
-
66
- $ gem install d_heap
67
-
68
- ## Usage
69
-
70
- The simplest way to use it is simply with `#push` and `#pop`. Push will
71
-
72
- ```ruby
73
- require "d_heap"
74
-
75
- heap = DHeap.new # defaults to a 4-ary heap
76
-
77
- # storing [time, task] tuples
78
- heap << [Time.now + 5*60, Task.new(1)]
79
- heap << [Time.now + 30, Task.new(2)]
80
- heap << [Time.now + 60, Task.new(3)]
81
- heap << [Time.now + 5, Task.new(4)]
82
-
83
- # peeking and popping (using last to get the task and ignore the time)
84
- heap.pop.last # => Task[4]
85
- heap.pop.last # => Task[2]
86
- heap.peak.last # => Task[3]
87
- heap.pop.last # => Task[3]
88
- heap.pop.last # => Task[1]
89
- ```
90
-
91
- Read the `rdoc` for more detailed documentation and examples.
92
-
93
94
  ## TODOs...
94
95
 
95
96
  _TODO:_ In addition to a basic _d_-ary heap class (`DHeap`), this library
@@ -30,4 +30,6 @@ Gem::Specification.new do |spec|
30
30
  spec.executables = spec.files.grep(%r{^exe/}) {|f| File.basename(f) }
31
31
  spec.require_paths = ["lib"]
32
32
  spec.extensions = ["ext/d_heap/extconf.rb"]
33
+
34
+ spec.add_development_dependency "rspec-benchmark"
33
35
  end
@@ -1,27 +1,29 @@
1
1
  #include "d_heap.h"
2
2
 
3
- ID id_ivar_a;
3
+ ID id_cmp; // <=>
4
+ ID id_ivar_values;
5
+ ID id_ivar_scores;
4
6
  ID id_ivar_d;
5
7
 
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) \
8
+ #define Get_DHeap(hobj, hstruct) ((hstruct) = get_dheap_struct(hobj))
9
+
10
+ #define DHEAP_IDX_LAST(heap) (DHEAP_SIZE(heap) - 1)
11
+ #define DHEAP_IDX_PARENT(heap, idx) ((idx - 1) / heap->d)
12
+ #define DHEAP_IDX_CHILD0(heap, idx) ((idx * heap->d) + 1)
13
+
14
+ #define DHEAP_SIZE(heap) (RARRAY_LEN((heap)->scores))
15
+ #define DHEAP_VALUE(heap, idx) RARRAY_AREF((heap)->values, idx)
16
+ #define DHEAP_SCORE(heap, idx) RARRAY_AREF((heap)->scores, idx)
17
+ #define DHEAP_ASSIGN(heap, idx, scr, val) \
18
+ rb_ary_store(heap->scores, idx, scr); \
19
+ rb_ary_store(heap->values, idx, val);
20
+ #define DHEAP_APPEND(heap, scr, val) \
21
+ rb_ary_push((heap)->scores, scr); \
22
+ rb_ary_push((heap)->values, val);
23
+ #define DHEAP_DROP_LAST(heap) ( \
24
+ rb_ary_pop(heap->scores), \
25
+ rb_ary_pop(heap->values) \
22
26
  ) // score, value
23
- #define IDX_PARENT(idx) ((idx - 1) / d)
24
- #define IDX_CHILD0(idx) ((idx * d) + 1)
25
27
 
26
28
  #define DHEAP_Check_d_size(d) \
27
29
  if (d < 2) { \
@@ -31,64 +33,219 @@ ID id_ivar_d;
31
33
  rb_raise(rb_eArgError, "DHeap d=%d is too large", d); \
32
34
  }
33
35
 
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); \
36
+ #define DHEAP_Check_Index(index, last_index) \
37
+ if (index < 0) { \
38
+ rb_raise(rb_eIndexError, "DHeap index %ld too small", index); \
37
39
  } \
38
- else if (last_index < sift_index) { \
39
- rb_raise(rb_eIndexError, "sift_index %ld too large", sift_index); \
40
+ else if (last_index < index) { \
41
+ rb_raise(rb_eIndexError, "DHeap index %ld too large", index); \
40
42
  }
41
43
 
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);
44
+ struct dheap_struct {
45
+ int d;
46
+ VALUE scores;
47
+ VALUE values;
48
+ };
49
+ typedef struct dheap_struct dheap_t;
50
+
51
+ static void
52
+ dheap_compact(void *ptr)
53
+ {
54
+ dheap_t *heap = ptr;
55
+ if (heap->scores) dheap_gc_location( heap->scores );
56
+ if (heap->values) dheap_gc_location( heap->values );
57
+ }
58
+
59
+ static void
60
+ dheap_mark(void *ptr)
61
+ {
62
+ dheap_t *heap = ptr;
63
+ if (heap->scores) rb_gc_mark_movable(heap->scores);
64
+ if (heap->values) rb_gc_mark_movable(heap->values);
65
+ }
66
+
67
+ static size_t
68
+ dheap_memsize(const void *ptr)
69
+ {
70
+ const dheap_t *heap = ptr;
71
+ size_t size = sizeof(*heap);
72
+ return size;
73
+ }
74
+
75
+ static const rb_data_type_t dheap_data_type = {
76
+ "DHeap",
77
+ {
78
+ (void (*)(void*))dheap_mark,
79
+ (void (*)(void*))RUBY_DEFAULT_FREE,
80
+ (size_t (*)(const void *))dheap_memsize,
81
+ dheap_compact_callback(dheap_compact),
82
+ },
83
+ 0, 0,
84
+ RUBY_TYPED_FREE_IMMEDIATELY,
85
+ };
86
+
87
+ static VALUE
88
+ dheap_s_alloc(VALUE klass)
89
+ {
90
+ VALUE obj;
91
+ dheap_t *heap;
92
+
93
+ obj = TypedData_Make_Struct(klass, dheap_t, &dheap_data_type, heap);
94
+ heap->d = DHEAP_DEFAULT_D;
95
+ heap->scores = Qnil;
96
+ heap->values = Qnil;
97
+
98
+ return obj;
99
+ }
100
+
101
+ static inline dheap_t *
102
+ get_dheap_struct(VALUE self)
103
+ {
104
+ dheap_t *heap;
105
+ TypedData_Get_Struct(self, dheap_t, &dheap_data_type, heap);
106
+ Check_Type(heap->scores, T_ARRAY);
107
+ return heap;
108
+ }
109
+
110
+ /*
111
+ * @overload initialize(d = DHeap::DEFAULT_D)
112
+ * Initialize a _d_-ary min-heap.
113
+ *
114
+ * @param d [Integer] maximum number of children per parent
115
+ */
116
+ static VALUE
117
+ dheap_initialize(int argc, VALUE *argv, VALUE self) {
118
+ rb_check_arity(argc, 0, 1);
119
+ dheap_t *heap;
120
+ TypedData_Get_Struct(self, dheap_t, &dheap_data_type, heap);
121
+
122
+ int d = DHEAP_DEFAULT_D;
123
+ if (argc) {
124
+ d = NUM2INT(argv[0]);
125
+ }
126
+ DHEAP_Check_d_size(d);
127
+ heap->d = d;
128
+
129
+ heap->scores = rb_ary_new_capa(10000);
130
+ heap->values = rb_ary_new_capa(10000);
131
+
132
+ return self;
133
+ }
134
+
135
+ /*
136
+ static inline VALUE
137
+ make_dheap_element(VALUE score, VALUE value)
138
+ {
139
+ elem_t *elem;
140
+ VALUE obj = TypedData_Make_Struct(
141
+ rb_cObject,
142
+ elem_t,
143
+ &dheap_elem_type,
144
+ elem);
145
+ elem->score = score;
146
+ elem->value = value;
147
+ return obj;
148
+ }
149
+
150
+ #define IsDHeapElem(value) rb_typeddata_is_kind_of((value), &dheap_elem_type)
151
+ #define Get_DHeap_Elem(value) \
152
+ TypedData_Get_Struct((obj), elem_t, &dheap_elem_type, (elem))
153
+
154
+ static inline elem_t *
155
+ get_dheap_element(VALUE obj)
156
+ {
157
+ elem_t *elem;
158
+ TypedData_Get_Struct((obj), elem_t, &dheap_elem_type, (elem));
159
+ return elem;
160
+ }
161
+
162
+ */
163
+
164
+ #define CMP_LT(a, b) (optimized_cmp(a, b) < 0)
165
+ #define CMP_LTE(a, b) (optimized_cmp(a, b) <= 0)
166
+ #define CMP_GT(a, b) (optimized_cmp(a, b) > 0)
167
+ #define CMP_GTE(a, b) (optimized_cmp(a, b) >= 0)
168
+
169
+ /*
170
+ * short-circuit evaluation for a few basic types.
171
+ *
172
+ * Only Integer, Float, and String are optimized,
173
+ * and only when both arguments are the same type.
174
+ */
175
+ static inline int
176
+ optimized_cmp(VALUE a, VALUE b) {
177
+ if (a == b) // Fixnum equality and object equality
178
+ return 0;
179
+ if (FIXNUM_P(a) && FIXNUM_P(b))
180
+ return (FIX2LONG(a) < FIX2LONG(b)) ? -1 : 1;
181
+ if (RB_FLOAT_TYPE_P(a) && RB_FLOAT_TYPE_P(b))
182
+ {
183
+ double x, y;
184
+ x = RFLOAT_VALUE(a);
185
+ y = RFLOAT_VALUE(b);
186
+ if (isnan(x) || isnan(y)) rb_cmperr(a, b); // raise ArgumentError
187
+ return (x < y) ? -1 : ((x == y) ? 0 : 1);
188
+ }
189
+ if (RB_TYPE_P(a, T_BIGNUM) && RB_TYPE_P(b, T_BIGNUM))
190
+ return FIX2INT(rb_big_cmp(a, b));
191
+ if (STRING_P(a) && STRING_P(b))
192
+ return rb_str_cmp(a, b);
193
+
194
+ // give up on an optimized version and just call (a <=> b)
195
+ return rb_cmpint(rb_funcallv(a, id_cmp, 1, &b), a, b);
196
+ }
50
197
 
51
198
  VALUE
52
- dheap_ary_sift_up(VALUE heap_array, int d, long sift_index) {
53
- DHEAP_Check_Sift_Args(heap_array, d, sift_index);
199
+ dheap_ary_sift_up(dheap_t *heap, long sift_index) {
200
+ long last_index = DHEAP_IDX_LAST(heap);
201
+ DHEAP_Check_Index(sift_index, last_index);
202
+
203
+ VALUE sift_value = DHEAP_VALUE(heap, sift_index);
204
+ VALUE sift_score = DHEAP_SCORE(heap, sift_index);
205
+
54
206
  // sift it up to where it belongs
55
207
  for (long parent_index; 0 < sift_index; sift_index = parent_index) {
56
- debug(rb_sprintf("sift up(%"PRIsVALUE", %d, %ld)", heap_array, d, sift_index));
57
- parent_index = IDX_PARENT(sift_index);
58
- VALUE parent_score = DHEAP_SCORE(heap_array, parent_index);
208
+ debug(rb_sprintf("sift up(%"PRIsVALUE", %d, %ld)", heap->values, heap->d, sift_index));
209
+ parent_index = DHEAP_IDX_PARENT(heap, sift_index);
210
+ VALUE parent_score = DHEAP_SCORE(heap, parent_index);
59
211
 
60
212
  // parent is smaller: heap is restored
61
213
  if (CMP_LTE(parent_score, sift_score)) break;
62
214
 
63
215
  // parent is larger: swap and continue sifting up
64
- VALUE parent_value = DHEAP_VALUE(heap_array, parent_index);
65
- DHEAP_ASSIGN(heap_array, sift_index, parent_score, parent_value);
66
- DHEAP_ASSIGN(heap_array, parent_index, sift_score, sift_value);
216
+ VALUE parent_value = DHEAP_VALUE(heap, parent_index);
217
+ DHEAP_ASSIGN(heap, sift_index, parent_score, parent_value);
218
+ DHEAP_ASSIGN(heap, parent_index, sift_score, sift_value);
67
219
  }
68
- debug(rb_sprintf("sifted (%"PRIsVALUE", %d, %ld)", heap_array, d, sift_index));
220
+ debug(rb_sprintf("sifted (%"PRIsVALUE", %d, %ld)", heap->values, heap->d, sift_index));
69
221
  return LONG2NUM(sift_index);
70
222
  }
71
223
 
72
224
  VALUE
73
- dheap_ary_sift_down(VALUE heap_array, int d, long sift_index) {
74
- DHEAP_Check_Sift_Args(heap_array, d, sift_index);
225
+ dheap_ary_sift_down(dheap_t *heap, long sift_index) {
226
+ long last_index = DHEAP_IDX_LAST(heap);
227
+ DHEAP_Check_Index(sift_index, last_index);
228
+
229
+ VALUE sift_value = DHEAP_VALUE(heap, sift_index);
230
+ VALUE sift_score = DHEAP_SCORE(heap, sift_index);
75
231
 
76
232
  // iteratively sift it down to where it belongs
77
233
  for (long child_index; sift_index < last_index; sift_index = child_index) {
78
- debug(rb_sprintf("sift dn(%"PRIsVALUE", %d, %ld)", heap_array, d, sift_index));
234
+ debug(rb_sprintf("sift dn(%"PRIsVALUE", %d, %ld)", heap->values, heap->d, sift_index));
79
235
  // find first child index, and break if we've reached the last layer
80
- long child_idx0 = child_index = IDX_CHILD0(sift_index);
236
+ long child_idx0 = child_index = DHEAP_IDX_CHILD0(heap, sift_index);
81
237
  if (last_index < child_idx0) break;
82
238
 
83
239
  // find the min child (and its child_index)
84
240
  // requires "d" comparisons to find min child and compare to sift_score
85
- VALUE child_score = DHEAP_SCORE(heap_array, child_idx0);
241
+ long last_sibidx = child_idx0 + heap->d - 1;
242
+ if (last_index < last_sibidx) last_sibidx = last_index;
243
+ VALUE child_score = DHEAP_SCORE(heap, child_idx0);
86
244
  child_index = child_idx0;
87
- for (int i = 1; i < d; ++i) {
88
- long sibling_index = child_idx0 + i;
89
- if (last_index < sibling_index) break;
90
-
91
- VALUE sibling_score = DHEAP_SCORE(heap_array, sibling_index);
245
+ for (long sibling_index = child_idx0 + 1;
246
+ sibling_index <= last_sibidx;
247
+ ++sibling_index) {
248
+ VALUE sibling_score = DHEAP_SCORE(heap, sibling_index);
92
249
 
93
250
  if (CMP_LT(sibling_score, child_score)) {
94
251
  child_score = sibling_score;
@@ -100,106 +257,45 @@ dheap_ary_sift_down(VALUE heap_array, int d, long sift_index) {
100
257
  if (CMP_LTE(sift_score, child_score)) break;
101
258
 
102
259
  // child is smaller: swap and continue sifting down
103
- VALUE child_value = DHEAP_VALUE(heap_array, child_index);
104
- DHEAP_ASSIGN(heap_array, sift_index, child_score, child_value);
105
- DHEAP_ASSIGN(heap_array, child_index, sift_score, sift_value);
260
+ VALUE child_value = DHEAP_VALUE(heap, child_index);
261
+ DHEAP_ASSIGN(heap, sift_index, child_score, child_value);
262
+ DHEAP_ASSIGN(heap, child_index, sift_score, sift_value);
106
263
  }
107
- debug(rb_sprintf("sifted (%"PRIsVALUE", %d, %ld)", heap_array, d, sift_index));
264
+ debug(rb_sprintf("sifted (%"PRIsVALUE", %d, %ld)", heap->values, heap->d, sift_index));
108
265
  return LONG2NUM(sift_index);
109
266
  }
110
267
 
111
- #define DHEAP_Load_Sift_Vals(heap_array, dval, idxval) \
112
- Check_Type(dval, T_FIXNUM); \
113
- int dint = FIX2INT(dval); \
114
- long idx = NUM2LONG(idxval);
115
-
116
- /*
117
- * Treats a +heap_array+ as a +d+-ary heap and sifts up from +sift_index+ to
118
- * restore the heap property for all nodes between it and the root of the tree.
119
- *
120
- * The array is interpreted as holding two entries for each node, a score and a
121
- * value. The scores will held in every even-numbered array index and the
122
- * values in every odd numbered index. The array is flat, not an array of
123
- * length=2 arrays.
124
- *
125
- * Time complexity: <b>O(log n / log d)</b> <i>(worst-case)</i>
126
- *
127
- * @param heap_array [Array] the array which is treated a heap.
128
- * @param d [Integer] the maximum number of children per parent
129
- * @param sift_index [Integer] the index to start sifting from
130
- * @return [Integer] the new index for the object that starts at +sift_index+.
131
- */
132
- static VALUE
133
- dheap_sift_up_s(VALUE unused, VALUE heap_array, VALUE d, VALUE sift_index) {
134
- DHEAP_Load_Sift_Vals(heap_array, d, sift_index);
135
- return dheap_ary_sift_up(heap_array, dint, idx);
136
- }
137
-
138
- /*
139
- * Treats +heap_array+ as a +d+-ary heap and sifts down from +sift_index+ to
140
- * restore the heap property. If all _d_ subtrees below +sift_index+ are already
141
- * heaps, this method ensures the entire subtree rooted at +sift_index+ will be
142
- * a heap.
143
- *
144
- * The array is interpreted as holding two entries for each node, a score and a
145
- * value. The scores will held in every even-numbered array index and the
146
- * values in every odd numbered index. The array is flat, not an array of
147
- * length=2 arrays.
148
- *
149
- * Time complexity: <b>O(d log n / log d)</b> <i>(worst-case)</i>
150
- *
151
- * @param heap_array [Array] the array which is treated a heap.
152
- * @param d [Integer] the maximum number of children per parent
153
- * @param sift_index [Integer] the index to start sifting down from
154
- * @return [Integer] the new index for the object that starts at +sift_index+.
155
- */
156
- static VALUE
157
- dheap_sift_down_s(VALUE unused, VALUE heap_array, VALUE d, VALUE sift_index) {
158
- DHEAP_Load_Sift_Vals(heap_array, d, sift_index);
159
- return dheap_ary_sift_down(heap_array, dint, idx);
160
- }
161
-
162
- /*
163
- * @overload initialize(d = DHeap::DEFAULT_D)
164
- * Initialize a _d_-ary min-heap.
165
- *
166
- * @param d [Integer] maximum number of children per parent
167
- */
168
- static VALUE
169
- dheap_initialize(int argc, VALUE *argv, VALUE self) {
170
- rb_check_arity(argc, 0, 1);
171
- int d = DHEAP_DEFAULT_D;
172
- if (argc) {
173
- d = NUM2INT(argv[0]);
174
- }
175
- DHEAP_Check_d_size(d);
176
- rb_ivar_set(self, id_ivar_d, INT2FIX(d));
177
- rb_ivar_set(self, id_ivar_a, rb_ary_new());
178
- return self;
179
- }
180
-
181
268
  /*
182
269
  * @return [Integer] the number of elements in the heap
183
270
  */
184
- static VALUE dheap_size(VALUE self) {
185
- VALUE ary = DHEAP_GET_A(self);
186
- long size = DHEAP_SIZE(ary);
271
+ static VALUE
272
+ dheap_size(VALUE self)
273
+ {
274
+ dheap_t *heap = get_dheap_struct(self);
275
+ long size = DHEAP_SIZE(heap);
187
276
  return LONG2NUM(size);
188
277
  }
189
278
 
190
279
  /*
191
280
  * @return [Boolean] is the heap empty?
192
281
  */
193
- static VALUE dheap_empty_p(VALUE self) {
194
- VALUE ary = DHEAP_GET_A(self);
195
- long size = DHEAP_SIZE(ary);
282
+ static VALUE
283
+ dheap_empty_p(VALUE self)
284
+ {
285
+ dheap_t *heap = get_dheap_struct(self);
286
+ long size = DHEAP_SIZE(heap);
196
287
  return size == 0 ? Qtrue : Qfalse;
197
288
  }
198
289
 
199
290
  /*
200
291
  * @return [Integer] the maximum number of children per parent
201
292
  */
202
- static VALUE dheap_attr_d(VALUE self) { return DHEAP_GET_D(self); }
293
+ static VALUE
294
+ dheap_attr_d(VALUE self)
295
+ {
296
+ dheap_t *heap = get_dheap_struct(self);
297
+ return INT2FIX(heap->d);
298
+ }
203
299
 
204
300
  /*
205
301
  * Freezes the heap as well as its underlying array, but does <i>not</i>
@@ -209,20 +305,13 @@ static VALUE dheap_attr_d(VALUE self) { return DHEAP_GET_D(self); }
209
305
  */
210
306
  static VALUE
211
307
  dheap_freeze(VALUE self) {
212
- VALUE ary = DHEAP_GET_A(self);
308
+ dheap_t *heap = get_dheap_struct(self);
213
309
  ID id_freeze = rb_intern("freeze");
214
- rb_funcall(ary, id_freeze, 0);
310
+ rb_funcall(heap->scores, id_freeze, 0);
311
+ rb_funcall(heap->values, id_freeze, 0);
215
312
  return rb_call_super(0, NULL);
216
313
  }
217
314
 
218
- static VALUE
219
- dheap_ary_push(VALUE ary, int d, VALUE val, VALUE scr)
220
- {
221
- DHEAP_APPEND(ary, scr, val);
222
- long last_index = DHEAP_LAST_IDX(ary);
223
- return dheap_ary_sift_up(ary, d, last_index);
224
- }
225
-
226
315
  /*
227
316
  * @overload push(score, value = score)
228
317
  *
@@ -244,11 +333,11 @@ dheap_push(int argc, VALUE *argv, VALUE self) {
244
333
  VALUE scr = argv[0];
245
334
  VALUE val = argc < 2 ? scr : argv[1];
246
335
 
247
- VALUE ary = DHEAP_GET_A(self);
248
- VALUE dval = DHEAP_GET_D(self);
249
- int d = FIX2INT(dval);
336
+ dheap_t *heap = get_dheap_struct(self);
250
337
 
251
- return dheap_ary_push(ary, d, val, scr);
338
+ DHEAP_APPEND(heap, scr, val);
339
+ long last_index = DHEAP_IDX_LAST(heap);
340
+ return dheap_ary_sift_up(heap, last_index);
252
341
  }
253
342
 
254
343
  /*
@@ -267,18 +356,29 @@ dheap_left_shift(VALUE self, VALUE value) {
267
356
  return self;
268
357
  }
269
358
 
270
- #define DHEAP_Pop_Init(self) \
271
- VALUE ary = DHEAP_GET_A(self); \
272
- VALUE dval = DHEAP_GET_D(self); \
273
- long last_index = DHEAP_LAST_IDX(ary); \
274
-
275
- #define DHEAP_Pop_SwapLastAndSiftDown(ary, dval, last_index, sift_value) \
276
- if (last_index == 0) { DHEAP_DROP_LAST(ary); return pop_value; } \
277
- VALUE sift_value = DHEAP_VALUE(ary, last_index); \
278
- VALUE sift_score = DHEAP_SCORE(ary, last_index); \
279
- DHEAP_ASSIGN(ary, 0, sift_score, sift_value); \
280
- DHEAP_DROP_LAST(ary); \
281
- dheap_ary_sift_down(ary, FIX2INT(dval), 0);
359
+ /*
360
+ dheap_t hstruct; \
361
+ Get_DHeap(self, hstruct); \
362
+ VALUE values = hstruct.values; \
363
+ VALUE scores = hstruct.scores; \
364
+ VALUE dval = INT2FIX(hstruct.d); \
365
+ */
366
+
367
+ static inline void
368
+ dheap_pop_swap_last_and_sift_down(dheap_t *heap, long last_index)
369
+ {
370
+ if (last_index == 0) {
371
+ DHEAP_DROP_LAST(heap);
372
+ }
373
+ else
374
+ {
375
+ VALUE sift_value = DHEAP_VALUE(heap, last_index);
376
+ VALUE sift_score = DHEAP_SCORE(heap, last_index);
377
+ DHEAP_ASSIGN(heap, 0, sift_score, sift_value);
378
+ DHEAP_DROP_LAST(heap);
379
+ dheap_ary_sift_down(heap, 0);
380
+ }
381
+ }
282
382
 
283
383
  /*
284
384
  * Returns the next value on the heap to be popped without popping it.
@@ -288,8 +388,9 @@ dheap_left_shift(VALUE self, VALUE value) {
288
388
  */
289
389
  static VALUE
290
390
  dheap_peek(VALUE self) {
291
- VALUE ary = DHEAP_GET_A(self);
292
- return DHEAP_VALUE(ary, 0);
391
+ dheap_t *heap = get_dheap_struct(self);
392
+ if (DHEAP_IDX_LAST(heap) < 0) return Qnil;
393
+ return DHEAP_VALUE(heap, 0);
293
394
  }
294
395
 
295
396
  /*
@@ -299,11 +400,13 @@ dheap_peek(VALUE self) {
299
400
  */
300
401
  static VALUE
301
402
  dheap_pop(VALUE self) {
302
- DHEAP_Pop_Init(self);
303
- if (last_index < 0) return Qnil;
304
- VALUE pop_value = DHEAP_VALUE(ary, 0);
403
+ dheap_t *heap = get_dheap_struct(self);
404
+ long last_index = DHEAP_IDX_LAST(heap);
305
405
 
306
- DHEAP_Pop_SwapLastAndSiftDown(ary, dval, last_index, sift_value);
406
+ if (last_index < 0) return Qnil;
407
+ VALUE pop_value = DHEAP_VALUE(heap, 0);
408
+
409
+ dheap_pop_swap_last_and_sift_down(heap, last_index);
307
410
  return pop_value;
308
411
  }
309
412
 
@@ -316,14 +419,16 @@ dheap_pop(VALUE self) {
316
419
  */
317
420
  static VALUE
318
421
  dheap_pop_lte(VALUE self, VALUE max_score) {
319
- DHEAP_Pop_Init(self);
422
+ dheap_t *heap = get_dheap_struct(self);
423
+ long last_index = DHEAP_IDX_LAST(heap);
424
+
320
425
  if (last_index < 0) return Qnil;
321
- VALUE pop_value = DHEAP_VALUE(ary, 0);
426
+ VALUE pop_value = DHEAP_VALUE(heap, 0);
322
427
 
323
- VALUE pop_score = DHEAP_SCORE(ary, 0);
428
+ VALUE pop_score = DHEAP_SCORE(heap, 0);
324
429
  if (max_score && !CMP_LTE(pop_score, max_score)) return Qnil;
325
430
 
326
- DHEAP_Pop_SwapLastAndSiftDown(ary, dval, last_index, sift_value);
431
+ dheap_pop_swap_last_and_sift_down(heap, last_index);
327
432
  return pop_value;
328
433
  }
329
434
 
@@ -336,14 +441,16 @@ dheap_pop_lte(VALUE self, VALUE max_score) {
336
441
  */
337
442
  static VALUE
338
443
  dheap_pop_lt(VALUE self, VALUE max_score) {
339
- DHEAP_Pop_Init(self);
444
+ dheap_t *heap = get_dheap_struct(self);
445
+ long last_index = DHEAP_IDX_LAST(heap);
446
+
340
447
  if (last_index < 0) return Qnil;
341
- VALUE pop_value = DHEAP_VALUE(ary, 0);
448
+ VALUE pop_value = DHEAP_VALUE(heap, 0);
342
449
 
343
- VALUE pop_score = DHEAP_SCORE(ary, 0);
450
+ VALUE pop_score = DHEAP_SCORE(heap, 0);
344
451
  if (max_score && !CMP_LT(pop_score, max_score)) return Qnil;
345
452
 
346
- DHEAP_Pop_SwapLastAndSiftDown(ary, dval, last_index, sift_value);
453
+ dheap_pop_swap_last_and_sift_down(heap, last_index);
347
454
  return pop_value;
348
455
  }
349
456
 
@@ -351,16 +458,16 @@ void
351
458
  Init_d_heap(void)
352
459
  {
353
460
  id_cmp = rb_intern_const("<=>");
354
- id_ivar_a = rb_intern_const("ary");
461
+ id_ivar_values = rb_intern_const("values");
462
+ id_ivar_scores = rb_intern_const("scores");
355
463
  id_ivar_d = rb_intern_const("d");
356
464
 
357
465
  rb_cDHeap = rb_define_class("DHeap", rb_cObject);
466
+ rb_define_alloc_func(rb_cDHeap, dheap_s_alloc);
467
+
358
468
  rb_define_const(rb_cDHeap, "MAX_D", INT2NUM(DHEAP_MAX_D));
359
469
  rb_define_const(rb_cDHeap, "DEFAULT_D", INT2NUM(DHEAP_DEFAULT_D));
360
470
 
361
- rb_define_singleton_method(rb_cDHeap, "heap_sift_down", dheap_sift_down_s, 3);
362
- rb_define_singleton_method(rb_cDHeap, "heap_sift_up", dheap_sift_up_s, 3);
363
-
364
471
  rb_define_method(rb_cDHeap, "initialize", dheap_initialize, -1);
365
472
  rb_define_method(rb_cDHeap, "d", dheap_attr_d, 0);
366
473
  rb_define_method(rb_cDHeap, "freeze", dheap_freeze, 0);
@@ -13,46 +13,22 @@
13
13
 
14
14
  VALUE rb_cDHeap;
15
15
 
16
- #define CMP_LT(a, b) (optimized_cmp(a, b) < 0)
17
- #define CMP_LTE(a, b) (optimized_cmp(a, b) <= 0)
18
- #define CMP_GT(a, b) (optimized_cmp(a, b) > 0)
19
- #define CMP_GTE(a, b) (optimized_cmp(a, b) >= 0)
16
+ // copied from pg gem
20
17
 
21
- // <=>
22
- ID id_cmp;
18
+ #define UNUSED(x) ((void)(x))
19
+
20
+ #ifdef HAVE_RB_GC_MARK_MOVABLE
21
+ #define dheap_compact_callback(x) ((void (*)(void*))(x))
22
+ #define dheap_gc_location(x) x = rb_gc_location(x)
23
+ #else
24
+ #define rb_gc_mark_movable(x) rb_gc_mark(x)
25
+ #define dheap_compact_callback(x) {(x)}
26
+ #define dheap_gc_location(x) UNUSED(x)
27
+ #endif
23
28
 
24
29
  // from internal/compar.h
25
30
  #define STRING_P(s) (RB_TYPE_P((s), T_STRING) && CLASS_OF(s) == rb_cString)
26
31
 
27
- /*
28
- * short-circuit evaluation for a few basic types.
29
- *
30
- * Only Integer, Float, and String are optimized,
31
- * and only when both arguments are the same type.
32
- */
33
- static inline int
34
- optimized_cmp(VALUE a, VALUE b) {
35
- if (a == b) // Fixnum equality and object equality
36
- return 0;
37
- if (FIXNUM_P(a) && FIXNUM_P(b))
38
- return (FIX2LONG(a) < FIX2LONG(b)) ? -1 : 1;
39
- if (RB_FLOAT_TYPE_P(a) && RB_FLOAT_TYPE_P(b))
40
- {
41
- double x, y;
42
- x = RFLOAT_VALUE(a);
43
- y = RFLOAT_VALUE(b);
44
- if (isnan(x) || isnan(y)) rb_cmperr(a, b); // raise ArgumentError
45
- return (x < y) ? -1 : ((x == y) ? 0 : 1);
46
- }
47
- if (RB_TYPE_P(a, T_BIGNUM) && RB_TYPE_P(b, T_BIGNUM))
48
- return FIX2INT(rb_big_cmp(a, b));
49
- if (STRING_P(a) && STRING_P(b))
50
- return rb_str_cmp(a, b);
51
-
52
- // give up on an optimized version and just call (a <=> b)
53
- return rb_cmpint(rb_funcallv(a, id_cmp, 1, &b), a, b);
54
- }
55
-
56
32
  #ifdef __D_HEAP_DEBUG
57
33
  #define debug(v) { \
58
34
  ID sym_puts = rb_intern("puts"); \
@@ -6,4 +6,6 @@ require "mkmf"
6
6
  # $CFLAGS << " -D__D_HEAP_DEBUG"
7
7
  # end
8
8
 
9
+ have_func "rb_gc_mark_movable" # since ruby-2.7
10
+
9
11
  create_makefile("d_heap/d_heap")
@@ -1,6 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  class DHeap
4
- VERSION = "0.2.2"
4
+ VERSION = "0.3.0"
5
5
 
6
6
  end
metadata CHANGED
@@ -1,15 +1,29 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: d_heap
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.2.2
4
+ version: 0.3.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - nicholas a. evans
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2020-12-27 00:00:00.000000000 Z
12
- dependencies: []
11
+ date: 2020-12-29 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: rspec-benchmark
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ">="
18
+ - !ruby/object:Gem::Version
19
+ version: '0'
20
+ type: :development
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - ">="
25
+ - !ruby/object:Gem::Version
26
+ version: '0'
13
27
  description: |
14
28
  A C extension implementation of a d-ary heap data structure, suitable for
15
29
  use in e.g. priority queues or Djikstra's algorithm.