data_structures_rmolinari 0.5.0 → 0.5.2

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: 7682f6d3b0779f347ce0797f55f33b9d7dcc7bd9c2039fc2fd6f865eb72e085a
4
- data.tar.gz: d717e5e36f79ddc4ecb605a59b475b7114359dea7476445590deb300f7915bd4
3
+ metadata.gz: 5d10fb46bf10f119b95239cf8e3ed06585804d37ccb9b7b1da0c7139dfcb31b9
4
+ data.tar.gz: dc393fb3e3f597df278832b3c4faae08866ef93ad747ee0a30fca19345fa6c8a
5
5
  SHA512:
6
- metadata.gz: c3ffd9a4f67f55b7a2df1c949cf2288c06fcae416d5ff03a10307a1b79c3dae1daa74e2576d5e190c989adeea47b046426fad8c3c64199aadf22ba500b317f36
7
- data.tar.gz: 8380d6117f2955da9362395f8315f5121b4f7afba2f69aabb1981a01b675cbbed81d07c10b5745409080c2588c92df3d676ec36efa128571234a74dceef0e20d
6
+ metadata.gz: 6ec16b3eb2a1f4deccf8a8c45cb20c9558a0267049236389de53444124feeefe282751f964f2cf1875e1fccd5fccc6ad5fea4edd0098fed7b358bc6051666b4e
7
+ data.tar.gz: 0435cf6031c7e40bf9706a7c8d7b493e7e31f7667131be682de59ad5dcd26a5151add0d03cee83b23da6fa6f0c132c964ef551341dc94ff5d17cffddc2b48f2a
data/README.md CHANGED
@@ -7,12 +7,11 @@ participating in the Advent of Code (https://adventofcode.com/).
7
7
  The implementations are based on the expository descriptions and pseudo-code I found as I read about each structure and so are not
8
8
  as fast as possible.
9
9
 
10
- The code is available as a gem: https://rubygems.org/gems/data_structures_rmolinari.
10
+ The code lives in the `DataStructuresRMolinari` module to avoid polluting the global namespace.
11
11
 
12
- ## Usage
12
+ It is distributed under the MIT license.
13
13
 
14
- The right way to organize the code is not obvious to me. For now the data structures are all defined in the module
15
- `DataStructuresRMolinari` to avoid polluting the global namespace.
14
+ It is available as a gem: https://rubygems.org/gems/data_structures_rmolinari.
16
15
 
17
16
  # Implementations
18
17
 
@@ -166,13 +165,13 @@ SegmentTree = DataStructuresRMolinari::SegmentTree # namespace module
166
165
 
167
166
  data = [1, -3, 2, 1, 5, -9]
168
167
 
169
- # Get a segment tree instance that will answer "max over this subinterval" questions about data.
168
+ # Get a segment tree instance that will answer "max over this subinterval?" questions about data.
170
169
  # Here we get one using the ruby implementation of the generic functionality.
171
170
  #
172
- # We offer :index_of_max as an alternative to :max. This will construct an instance that answers
173
- # questions of the form "an index of the maximum value over this subinterval".
171
+ # Put :index_of_max in place of :map to get an instance that returns "an index of the maximum value
172
+ # over this subinterval".
174
173
  #
175
- # To use the version written in C, put :c instead of :ruby.
174
+ # To use the generic code written in C, put :c instead of :ruby.
176
175
  seg_tree = SegmentTree.construct(data, :max, :ruby)
177
176
 
178
177
  seg_tree.max_on(0, 2) # => 2
@@ -208,8 +207,10 @@ The implementation uses the remarkable Convenient Containers library from Jackso
208
207
  the pure Ruby `SegmentTreeTemplate` class.
209
208
 
210
209
  A benchmark suggests that a long sequence of `max_on` operations against a max-val Segment Tree is about 4 times as fast with C as
211
- with Ruby. I'm a bit suprised the improvment isn't larger, but remember that the C code must still interact with the Ruby objects in
212
- the underlying data array, and must combine them, etc., via Ruby lambdas.
210
+ with Ruby. The speedup factor is lowered to a little over 2 when the Ruby code is run with YJIT (under Ruby 3.1.3).
211
+
212
+ I'm a bit suprised the improvement isn't larger, but remember that the C code must still interact with the Ruby objects
213
+ in the underlying data array, and must access and combine them via Ruby lambdas.
213
214
 
214
215
  # References
215
216
  - [Allan] Allan, J., _CC: Convenient Containers_, https://github.com/JacksonAllan/CC, (retrieved 2023-02-01).
@@ -69,6 +69,11 @@ typedef struct du_data {
69
69
  size_t subset_count;
70
70
  } disjoint_union_data;
71
71
 
72
+ /************************************************************
73
+ * Memory Management
74
+ *
75
+ */
76
+
72
77
  /*
73
78
  * Create one (on the heap).
74
79
  */
@@ -98,9 +103,76 @@ static void disjoint_union_free(void *ptr) {
98
103
  }
99
104
 
100
105
  /************************************************************
101
- * The disjoint union operations
106
+ * How much memory (roughly) does a disjoint_union_data instance consume?
107
+ *
108
+ * I guess the Ruby runtime can use this information when deciding how agressive to be during garbage collection and such.
109
+ */
110
+ static size_t disjoint_union_memsize(const void *ptr) {
111
+ if (ptr) {
112
+ const disjoint_union_data *du = ptr;
113
+
114
+ // See https://github.com/JacksonAllan/CC/issues/3
115
+ return sizeof( cc_vec_hdr_ty ) + cap( du->pairs ) * CC_EL_SIZE( *(du->pairs) );
116
+ } else {
117
+ return 0;
118
+ }
119
+ }
120
+
121
+ // There is no need for a mark() function as we don't hold any Ruby objects ourselves.
122
+
123
+ /*
124
+ * A configuration struct that tells the Ruby runtime how to deal with a disjoint_union_data object.
125
+ *
126
+ * https://docs.ruby-lang.org/en/master/extension_rdoc.html#label-Encapsulate+C+data+into+a+Ruby+object
127
+ */
128
+ static const rb_data_type_t disjoint_union_type = {
129
+ .wrap_struct_name = "disjoint_union",
130
+ { // help for the Ruby garbage collector
131
+ .dmark = NULL, // dmark, for marking other Ruby objects. We don't hold any other objects so this can be NULL
132
+ .dfree = disjoint_union_free, // how to free the memory associated with an object
133
+ .dsize = disjoint_union_memsize, // roughly how much space does the object consume?
134
+ },
135
+ .data = NULL, // a data field we could use for something here if we wanted. Ruby ignores it
136
+ .flags = 0 // GC-related flag values.
137
+ };
138
+
139
+ /*
140
+ * End memory management functions
102
141
  ************************************************************/
103
142
 
143
+ /************************************************************
144
+ * Wrapping and unwrapping things for the Ruby runtime
145
+ *
146
+ */
147
+
148
+ /*
149
+ * Unwrap a Ruby-side disjoint union object to get the C struct inside.
150
+ */
151
+ static disjoint_union_data *unwrapped(VALUE self) {
152
+ disjoint_union_data *disjoint_union;
153
+ TypedData_Get_Struct((self), disjoint_union_data, &disjoint_union_type, disjoint_union);
154
+ return disjoint_union;
155
+ }
156
+
157
+ /*
158
+ * This is for CDisjointUnion.allocate on the Ruby side
159
+ */
160
+ static VALUE disjoint_union_alloc(VALUE klass) {
161
+ // Get one on the heap
162
+ disjoint_union_data *disjoint_union = create_disjoint_union();
163
+ // Wrap it up into a Ruby object
164
+ return TypedData_Wrap_Struct(klass, &disjoint_union_type, disjoint_union);
165
+ }
166
+
167
+ /*
168
+ * End wrapping and unwrapping functions.
169
+ ************************************************************/
170
+
171
+ /************************************************************
172
+ * The Disjoint Union API here on the C side
173
+ *
174
+ */
175
+
104
176
  /*
105
177
  * Is the given element already a member of the universe?
106
178
  */
@@ -114,13 +186,6 @@ static int present_p(disjoint_union_data *disjoint_union, size_t element) {
114
186
  static void assert_membership(disjoint_union_data *disjoint_union, size_t element) {
115
187
  if (!present_p(disjoint_union, element)) {
116
188
  rb_raise(eSharedDataError, "Value %zu is not part of the universe", element);
117
- /* rb_raise( */
118
- /* eSharedDataError, */
119
- /* "Value %zu is not part of the universe, size = %zu, forest_val = %lu", */
120
- /* element, */
121
- /* size(disjoint_union->pairs), */
122
- /* get(disjoint_union->pairs, element)->parent */
123
- /* ); */
124
189
  }
125
190
  }
126
191
 
@@ -206,59 +271,15 @@ static void unite(disjoint_union_data *disjoint_union, size_t elt1, size_t elt2)
206
271
  link_roots(disjoint_union, root1, root2);
207
272
  }
208
273
 
209
-
210
- /**
211
- * Wrapping and unwrapping things for the Ruby runtime
212
- *
213
- */
214
-
215
- // How much memory (roughly) does a disjoint_union_data instance consume? I guess the Ruby runtime can use this information when
216
- // deciding how agressive to be during garbage collection and such.
217
- static size_t disjoint_union_memsize(const void *ptr) {
218
- if (ptr) {
219
- const disjoint_union_data *du = ptr;
220
-
221
- // See https://github.com/JacksonAllan/CC/issues/3
222
- return sizeof( cc_vec_hdr_ty ) + cap( du->pairs ) * CC_EL_SIZE( *(du->pairs) );
223
- } else {
224
- return 0;
225
- }
226
- }
227
-
228
- /*
229
- * A configuration struct that tells the Ruby runtime how to deal with a disjoint_union_data object.
230
- *
231
- * https://docs.ruby-lang.org/en/master/extension_rdoc.html#label-Encapsulate+C+data+into+a+Ruby+object
232
- */
233
- static const rb_data_type_t disjoint_union_type = {
234
- .wrap_struct_name = "disjoint_union",
235
- { // help for the Ruby garbage collector
236
- .dmark = NULL, // dmark, for marking other Ruby objects. We don't hold any other objects so this can be NULL
237
- .dfree = disjoint_union_free, // how to free the memory associated with an object
238
- .dsize = disjoint_union_memsize, // roughly how much space does the object consume?
239
- },
240
- .data = NULL, // a data field we could use for something here if we wanted. Ruby ignores it
241
- .flags = 0 // GC-related flag values.
242
- };
243
-
244
274
  /*
245
- * Unwrap a Ruby-side disjoint union object to get the C struct inside.
246
- */
247
- static disjoint_union_data *unwrapped(VALUE self) {
248
- disjoint_union_data *disjoint_union;
249
- TypedData_Get_Struct((self), disjoint_union_data, &disjoint_union_type, disjoint_union);
250
- return disjoint_union;
251
- }
275
+ * End C impelementaion of the Segment Tree API
276
+ ************************************************************/
252
277
 
253
- /*
254
- * This is for CDisjointUnion.allocate on the Ruby side
278
+ /************************************************************
279
+ * The wrappers around the C functionality.
280
+ *
281
+ * These become Ruby methods via rb_define_method() below.
255
282
  */
256
- static VALUE disjoint_union_alloc(VALUE klass) {
257
- // Get one on the heap
258
- disjoint_union_data *disjoint_union = create_disjoint_union();
259
- // Wrap it up into a Ruby object
260
- return TypedData_Wrap_Struct(klass, &disjoint_union_type, disjoint_union);
261
- }
262
283
 
263
284
  /*
264
285
  * A single parameter is optional. If given it should be a non-negative integer and specifies the initial size, s, of the universe
@@ -286,17 +307,6 @@ static VALUE disjoint_union_init(int argc, VALUE *argv, VALUE self) {
286
307
  return self;
287
308
  }
288
309
 
289
- /**
290
- * And now the simple wrappers around the Disjoint Union C functionality. In each case we
291
- * - unwrap a 'VALUE self',
292
- * - i.e., the CDisjointUnion instance on the Ruby side;
293
- * - munge any other arguments into longs;
294
- * - call the appropriate C function to act on the struct; and
295
- * - return an appropriate VALUE for the Ruby runtime can use.
296
- *
297
- * We make them into methods on CDisjointUnion in the Init_CDisjointUnion function, below.
298
- */
299
-
300
310
  /*
301
311
  * Add a new subset to the universe containing the element +new_v+.
302
312
  *
@@ -1,17 +1,5 @@
1
1
  require 'mkmf'
2
2
 
3
- abort 'missing malloc()' unless have_func "malloc"
4
- abort 'missing realloc()' unless have_func "realloc"
3
+ require_relative '../extconf_shared.rb'
5
4
 
6
- if try_cflags('-O3')
7
- append_cflags('-O3')
8
- end
9
-
10
- extension_name = "c_disjoint_union"
11
- dir_config(extension_name)
12
-
13
- $srcs = ["disjoint_union.c", "../shared.c"]
14
- $INCFLAGS << " -I$(srcdir)/.."
15
- $VPATH << "$(srcdir)/.."
16
-
17
- create_makefile("data_structures_rmolinari/c_disjoint_union")
5
+ generate_makefile('disjoint_union')
@@ -1,17 +1,4 @@
1
1
  require 'mkmf'
2
+ require_relative '../extconf_shared.rb'
2
3
 
3
- abort 'missing malloc()' unless have_func "malloc"
4
- abort 'missing realloc()' unless have_func "realloc"
5
-
6
- if try_cflags('-O3')
7
- append_cflags('-O3')
8
- end
9
-
10
- extension_name = "c_segment_tree_template"
11
- dir_config(extension_name)
12
-
13
- $srcs = ["segment_tree_template.c", "../shared.c"]
14
- $INCFLAGS << " -I$(srcdir)/.."
15
- $VPATH << "$(srcdir)/.."
16
-
17
- create_makefile("data_structures_rmolinari/c_segment_tree_template")
4
+ generate_makefile('segment_tree_template')
@@ -190,11 +190,11 @@ static void setup(segment_tree_data* seg_tree, VALUE combine, VALUE single_cell_
190
190
  VALUE idCall = rb_intern("call");
191
191
 
192
192
  if (!rb_obj_respond_to(combine, idCall, TRUE)) {
193
- rb_raise(rb_eArgError, "wrong type argument %"PRIsVALUE" (should be callable)", rb_obj_class(combine));
193
+ rb_raise(rb_eArgError, "wrong type argument %" PRIsVALUE " (should be callable)", rb_obj_class(combine));
194
194
  }
195
195
 
196
196
  if (!rb_obj_respond_to(single_cell_array_val, idCall, TRUE)) {
197
- rb_raise(rb_eArgError, "wrong type argument %"PRIsVALUE" (should be callable)", rb_obj_class(single_cell_array_val));
197
+ rb_raise(rb_eArgError, "wrong type argument %" PRIsVALUE " (should be callable)", rb_obj_class(single_cell_array_val));
198
198
  }
199
199
 
200
200
  seg_tree->combine_lambda = combine;
@@ -215,7 +215,6 @@ static void setup(segment_tree_data* seg_tree, VALUE combine, VALUE single_cell_
215
215
  build(seg_tree, TREE_ROOT, 0, seg_tree->size - 1);
216
216
  }
217
217
 
218
-
219
218
  /*
220
219
  * Determine the value for the subarray A(left, right).
221
220
  *
@@ -297,8 +296,10 @@ static void update_val_at(segment_tree_data *seg_tree, size_t idx, size_t tree_i
297
296
  * End C implementation of the Segment Tree API
298
297
  ************************************************************/
299
298
 
300
- /**
301
- * And now the wrappers around the C functionality.
299
+ /************************************************************
300
+ * The wrappers around the C functionality.
301
+ *
302
+ * These become Ruby methods via rb_define_method() below.
302
303
  */
303
304
 
304
305
  /*