data_structures_rmolinari 0.5.1 → 0.5.3
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +6 -7
- data/ext/c_disjoint_union/disjoint_union.c +79 -69
- data/ext/c_disjoint_union/extconf.rb +2 -14
- data/ext/c_segment_tree_template/extconf.rb +2 -15
- data/ext/c_segment_tree_template/segment_tree_template.c +6 -5
- data/ext/extconf_shared.rb +19 -0
- data/ext/shared.c +1 -0
- data/ext/shared.h +1 -4
- metadata +3 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 78d2f04135a4e241d64b8d6c71fbe05d84c1995ee2ce58362df313a9c75f527b
|
4
|
+
data.tar.gz: 6def12eef0b4aeb5ef12eff37f89bb49b695c60369e02c407969255cf03c5f57
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: '08a7b5abf025e232d5863310860f5b0e2bfea014688750b34e48028cc2a57f4bc1ba4d0638974e64342fe377d729dd7feab0b985db684035f6e8905f8f7b708c'
|
7
|
+
data.tar.gz: 923ac00b27e35c41d392d465aacdd73dd8313d46ac66a741eb008355df2abbb6c10ee62bfe8bce9e5a8b0623be06dea98a298f5ef07efb10cb5cfc0ec447bf66
|
data/README.md
CHANGED
@@ -7,14 +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
|
10
|
+
The code lives in the `DataStructuresRMolinari` module to avoid polluting the global namespace.
|
11
11
|
|
12
12
|
It is distributed under the MIT license.
|
13
13
|
|
14
|
-
|
15
|
-
|
16
|
-
The right way to organize the code is not obvious to me. For now the data structures are all defined in the module
|
17
|
-
`DataStructuresRMolinari` to avoid polluting the global namespace.
|
14
|
+
It is available as a gem: https://rubygems.org/gems/data_structures_rmolinari.
|
18
15
|
|
19
16
|
# Implementations
|
20
17
|
|
@@ -210,8 +207,10 @@ The implementation uses the remarkable Convenient Containers library from Jackso
|
|
210
207
|
the pure Ruby `SegmentTreeTemplate` class.
|
211
208
|
|
212
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
|
213
|
-
with Ruby.
|
214
|
-
|
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.
|
215
214
|
|
216
215
|
# References
|
217
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
|
-
*
|
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
|
-
*
|
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
|
-
*
|
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
|
-
|
4
|
-
abort 'missing realloc()' unless have_func "realloc"
|
3
|
+
require_relative '../extconf_shared.rb'
|
5
4
|
|
6
|
-
|
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
|
-
|
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
|
-
*
|
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
|
/*
|
@@ -0,0 +1,19 @@
|
|
1
|
+
def generate_makefile(name)
|
2
|
+
extension_name = "c_#{name}"
|
3
|
+
source_name = "#{name}.c"
|
4
|
+
|
5
|
+
abort 'missing malloc()' unless have_func "malloc"
|
6
|
+
abort 'missing realloc()' unless have_func "realloc"
|
7
|
+
|
8
|
+
if try_cflags('-O3')
|
9
|
+
append_cflags('-O3')
|
10
|
+
end
|
11
|
+
|
12
|
+
dir_config(extension_name)
|
13
|
+
|
14
|
+
$srcs = [source_name, "../shared.c"]
|
15
|
+
$INCFLAGS << " -I$(srcdir)/.."
|
16
|
+
$VPATH << "$(srcdir)/.."
|
17
|
+
|
18
|
+
create_makefile("data_structures_rmolinari/#{extension_name}")
|
19
|
+
end
|
data/ext/shared.c
CHANGED
data/ext/shared.h
CHANGED
@@ -2,7 +2,6 @@
|
|
2
2
|
#define SHARED_H
|
3
3
|
|
4
4
|
#include <stddef.h>
|
5
|
-
#include "ruby.h"
|
6
5
|
|
7
6
|
#define mShared rb_define_module("Shared")
|
8
7
|
#define eSharedDataError rb_const_get(mShared, rb_intern_const("DataError"))
|
@@ -12,13 +11,11 @@
|
|
12
11
|
//#define debug(...) printf(__VA_ARGS__)
|
13
12
|
#define debug(...)
|
14
13
|
|
15
|
-
/* What we might think of as vector[index]. It is assignable */
|
14
|
+
/* What we might think of as vector[index] for a CC vec(foo). It is assignable */
|
16
15
|
#define lval(vector, index) (*get(vector, index))
|
17
16
|
|
18
17
|
/*
|
19
18
|
* Binary tree arithmetic for an implicit tree in an array, 1-based.
|
20
|
-
*
|
21
|
-
* TODO: into shared header
|
22
19
|
*/
|
23
20
|
#define TREE_ROOT 1
|
24
21
|
size_t midpoint(size_t left, size_t right);
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: data_structures_rmolinari
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.5.
|
4
|
+
version: 0.5.3
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Rory Molinari
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2023-02-
|
11
|
+
date: 2023-02-15 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: must_be
|
@@ -90,6 +90,7 @@ files:
|
|
90
90
|
- ext/c_segment_tree_template/extconf.rb
|
91
91
|
- ext/c_segment_tree_template/segment_tree_template.c
|
92
92
|
- ext/cc.h
|
93
|
+
- ext/extconf_shared.rb
|
93
94
|
- ext/shared.c
|
94
95
|
- ext/shared.h
|
95
96
|
- lib/data_structures_rmolinari.rb
|