carray 3.0.1 → 3.0.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/CHANGELOG.md +824 -0
- data/README.md +5 -6
- data/carray.gemspec +1 -1
- data/ext/ca_axis_dispatch.c +258 -4
- data/ext/ca_axis_group.c +368 -184
- data/ext/ca_binop_dispatch.c +54 -32
- data/ext/ca_binop_dispatch.h +1 -0
- data/ext/ca_categorical_iterator.c +90 -41
- data/ext/ca_compare.c +8 -2
- data/ext/ca_for_buffer.h +21 -68
- data/ext/ca_group_iter.c +27 -11
- data/ext/ca_kernel_iterator.c +960 -245
- data/ext/ca_kernel_iterator.h +352 -181
- data/ext/ca_monop_dispatch.c +4 -4
- data/ext/ca_monop_dispatch.h +4 -0
- data/ext/ca_obj_array.c +194 -42
- data/ext/ca_obj_bincmp.c +152 -121
- data/ext/ca_obj_binop.c +107 -210
- data/ext/ca_obj_bitarray.c +37 -24
- data/ext/ca_obj_bitfield.c +95 -62
- data/ext/ca_obj_block.c +5 -4
- data/ext/ca_obj_byte_swap.c +40 -40
- data/ext/ca_obj_const_string.c +180 -50
- data/ext/ca_obj_face.c +428 -62
- data/ext/ca_obj_face.h +107 -47
- data/ext/ca_obj_fake.c +21 -50
- data/ext/ca_obj_farray.c +1 -0
- data/ext/ca_obj_field.c +16 -3
- data/ext/ca_obj_fixlen_string.c +39 -26
- data/ext/ca_obj_grid.c +181 -124
- data/ext/ca_obj_meld.c +235 -55
- data/ext/ca_obj_moncmp.c +61 -61
- data/ext/ca_obj_monop.c +125 -107
- data/ext/ca_obj_object.c +551 -233
- data/ext/ca_obj_record.c +51 -16
- data/ext/ca_obj_reduce.c +5 -8
- data/ext/ca_obj_refer.c +117 -33
- data/ext/ca_obj_remap.c +57 -17
- data/ext/ca_obj_repeat.c +21 -5
- data/ext/ca_obj_roll.c +115 -11
- data/ext/ca_obj_select.c +88 -75
- data/ext/ca_obj_select_axis.c +58 -60
- data/ext/ca_obj_shift.c +22 -102
- data/ext/ca_obj_stack.c +223 -123
- data/ext/ca_obj_stride.c +267 -35
- data/ext/ca_obj_string.c +25 -22
- data/ext/ca_obj_tile.c +32 -16
- data/ext/ca_obj_time.c +23 -25
- data/ext/ca_obj_timedelta.c +21 -23
- data/ext/ca_obj_transpose.c +12 -3
- data/ext/ca_obj_triop.c +110 -117
- data/ext/ca_obj_window.c +157 -29
- data/ext/ca_op_ipower.c +25 -13
- data/ext/ca_op_powi.h +57 -30
- data/ext/ca_pow5_table.h +656 -0
- data/ext/ca_rng_normal.h +42 -0
- data/ext/ca_rng_xoshiro256pp.h +105 -0
- data/ext/ca_sort_kernels.h +70 -0
- data/ext/ca_sweep_engine.c +385 -198
- data/ext/ca_sweep_engine.h +31 -23
- data/ext/ca_triop_dispatch.c +4 -1
- data/ext/ca_triop_dispatch.h +5 -2
- data/ext/caframe_csv_reader.c +463 -0
- data/ext/carray.h +90 -28
- data/ext/carray_access.c +487 -231
- data/ext/carray_address_basis.c +731 -0
- data/ext/carray_attribute.c +45 -3
- data/ext/carray_bincount.c +32 -2
- data/ext/carray_broadcast.c +49 -17
- data/ext/carray_call_cfunc.c +667 -511
- data/ext/carray_cast.c +1556 -247
- data/ext/carray_cast_func.rb +34 -21
- data/ext/carray_conversion.c +197 -73
- data/ext/carray_copy.c +84 -38
- data/ext/carray_core.c +591 -119
- data/ext/carray_count.c +148 -96
- data/ext/carray_element.c +18 -27
- data/ext/carray_factorize.c +604 -291
- data/ext/carray_generate.c +76 -40
- data/ext/carray_histogram.c +152 -94
- data/ext/carray_hold.c +2 -1
- data/ext/carray_index_classifier.c +63 -96
- data/ext/carray_internal.h +400 -1
- data/ext/carray_lazy.c +834 -79
- data/ext/carray_loop.c +88 -66
- data/ext/carray_mask.c +705 -324
- data/ext/carray_math_kernel.h +1 -0
- data/ext/carray_median_percentile.c +263 -63
- data/ext/carray_memory_view.c +254 -248
- data/ext/carray_operator.c +1093 -363
- data/ext/carray_order.c +133 -69
- data/ext/carray_partition.c +102 -56
- data/ext/carray_random.c +582 -204
- data/ext/carray_scatter.c +308 -65
- data/ext/carray_segment.c +257 -0
- data/ext/carray_slab.c +183 -70
- data/ext/carray_sort.c +235 -240
- data/ext/carray_sort_kernel.c +8 -1
- data/ext/carray_struct.c +99 -21
- data/ext/carray_test.c +167 -64
- data/ext/carray_time_text.c +1182 -0
- data/ext/carray_utils.c +363 -98
- data/ext/extconf.rb +27 -2
- data/ext/mk_call_cfunc.rb +103 -118
- data/ext/mk_pow5_table.rb +35 -0
- data/ext/mkkernel.rb +1349 -851
- data/ext/ruby_carray.c +17 -2
- data/ext/version.h +4 -4
- data/ext/version.rb +4 -2
- data/lib/carray/attribute.rb +39 -0
- data/lib/carray/autoload_carray.rb +24 -3
- data/lib/carray/autoload_method_extension.rb +18 -0
- data/lib/carray/axis_group.rb +245 -20
- data/lib/carray/basics.rb +95 -25
- data/lib/carray/bincount_nd.rb +6 -0
- data/lib/carray/block_iterator.rb +200 -54
- data/lib/carray/boolean_reduce.rb +21 -29
- data/lib/carray/categorical.rb +244 -59
- data/lib/carray/categorical_iterator.rb +391 -757
- data/lib/carray/complex.rb +36 -29
- data/lib/carray/conditional.rb +94 -7
- data/lib/carray/const_string.rb +177 -27
- data/lib/carray/construct.rb +40 -0
- data/lib/carray/data_type_extension.rb +3 -0
- data/lib/carray/data_type_limits.rb +91 -0
- data/lib/carray/fixlen_string.rb +1 -1
- data/lib/carray/frame/concat.rb +157 -6
- data/lib/carray/frame/convert.rb +13 -7
- data/lib/carray/frame/csv_parser.rb +590 -76
- data/lib/carray/frame/describe.rb +79 -0
- data/lib/carray/frame/frame.rb +205 -35
- data/lib/carray/frame/group.rb +199 -40
- data/lib/carray/frame/io.rb +452 -60
- data/lib/carray/frame/join.rb +134 -46
- data/lib/carray/frame/records.rb +54 -19
- data/lib/carray/frame/reshape.rb +448 -0
- data/lib/carray/frame/sort.rb +26 -2
- data/lib/carray/frame/verbs.rb +726 -56
- data/lib/carray/frame.rb +2 -0
- data/lib/carray/fuse_source.rb +111 -2
- data/lib/carray/fusion.rb +229 -38
- data/lib/carray/inspect.rb +71 -20
- data/lib/carray/iterator.rb +157 -2
- data/lib/carray/lazy.rb +54 -67
- data/lib/carray/mask_gap_fill.rb +66 -50
- data/lib/carray/math.rb +32 -19
- data/lib/carray/meld_reduce.rb +40 -7
- data/lib/carray/methods/align_addr.rb +8 -4
- data/lib/carray/methods/bincount.rb +2 -2
- data/lib/carray/methods/bit_string.rb +14 -3
- data/lib/carray/methods/composition.rb +16 -10
- data/lib/carray/methods/discovery_along.rb +74 -0
- data/lib/carray/methods/factorize.rb +50 -0
- data/lib/carray/methods/gather_nd.rb +13 -2
- data/lib/carray/methods/insert_block.rb +9 -14
- data/lib/carray/methods/is_in.rb +130 -6
- data/lib/carray/methods/join.rb +1 -1
- data/lib/carray/methods/locate_addr.rb +235 -21
- data/lib/carray/methods/mask_duplicates.rb +35 -1
- data/lib/carray/methods/mode.rb +18 -1
- data/lib/carray/methods/nunique.rb +22 -1
- data/lib/carray/methods/repeat.rb +91 -0
- data/lib/carray/methods/resize.rb +21 -23
- data/lib/carray/methods/snap.rb +34 -12
- data/lib/carray/methods/unique.rb +52 -3
- data/lib/carray/record_order.rb +208 -0
- data/lib/carray/rng.rb +86 -0
- data/lib/carray/runtime.rb +14 -11
- data/lib/carray/segment_iterator.rb +990 -0
- data/lib/carray/serialize.rb +197 -27
- data/lib/carray/slab_iterator.rb +109 -40
- data/lib/carray/stack.rb +10 -4
- data/lib/carray/string_operation_extension.rb +9 -4
- data/lib/carray/struct.rb +142 -51
- data/lib/carray/struct_builder.rb +110 -14
- data/lib/carray/table.rb +2 -2
- data/lib/carray/time.rb +535 -105
- data/lib/carray/window_iterator.rb +259 -82
- data/lib/carray.rb +4 -2
- data/yard-stubs/ca_obj_array.rb +4 -4
- data/yard-stubs/ca_obj_bitarray.rb +7 -0
- data/yard-stubs/ca_obj_bitfield.rb +23 -12
- data/yard-stubs/ca_obj_block.rb +2 -7
- data/yard-stubs/ca_obj_fake.rb +1 -1
- data/yard-stubs/ca_obj_field.rb +1 -1
- data/yard-stubs/ca_obj_refer.rb +3 -3
- data/yard-stubs/ca_obj_roll.rb +0 -1
- data/yard-stubs/ca_obj_stride.rb +10 -7
- data/yard-stubs/ca_obj_transpose.rb +1 -1
- data/yard-stubs/ca_obj_window.rb +11 -4
- data/yard-stubs/carray_access.rb +6 -5
- data/yard-stubs/carray_broadcast.rb +2 -2
- data/yard-stubs/carray_cast.rb +78 -43
- data/yard-stubs/carray_conversion.rb +13 -1
- data/yard-stubs/carray_copy.rb +8 -3
- data/yard-stubs/carray_core.rb +3 -83
- data/yard-stubs/carray_count.rb +16 -2
- data/yard-stubs/carray_lazy.rb +210 -1
- data/yard-stubs/carray_loop.rb +4 -1
- data/yard-stubs/carray_mask.rb +34 -10
- data/yard-stubs/carray_math.rb +1499 -4
- data/yard-stubs/carray_median_percentile.rb +26 -8
- data/yard-stubs/carray_order.rb +43 -82
- data/yard-stubs/carray_scatter.rb +10 -3
- data/yard-stubs/carray_segment.rb +72 -0
- data/yard-stubs/carray_slab.rb +11 -8
- data/yard-stubs/carray_sort.rb +50 -8
- data/yard-stubs/carray_test.rb +10 -0
- data/yard-stubs/carray_utils.rb +0 -26
- metadata +19 -19
- data/ext/ca_for_each_element.h +0 -239
- data/ext/carray_build_flags.h +0 -3
- data/ext/carray_config.h +0 -28
- data/ext/carray_kernels_bincmp.c +0 -4446
- data/ext/carray_kernels_binop.c +0 -11001
- data/ext/carray_kernels_init.c +0 -1131
- data/ext/carray_kernels_map.c +0 -3467
- data/ext/carray_kernels_moncmp.c +0 -2097
- data/ext/carray_kernels_monop.c +0 -18313
- data/ext/carray_kernels_reduce_aggregate.c +0 -25837
- data/ext/carray_kernels_reduce_boolean.c +0 -330
- data/ext/carray_kernels_reduce_cumulative.c +0 -14593
- data/ext/carray_kernels_reduce_extreme.c +0 -16948
- data/ext/carray_kernels_reduce_variance.c +0 -3910
- data/ext/carray_kernels_scan.c +0 -3693
- data/ext/carray_kernels_search.c +0 -32138
- data/ext/carray_kernels_sort.c +0 -10626
- data/ext/carray_kernels_triop.c +0 -1392
data/ext/ca_axis_group.c
CHANGED
|
@@ -33,6 +33,7 @@
|
|
|
33
33
|
--------------------------------------------------------------------------- */
|
|
34
34
|
|
|
35
35
|
#include "carray.h"
|
|
36
|
+
#include "carray_internal.h" /* CA_EXTREMUM_REPLACES, ca_obj_extremum_replaces */
|
|
36
37
|
#include "ca_kernel_iterator.h"
|
|
37
38
|
#include <math.h>
|
|
38
39
|
|
|
@@ -64,8 +65,8 @@ group_op_code (VALUE vop)
|
|
|
64
65
|
else if ( id == rb_intern("max_addr") ) return GR_MAXADDR;
|
|
65
66
|
else if ( id == rb_intern("all") ) return GR_ALL;
|
|
66
67
|
else if ( id == rb_intern("any") ) return GR_ANY;
|
|
67
|
-
rb_raise(rb_eArgError, "axis_group_reduce: unsupported op :%
|
|
68
|
-
|
|
68
|
+
rb_raise(rb_eArgError, "axis_group_reduce: unsupported op :%"PRIsVALUE,
|
|
69
|
+
rb_id2str(id));
|
|
69
70
|
}
|
|
70
71
|
|
|
71
72
|
/* ---- per-slab-element composite-code walk, templated on the load type ----
|
|
@@ -110,16 +111,20 @@ group_op_code (VALUE vop)
|
|
|
110
111
|
ca_size_t *gw_ocode = NULL, *gw_doff = NULL, *gw_moff = NULL, \
|
|
111
112
|
*gw_gaddr = NULL; \
|
|
112
113
|
int gw_ready = 0; \
|
|
114
|
+
/* freed by the unwind too: the walk can raise between here and the \
|
|
115
|
+
releases below (a gather that cannot convert a cell, and in the object \
|
|
116
|
+
lane a callback into Ruby). */ \
|
|
117
|
+
volatile VALUE gw_h1 = 0, gw_h2 = 0, gw_h3 = 0, gw_h4 = 0; \
|
|
113
118
|
int gw_need_addr = ( op == GR_MINADDR || op == GR_MAXADDR ); \
|
|
114
119
|
CA_FOR_EACH_SLAB(st, ca, axes, (int8_t) ngroup, CA_KERNEL_READ, p, m) { \
|
|
115
120
|
int8_t sndim = st.slab_ndim; \
|
|
116
121
|
ca_size_t SE = st.slab_elements; \
|
|
117
122
|
if ( SE > 0 ) { \
|
|
118
123
|
if ( ! gw_ready ) { \
|
|
119
|
-
gw_ocode =
|
|
120
|
-
gw_doff =
|
|
121
|
-
gw_moff =
|
|
122
|
-
gw_gaddr =
|
|
124
|
+
gw_ocode = ALLOCV_N(ca_size_t, gw_h1, SE); \
|
|
125
|
+
gw_doff = ALLOCV_N(ca_size_t, gw_h2, SE); \
|
|
126
|
+
gw_moff = ALLOCV_N(ca_size_t, gw_h3, SE); \
|
|
127
|
+
gw_gaddr = ALLOCV_N(ca_size_t, gw_h4, SE); \
|
|
123
128
|
ca_size_t sidx[CA_RANK_MAX]; \
|
|
124
129
|
for ( int8_t k = 0; k < sndim; k++ ) sidx[k] = 0; \
|
|
125
130
|
for ( ca_size_t e = 0; e < SE; e++ ) { \
|
|
@@ -158,21 +163,95 @@ group_op_code (VALUE vop)
|
|
|
158
163
|
ca_size_t oc = gw_ocode[e]; \
|
|
159
164
|
if ( oc == GW_SKIP ) continue; \
|
|
160
165
|
if ( m && m[ gw_moff[e] ] ) continue; \
|
|
161
|
-
|
|
166
|
+
T rv = *(T *)(p + gw_doff[e]); \
|
|
167
|
+
double v = (double) rv; \
|
|
162
168
|
ca_size_t o = oc + b; \
|
|
163
169
|
ca_size_t gaddr = gw_gaddr[e]; \
|
|
164
170
|
ACCUM; \
|
|
165
|
-
(void) v; (void) o; (void) gaddr;
|
|
171
|
+
(void) rv; (void) v; (void) o; (void) gaddr; \
|
|
166
172
|
} \
|
|
167
173
|
} \
|
|
168
174
|
b++; \
|
|
169
175
|
} \
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
if ( gw_moff ) xfree(gw_moff); \
|
|
173
|
-
if ( gw_gaddr ) xfree(gw_gaddr); \
|
|
176
|
+
ALLOCV_END(gw_h1); ALLOCV_END(gw_h2); \
|
|
177
|
+
ALLOCV_END(gw_h3); ALLOCV_END(gw_h4); \
|
|
174
178
|
} while (0)
|
|
175
179
|
|
|
180
|
+
/* GROUP_DISPATCH_T(ACCUM_T): the same walk, for an accumulator that needs to
|
|
181
|
+
name the source type -- one that keeps its answer in that type instead of
|
|
182
|
+
widening to double. ACCUM_T is invoked as ACCUM_T(T). */
|
|
183
|
+
#define GROUP_DISPATCH_T(ACCUM_T) \
|
|
184
|
+
switch ( ca->data_type ) { \
|
|
185
|
+
case CA_BOOLEAN: GROUP_WALK(boolean8_t, ACCUM_T(boolean8_t)); break; \
|
|
186
|
+
case CA_INT8: GROUP_WALK(int8_t, ACCUM_T(int8_t)); break; \
|
|
187
|
+
case CA_UINT8: GROUP_WALK(uint8_t, ACCUM_T(uint8_t)); break; \
|
|
188
|
+
case CA_INT16: GROUP_WALK(int16_t, ACCUM_T(int16_t)); break; \
|
|
189
|
+
case CA_UINT16: GROUP_WALK(uint16_t, ACCUM_T(uint16_t)); break; \
|
|
190
|
+
case CA_INT32: GROUP_WALK(int32_t, ACCUM_T(int32_t)); break; \
|
|
191
|
+
case CA_UINT32: GROUP_WALK(uint32_t, ACCUM_T(uint32_t)); break; \
|
|
192
|
+
case CA_INT64: GROUP_WALK(int64_t, ACCUM_T(int64_t)); break; \
|
|
193
|
+
case CA_UINT64: GROUP_WALK(uint64_t, ACCUM_T(uint64_t)); break; \
|
|
194
|
+
case CA_FLOAT32: GROUP_WALK(float, ACCUM_T(float)); break; \
|
|
195
|
+
case CA_FLOAT64: GROUP_WALK(double, ACCUM_T(double)); break; \
|
|
196
|
+
default: break; \
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
/* An extremum keeps the source data type: its magnitude never grows, so
|
|
200
|
+
widening to double buys nothing and costs exactness -- an int64 past 2^53
|
|
201
|
+
came back rounded, and a genuinely larger value could lose the comparison
|
|
202
|
+
to a smaller one that rounded to the same double. The scan siblings
|
|
203
|
+
(GROUP_SCAN_EXTREMUM_WALK) already held a native accumulator; these are the
|
|
204
|
+
reduce twins.
|
|
205
|
+
|
|
206
|
+
A NaN loses every contest, as it does in CArray's own min / max: it is held
|
|
207
|
+
only while nothing else has landed, the first number displaces it, and a
|
|
208
|
+
group of nothing but NaN answers NaN (for a position, UNDEF). `v` is the
|
|
209
|
+
widened load, so testing it for NaN is the same question for every source
|
|
210
|
+
type and costs an integer body nothing. The replacement test is the
|
|
211
|
+
shared CA_EXTREMUM_REPLACES; the first member is taken whatever it is.
|
|
212
|
+
seen_num[] says whether a number has landed (the positions read it);
|
|
213
|
+
cnt[] says whether anything has. */
|
|
214
|
+
#define GMINMAX(T, A, CMP) \
|
|
215
|
+
do { \
|
|
216
|
+
A *acc = (A *) co->ptr; \
|
|
217
|
+
A av = (A) rv; \
|
|
218
|
+
if ( cnt[o] == 0 || CA_EXTREMUM_REPLACES(av, acc[o], CMP) ) acc[o] = av; \
|
|
219
|
+
if ( v == v ) seen_num[o] = 1; \
|
|
220
|
+
cnt[o] += 1; \
|
|
221
|
+
} while (0)
|
|
222
|
+
|
|
223
|
+
/* Boolean answers as its 0/1 numeric storage, which is what CArray#min / #max
|
|
224
|
+
do with a boolean array -- the boolean-returning twins are all / any. Every
|
|
225
|
+
other type answers in its own. */
|
|
226
|
+
#define GROUP_DISPATCH_EXTREMUM(CMP) \
|
|
227
|
+
switch ( ca->data_type ) { \
|
|
228
|
+
case CA_BOOLEAN: GROUP_WALK(boolean8_t, GMINMAX(boolean8_t, uint64_t, CMP)); break; \
|
|
229
|
+
case CA_INT8: GROUP_WALK(int8_t, GMINMAX(int8_t, int8_t, CMP)); break; \
|
|
230
|
+
case CA_UINT8: GROUP_WALK(uint8_t, GMINMAX(uint8_t, uint8_t, CMP)); break; \
|
|
231
|
+
case CA_INT16: GROUP_WALK(int16_t, GMINMAX(int16_t, int16_t, CMP)); break; \
|
|
232
|
+
case CA_UINT16: GROUP_WALK(uint16_t, GMINMAX(uint16_t, uint16_t, CMP)); break; \
|
|
233
|
+
case CA_INT32: GROUP_WALK(int32_t, GMINMAX(int32_t, int32_t, CMP)); break; \
|
|
234
|
+
case CA_UINT32: GROUP_WALK(uint32_t, GMINMAX(uint32_t, uint32_t, CMP)); break; \
|
|
235
|
+
case CA_INT64: GROUP_WALK(int64_t, GMINMAX(int64_t, int64_t, CMP)); break; \
|
|
236
|
+
case CA_UINT64: GROUP_WALK(uint64_t, GMINMAX(uint64_t, uint64_t, CMP)); break; \
|
|
237
|
+
case CA_FLOAT32: GROUP_WALK(float, GMINMAX(float, float, CMP)); break; \
|
|
238
|
+
case CA_FLOAT64: GROUP_WALK(double, GMINMAX(double, double, CMP)); break; \
|
|
239
|
+
default: break; \
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
#define GMINMAXADDR(T, CMP, ADDR) \
|
|
243
|
+
do { \
|
|
244
|
+
T *acc = (T *) xbuf; \
|
|
245
|
+
if ( v == v && ( ! seen_num[o] || rv CMP acc[o] ) ) { \
|
|
246
|
+
acc[o] = rv; \
|
|
247
|
+
ADDR[o] = (int64_t) ( band_addr[b] + gaddr ); \
|
|
248
|
+
seen_num[o] = 1; \
|
|
249
|
+
} \
|
|
250
|
+
cnt[o] += 1; \
|
|
251
|
+
} while (0)
|
|
252
|
+
#define GMINADDR(T) GMINMAXADDR(T, <, mnaddr)
|
|
253
|
+
#define GMAXADDR(T) GMINMAXADDR(T, >, mxaddr)
|
|
254
|
+
|
|
176
255
|
/* Run one walk over every supported native data type. Dispatched on the source
|
|
177
256
|
data_type so the inner loop stays monomorphic (no forced float64 cast). */
|
|
178
257
|
#define GROUP_DISPATCH(ACCUM) \
|
|
@@ -232,7 +311,7 @@ group_op_code (VALUE vop)
|
|
|
232
311
|
Empty / all-masked groups follow the CArray zero-contribution contract: an
|
|
233
312
|
identity-bearing op returns its identity (sum 0, prod 1, count 0, all true,
|
|
234
313
|
any false), a ratio / extremum returns UNDEF. Sample variance / stddev:
|
|
235
|
-
n
|
|
314
|
+
n < 2 UNDEF, n >= 2 the formula.
|
|
236
315
|
*/
|
|
237
316
|
static VALUE
|
|
238
317
|
rb_ca_axis_group_reduce (VALUE self, VALUE vgaxes, VALUE vbundles, VALUE vop)
|
|
@@ -243,7 +322,7 @@ rb_ca_axis_group_reduce (VALUE self, VALUE vgaxes, VALUE vbundles, VALUE vop)
|
|
|
243
322
|
GetCArray(self, src);
|
|
244
323
|
|
|
245
324
|
if ( src->ndim <= 0 ) {
|
|
246
|
-
rb_raise(
|
|
325
|
+
rb_raise(rb_eArgError, "axis_group_reduce: scalar source");
|
|
247
326
|
}
|
|
248
327
|
|
|
249
328
|
/* --- group (slab) axes --- */
|
|
@@ -255,7 +334,6 @@ rb_ca_axis_group_reduce (VALUE self, VALUE vgaxes, VALUE vbundles, VALUE vop)
|
|
|
255
334
|
int8_t axes[CA_RANK_MAX];
|
|
256
335
|
char is_group[CA_RANK_MAX];
|
|
257
336
|
for ( int8_t i = 0; i < src->ndim; i++ ) is_group[i] = 0;
|
|
258
|
-
ca_size_t group_prod = 1;
|
|
259
337
|
for ( long i = 0; i < ngroup; i++ ) {
|
|
260
338
|
int a = NUM2INT(RARRAY_AREF(vgaxes, i));
|
|
261
339
|
if ( a < 0 || a >= src->ndim ) {
|
|
@@ -269,7 +347,6 @@ rb_ca_axis_group_reduce (VALUE self, VALUE vgaxes, VALUE vbundles, VALUE vop)
|
|
|
269
347
|
}
|
|
270
348
|
is_group[a] = 1;
|
|
271
349
|
axes[i] = (int8_t) a;
|
|
272
|
-
group_prod *= src->dim[a];
|
|
273
350
|
}
|
|
274
351
|
|
|
275
352
|
/* --- bundles: small per-group code tables (metadata, kept alive) --- */
|
|
@@ -303,12 +380,23 @@ rb_ca_axis_group_reduce (VALUE self, VALUE vgaxes, VALUE vbundles, VALUE vop)
|
|
|
303
380
|
rb_raise(rb_eArgError, "axis_group_reduce: bundle k must be positive");
|
|
304
381
|
}
|
|
305
382
|
|
|
306
|
-
/*
|
|
383
|
+
/* The code table is copied out and the source let go of at once, rather
|
|
384
|
+
than held attached for the length of the walk. Everything between here
|
|
385
|
+
and the end of the walk can raise -- a validation just below, a gather
|
|
386
|
+
that cannot convert a cell, an object-lane callback into Ruby -- and a
|
|
387
|
+
raise jumps over every detach, leaving the source materialised with
|
|
388
|
+
nothing left to release it. The copy rides a temporary buffer that the
|
|
389
|
+
unwind collects. */
|
|
307
390
|
VALUE v32 = rb_ca_wrap_readonly(vcodes, INT2NUM(CA_INT32));
|
|
308
391
|
rb_ary_push((VALUE) keep, v32);
|
|
309
392
|
GetCArray(v32, bundle_ca[bi]);
|
|
310
|
-
|
|
311
|
-
|
|
393
|
+
{
|
|
394
|
+
size_t nbytes = (size_t) bundle_ca[bi]->elements * sizeof(int32_t);
|
|
395
|
+
VALUE vbuf = rb_str_tmp_new((long) nbytes);
|
|
396
|
+
rb_ary_push((VALUE) keep, vbuf);
|
|
397
|
+
ca_copy_data(bundle_ca[bi], RSTRING_PTR(vbuf));
|
|
398
|
+
bundle_codes[bi] = (int32_t *) RSTRING_PTR(vbuf);
|
|
399
|
+
}
|
|
312
400
|
|
|
313
401
|
int nb = (int) RARRAY_LEN(vbaxes);
|
|
314
402
|
if ( nb <= 0 || nb > src->ndim ) {
|
|
@@ -363,13 +451,21 @@ rb_ca_axis_group_reduce (VALUE self, VALUE vgaxes, VALUE vbundles, VALUE vop)
|
|
|
363
451
|
}
|
|
364
452
|
}
|
|
365
453
|
|
|
366
|
-
/* --- band layout + output shape [K_total, *band_dims] ---
|
|
367
|
-
|
|
454
|
+
/* --- band layout + output shape [K_total, *band_dims] ---
|
|
455
|
+
band is the product of the band dims, taken directly rather than as
|
|
456
|
+
src->elements / group_prod: a zero-length *group* axis makes group_prod
|
|
457
|
+
zero, and the division has to answer something. Answering 0 made nout 0
|
|
458
|
+
while the output really had K_total x band cells, so every finalisation
|
|
459
|
+
loop below ran zero times and the freshly zeroed buffer went back to the
|
|
460
|
+
caller unmasked -- a mean, a min and a variance all reported as 0.0.
|
|
461
|
+
Taken directly, nout == co->elements for every shape, and the group with
|
|
462
|
+
no cell in it takes the same empty-group path as any other. */
|
|
463
|
+
ca_size_t band = 1;
|
|
368
464
|
ca_size_t odim[CA_RANK_MAX];
|
|
369
465
|
int8_t ondim = 1;
|
|
370
466
|
odim[0] = K_total;
|
|
371
467
|
for ( int8_t i = 0; i < src->ndim; i++ ) {
|
|
372
|
-
if ( ! is_group[i] ) odim[ondim++] = src->dim[i];
|
|
468
|
+
if ( ! is_group[i] ) { odim[ondim++] = src->dim[i]; band *= src->dim[i]; }
|
|
373
469
|
}
|
|
374
470
|
ca_size_t nout = K_total * band;
|
|
375
471
|
|
|
@@ -380,10 +476,9 @@ rb_ca_axis_group_reduce (VALUE self, VALUE vgaxes, VALUE vbundles, VALUE vop)
|
|
|
380
476
|
case CA_INT32: case CA_UINT32: case CA_INT64: case CA_UINT64:
|
|
381
477
|
case CA_FLOAT32: case CA_FLOAT64: break;
|
|
382
478
|
default:
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
src->data_type);
|
|
479
|
+
rb_raise(rb_eCADataTypeError,
|
|
480
|
+
"axis_group: %"PRIsVALUE" over a group is not supported for data type %s",
|
|
481
|
+
rb_sym2str(vop), ca_type_name[src->data_type]);
|
|
387
482
|
}
|
|
388
483
|
|
|
389
484
|
/* output data type per op */
|
|
@@ -392,35 +487,52 @@ rb_ca_axis_group_reduce (VALUE self, VALUE vgaxes, VALUE vbundles, VALUE vop)
|
|
|
392
487
|
else if ( op == GR_MINADDR || op == GR_MAXADDR ) out_dt = CA_INT64;
|
|
393
488
|
else if ( op == GR_ALL || op == GR_ANY ) out_dt = CA_BOOLEAN;
|
|
394
489
|
else if ( op == GR_ACCUM ) out_dt = src->data_type;
|
|
490
|
+
else if ( op == GR_MIN || op == GR_MAX )
|
|
491
|
+
out_dt = ( src->data_type == CA_BOOLEAN ) ? CA_UINT64 : src->data_type;
|
|
395
492
|
VALUE vout = rb_carray_new(out_dt, ondim, odim, 0, NULL);
|
|
396
493
|
GetCArray(vout, co);
|
|
397
494
|
|
|
398
495
|
/* --- accumulator buffers (only those the op needs; all O(nout)) --- */
|
|
399
|
-
|
|
400
|
-
|
|
496
|
+
/* Scratch that the unwind collects. A raise anywhere in the walk -- an
|
|
497
|
+
object-lane callback into Ruby, a gather that cannot convert a cell --
|
|
498
|
+
jumps over every release below, so these ride ALLOCV rather than plain
|
|
499
|
+
malloc: the temporary buffer behind each holder is freed whether the
|
|
500
|
+
function returns or unwinds. */
|
|
501
|
+
volatile VALUE h_cnt = 0, h_sum = 0, h_sumsq = 0, h_prod = 0, h_seen = 0,
|
|
502
|
+
h_xbuf = 0, h_nz = 0, h_mnaddr = 0, h_mxaddr = 0, h_band = 0;
|
|
503
|
+
ca_size_t *cnt = ALLOCV_N(ca_size_t, h_cnt, nout);
|
|
504
|
+
MEMZERO(cnt, ca_size_t, nout);
|
|
505
|
+
double *sum = NULL, *sumsq = NULL, *prod = NULL;
|
|
506
|
+
boolean8_t *seen_num = NULL; /* has a number (not a NaN) landed here? */
|
|
507
|
+
char *xbuf = NULL; /* running extremum, in the source type */
|
|
401
508
|
ca_size_t *nz = NULL;
|
|
402
509
|
int64_t *mnaddr = NULL, *mxaddr = NULL; /* flat source addr of min / max */
|
|
403
510
|
ca_size_t *band_addr = NULL; /* raveled addr of each band cell */
|
|
404
511
|
if ( op == GR_SUM || op == GR_MEAN || op == GR_VARIANCE || op == GR_STDDEV ||
|
|
405
512
|
op == GR_VARIANCEP || op == GR_STDDEVP ) {
|
|
406
|
-
sum =
|
|
513
|
+
sum = ALLOCV_N(double, h_sum, nout); MEMZERO(sum, double, nout);
|
|
407
514
|
}
|
|
408
515
|
if ( op == GR_PROD ) {
|
|
409
|
-
prod =
|
|
516
|
+
prod = ALLOCV_N(double, h_prod, nout);
|
|
410
517
|
for ( ca_size_t o = 0; o < nout; o++ ) prod[o] = 1.0;
|
|
411
518
|
}
|
|
412
|
-
if ( op == GR_MIN || op ==
|
|
413
|
-
|
|
414
|
-
|
|
519
|
+
if ( op == GR_MIN || op == GR_MAX ||
|
|
520
|
+
op == GR_MINADDR || op == GR_MAXADDR ) {
|
|
521
|
+
seen_num = ALLOCV_N(boolean8_t, h_seen, nout);
|
|
522
|
+
MEMZERO(seen_num, boolean8_t, nout);
|
|
415
523
|
}
|
|
416
|
-
if ( op ==
|
|
417
|
-
|
|
418
|
-
|
|
524
|
+
if ( op == GR_MIN || op == GR_MAX ) {
|
|
525
|
+
/* the walk writes the extremum straight into the output, in its own type */
|
|
526
|
+
MEMZERO(co->ptr, char, (size_t) nout * co->bytes);
|
|
527
|
+
}
|
|
528
|
+
if ( op == GR_MINADDR || op == GR_MAXADDR ) {
|
|
529
|
+
xbuf = ALLOCV_N(char, h_xbuf, (size_t) nout * src->bytes);
|
|
530
|
+
MEMZERO(xbuf, char, (size_t) nout * src->bytes);
|
|
419
531
|
}
|
|
420
|
-
if ( op == GR_MINADDR ) { mnaddr =
|
|
421
|
-
if ( op == GR_MAXADDR ) { mxaddr =
|
|
532
|
+
if ( op == GR_MINADDR ) { mnaddr = ALLOCV_N(int64_t, h_mnaddr, nout); MEMZERO(mnaddr, int64_t, nout); }
|
|
533
|
+
if ( op == GR_MAXADDR ) { mxaddr = ALLOCV_N(int64_t, h_mxaddr, nout); MEMZERO(mxaddr, int64_t, nout); }
|
|
422
534
|
if ( op == GR_ALL || op == GR_ANY ) {
|
|
423
|
-
nz =
|
|
535
|
+
nz = ALLOCV_N(ca_size_t, h_nz, nout); MEMZERO(nz, ca_size_t, nout);
|
|
424
536
|
}
|
|
425
537
|
|
|
426
538
|
/* min_addr / max_addr need the flat raveled source address of each cell.
|
|
@@ -436,7 +548,7 @@ rb_ca_axis_group_reduce (VALUE self, VALUE vgaxes, VALUE vbundles, VALUE vop)
|
|
|
436
548
|
int band_axis[CA_RANK_MAX]; int nband_axes = 0;
|
|
437
549
|
for ( int8_t k = 0; k < src->ndim; k++ )
|
|
438
550
|
if ( ! is_group[k] ) band_axis[nband_axes++] = k;
|
|
439
|
-
band_addr =
|
|
551
|
+
band_addr = ALLOCV_N(ca_size_t, h_band, band > 0 ? band : 1);
|
|
440
552
|
for ( ca_size_t bb = 0; bb < band; bb++ ) {
|
|
441
553
|
ca_size_t rem = bb, addr = 0;
|
|
442
554
|
for ( int j = nband_axes - 1; j >= 0; j-- ) { /* last band axis fastest */
|
|
@@ -459,27 +571,13 @@ rb_ca_axis_group_reduce (VALUE self, VALUE vgaxes, VALUE vbundles, VALUE vop)
|
|
|
459
571
|
GROUP_DISPATCH( cnt[o] += 1; sum[o] += v; );
|
|
460
572
|
for ( ca_size_t o = 0; o < nout; o++ )
|
|
461
573
|
if ( cnt[o] > 0 ) sum[o] /= (double) cnt[o]; /* sum -> mean */
|
|
462
|
-
sumsq =
|
|
574
|
+
sumsq = ALLOCV_N(double, h_sumsq, nout); MEMZERO(sumsq, double, nout);
|
|
463
575
|
GROUP_DISPATCH( { double _d = v - sum[o]; sumsq[o] += _d * _d; } );
|
|
464
576
|
}
|
|
465
|
-
else if ( op == GR_MINADDR ) {
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
ca_size_t faddr = band_addr[b] + gaddr;
|
|
470
|
-
mn[o] = v; mnaddr[o] = (int64_t) faddr;
|
|
471
|
-
}
|
|
472
|
-
);
|
|
473
|
-
}
|
|
474
|
-
else if ( op == GR_MAXADDR ) {
|
|
475
|
-
GROUP_DISPATCH(
|
|
476
|
-
cnt[o] += 1;
|
|
477
|
-
if ( v > mx[o] ) {
|
|
478
|
-
ca_size_t faddr = band_addr[b] + gaddr;
|
|
479
|
-
mx[o] = v; mxaddr[o] = (int64_t) faddr;
|
|
480
|
-
}
|
|
481
|
-
);
|
|
482
|
-
}
|
|
577
|
+
else if ( op == GR_MINADDR ) { GROUP_DISPATCH_T(GMINADDR); }
|
|
578
|
+
else if ( op == GR_MAXADDR ) { GROUP_DISPATCH_T(GMAXADDR); }
|
|
579
|
+
else if ( op == GR_MIN ) { GROUP_DISPATCH_EXTREMUM(<); }
|
|
580
|
+
else if ( op == GR_MAX ) { GROUP_DISPATCH_EXTREMUM(>); }
|
|
483
581
|
else if ( op == GR_ACCUM ) {
|
|
484
582
|
MEMZERO(co->ptr, char, (size_t) nout * co->bytes);
|
|
485
583
|
switch ( ca->data_type ) {
|
|
@@ -502,8 +600,6 @@ rb_ca_axis_group_reduce (VALUE self, VALUE vgaxes, VALUE vbundles, VALUE vop)
|
|
|
502
600
|
cnt[o] += 1;
|
|
503
601
|
if ( sum ) sum[o] += v;
|
|
504
602
|
if ( prod ) prod[o] *= v;
|
|
505
|
-
if ( mn ) { if ( v < mn[o] ) mn[o] = v; }
|
|
506
|
-
if ( mx ) { if ( v > mx[o] ) mx[o] = v; }
|
|
507
603
|
if ( nz ) { if ( v != 0.0 ) nz[o] += 1; }
|
|
508
604
|
);
|
|
509
605
|
}
|
|
@@ -521,8 +617,8 @@ rb_ca_axis_group_reduce (VALUE self, VALUE vgaxes, VALUE vbundles, VALUE vop)
|
|
|
521
617
|
/* Empty / all-masked groups follow the same zero-contribution contract as
|
|
522
618
|
CArray reductions (ERI, matching the categorical sibling): an identity-
|
|
523
619
|
bearing op returns its identity (sum 0, prod 1, count 0, all true, any
|
|
524
|
-
false), a ratio / extremum returns UNDEF. Sample variance/stddev: n
|
|
525
|
-
UNDEF
|
|
620
|
+
false), a ratio / extremum returns UNDEF. Sample variance/stddev: n<2
|
|
621
|
+
UNDEF (n-1 is zero), n>=2 the formula. */
|
|
526
622
|
if ( op == GR_COUNT ) {
|
|
527
623
|
int64_t *out = (int64_t *) co->ptr;
|
|
528
624
|
for ( ca_size_t o = 0; o < nout; o++ ) out[o] = (int64_t) cnt[o];
|
|
@@ -531,13 +627,20 @@ rb_ca_axis_group_reduce (VALUE self, VALUE vgaxes, VALUE vbundles, VALUE vop)
|
|
|
531
627
|
int64_t *out = (int64_t *) co->ptr;
|
|
532
628
|
int64_t *addr = ( op == GR_MINADDR ) ? mnaddr : mxaddr;
|
|
533
629
|
for ( ca_size_t o = 0; o < nout; o++ ) {
|
|
534
|
-
|
|
630
|
+
/* Nothing present, or nothing but NaN: either way no cell here won,
|
|
631
|
+
and the core answers UNDEF for both. */
|
|
632
|
+
if ( cnt[o] == 0 || ! seen_num[o] ) { out[o] = 0; MARK_UNDEF(o); }
|
|
535
633
|
else out[o] = addr[o];
|
|
536
634
|
}
|
|
537
635
|
}
|
|
538
636
|
else if ( op == GR_ACCUM ) {
|
|
539
637
|
/* already folded in place, in the source's own type; empty groups hold 0 */
|
|
540
638
|
}
|
|
639
|
+
else if ( op == GR_MIN || op == GR_MAX ) {
|
|
640
|
+
/* the walk wrote the extremum in place, in the source's own type; a group
|
|
641
|
+
with no cell in it has nothing to report */
|
|
642
|
+
for ( ca_size_t o = 0; o < nout; o++ ) if ( cnt[o] == 0 ) MARK_UNDEF(o);
|
|
643
|
+
}
|
|
541
644
|
else if ( op == GR_ALL ) {
|
|
542
645
|
boolean8_t *out = (boolean8_t *) co->ptr; /* empty -> true (vacuous) */
|
|
543
646
|
for ( ca_size_t o = 0; o < nout; o++ )
|
|
@@ -558,16 +661,9 @@ rb_ca_axis_group_reduce (VALUE self, VALUE vgaxes, VALUE vbundles, VALUE vop)
|
|
|
558
661
|
if ( cnt[o] == 0 ) { out[o] = 0.0; MARK_UNDEF(o); }
|
|
559
662
|
else out[o] = sum[o] / (double) cnt[o];
|
|
560
663
|
break;
|
|
561
|
-
case GR_MIN:
|
|
562
|
-
if ( cnt[o] == 0 ) { out[o] = 0.0; MARK_UNDEF(o); } else out[o] = mn[o];
|
|
563
|
-
break;
|
|
564
|
-
case GR_MAX:
|
|
565
|
-
if ( cnt[o] == 0 ) { out[o] = 0.0; MARK_UNDEF(o); } else out[o] = mx[o];
|
|
566
|
-
break;
|
|
567
664
|
case GR_VARIANCE:
|
|
568
665
|
case GR_STDDEV:
|
|
569
|
-
if ( cnt[o]
|
|
570
|
-
else if ( cnt[o] == 1 ) { out[o] = 0.0; } /* n=1 contract */
|
|
666
|
+
if ( cnt[o] < 2 ) { out[o] = 0.0; MARK_UNDEF(o); }
|
|
571
667
|
else { double var = sumsq[o] / ( (double) cnt[o] - 1.0 );
|
|
572
668
|
out[o] = ( op == GR_STDDEV ) ? sqrt(var) : var; }
|
|
573
669
|
break;
|
|
@@ -582,17 +678,10 @@ rb_ca_axis_group_reduce (VALUE self, VALUE vgaxes, VALUE vbundles, VALUE vop)
|
|
|
582
678
|
}
|
|
583
679
|
#undef MARK_UNDEF
|
|
584
680
|
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
if ( prod ) xfree(prod);
|
|
590
|
-
if ( mn ) xfree(mn);
|
|
591
|
-
if ( mx ) xfree(mx);
|
|
592
|
-
if ( nz ) xfree(nz);
|
|
593
|
-
if ( mnaddr ) xfree(mnaddr);
|
|
594
|
-
if ( mxaddr ) xfree(mxaddr);
|
|
595
|
-
if ( band_addr ) xfree(band_addr);
|
|
681
|
+
ALLOCV_END(h_cnt); ALLOCV_END(h_sum); ALLOCV_END(h_sumsq);
|
|
682
|
+
ALLOCV_END(h_prod); ALLOCV_END(h_seen); ALLOCV_END(h_xbuf);
|
|
683
|
+
ALLOCV_END(h_nz); ALLOCV_END(h_mnaddr); ALLOCV_END(h_mxaddr);
|
|
684
|
+
ALLOCV_END(h_band);
|
|
596
685
|
|
|
597
686
|
RB_GC_GUARD(keep);
|
|
598
687
|
return vout;
|
|
@@ -763,7 +852,9 @@ group_scan_build_plan (ca_iter_state *st, boolean8_t *m,
|
|
|
763
852
|
emits its own value: a per-group `seen` byte initialises the accumulator
|
|
764
853
|
lazily on first hit — no sentinel like HUGE_VAL, which an integer data type could
|
|
765
854
|
not represent. CMP is > for max, < for min: a later member replaces the
|
|
766
|
-
running extremum when
|
|
855
|
+
running extremum when CA_EXTREMUM_REPLACES (a NaN never does, and a
|
|
856
|
+
number displaces a leading NaN, as the core cummax / cummin and the
|
|
857
|
+
reduce twins above do). A cell masked within its group holds the
|
|
767
858
|
current extremum once a member has been seen (output NOT masked, like sum);
|
|
768
859
|
before any member the extremum is undefined so the cell stays UNDEF (the
|
|
769
860
|
CArray reduction contract: empty max/min has no value — deliberately NOT the
|
|
@@ -776,7 +867,8 @@ group_scan_build_plan (ca_iter_state *st, boolean8_t *m,
|
|
|
776
867
|
boolean8_t *m; \
|
|
777
868
|
ca_size_t b = 0; \
|
|
778
869
|
int ready = 0; \
|
|
779
|
-
|
|
870
|
+
volatile VALUE h_acce = 0; /* freed by the unwind if the walk raises */ \
|
|
871
|
+
T *acce = ALLOCV_N(T, h_acce, K_total); \
|
|
780
872
|
T *outp = (T *) co->ptr; \
|
|
781
873
|
CA_FOR_EACH_SLAB(st, ca, axes, (int8_t) ngroup, CA_KERNEL_READ, p, m) { \
|
|
782
874
|
ca_size_t SE = st.slab_elements; \
|
|
@@ -803,13 +895,15 @@ group_scan_build_plan (ca_iter_state *st, boolean8_t *m,
|
|
|
803
895
|
} \
|
|
804
896
|
T rv = *(T *)(p + sw_doff[e]); \
|
|
805
897
|
if ( ! seen[code] ) { acce[code] = rv; seen[code] = 1; } \
|
|
806
|
-
else if ( rv
|
|
898
|
+
else if ( CA_EXTREMUM_REPLACES(rv, acce[code], CMP) ) { \
|
|
899
|
+
acce[code] = rv; \
|
|
900
|
+
} \
|
|
807
901
|
outp[addr] = acce[code]; \
|
|
808
902
|
} \
|
|
809
903
|
} \
|
|
810
904
|
b++; \
|
|
811
905
|
} \
|
|
812
|
-
|
|
906
|
+
ALLOCV_END(h_acce); \
|
|
813
907
|
} while (0)
|
|
814
908
|
|
|
815
909
|
#define GROUP_SCAN_EXTREMUM_DISPATCH(CMP) \
|
|
@@ -841,19 +935,19 @@ group_scan_build_plan (ca_iter_state *st, boolean8_t *m,
|
|
|
841
935
|
both unmasked (matching core object cumsum / cumprod); cummax / cummin hold
|
|
842
936
|
the running VALUE only once a member has been seen and stay UNDEF before that
|
|
843
937
|
(no identity for arbitrary objects). GW_SKIP (excluded) stays UNDEF. accn /
|
|
844
|
-
seen are reset at each band; acco carries the running VALUE.
|
|
938
|
+
seen are reset at each band; acco carries the running VALUE.
|
|
939
|
+
Expanded only in group_scan_object_body, whose walk state is c->st. */
|
|
845
940
|
#define GROUP_SCAN_OBJECT_WALK(EMIT, HOLD) \
|
|
846
941
|
do { \
|
|
847
|
-
ca_iter_state st; \
|
|
848
942
|
char *p; \
|
|
849
943
|
boolean8_t *m; \
|
|
850
944
|
ca_size_t b = 0; \
|
|
851
945
|
int ready = 0; \
|
|
852
|
-
CA_FOR_EACH_SLAB(st, ca, axes, (int8_t) ngroup, CA_KERNEL_READ, p, m) {
|
|
853
|
-
ca_size_t SE = st.slab_elements;
|
|
946
|
+
CA_FOR_EACH_SLAB(c->st, ca, axes, (int8_t) ngroup, CA_KERNEL_READ, p, m) {\
|
|
947
|
+
ca_size_t SE = c->st.slab_elements; \
|
|
854
948
|
if ( SE > 0 ) { \
|
|
855
949
|
if ( ! ready ) { \
|
|
856
|
-
group_scan_build_plan(&st, m, n_bundles, bundle_codes, bundle_k
|
|
950
|
+
group_scan_build_plan(&c->st, m, n_bundles, bundle_codes, bundle_k,\
|
|
857
951
|
bundle_placeval, bundle_nconsumed, \
|
|
858
952
|
bundle_slot, bundle_cstride, ngroup, \
|
|
859
953
|
grstride, sw_code, sw_doff, sw_moff, sw_addr);\
|
|
@@ -878,6 +972,137 @@ group_scan_build_plan (ca_iter_state *st, boolean8_t *m,
|
|
|
878
972
|
} \
|
|
879
973
|
} while (0)
|
|
880
974
|
|
|
975
|
+
/* Marks output cell o UNDEF, creating the output mask on first use. Used
|
|
976
|
+
by the scan walks, in functions that hold the output as `co` and the mask
|
|
977
|
+
pointer as `omask`. */
|
|
978
|
+
#define MARK_OUT_UNDEF(o) do { \
|
|
979
|
+
if ( ! omask ) { \
|
|
980
|
+
ca_create_mask(co); \
|
|
981
|
+
omask = (boolean8_t *) co->mask->ptr; \
|
|
982
|
+
} \
|
|
983
|
+
omask[o] = 1; \
|
|
984
|
+
} while (0)
|
|
985
|
+
|
|
986
|
+
/* The CA_OBJECT lane of __axis_group_scan__. Every cell calls into Ruby,
|
|
987
|
+
so the walk can raise part way; its state lives in the caller's frame, in
|
|
988
|
+
c->st, and ca_iter_ensure finishes it however this body leaves. The
|
|
989
|
+
buffers are ALLOCV-backed, so the unwind collects them. */
|
|
990
|
+
typedef struct {
|
|
991
|
+
ca_iter_state st;
|
|
992
|
+
CArray *ca;
|
|
993
|
+
CArray *co;
|
|
994
|
+
int op;
|
|
995
|
+
int8_t *axes;
|
|
996
|
+
long ngroup;
|
|
997
|
+
int n_bundles;
|
|
998
|
+
int32_t **bundle_codes;
|
|
999
|
+
ca_size_t *bundle_k;
|
|
1000
|
+
ca_size_t *bundle_placeval;
|
|
1001
|
+
int *bundle_nconsumed;
|
|
1002
|
+
int (*bundle_slot)[CA_RANK_MAX];
|
|
1003
|
+
ca_size_t (*bundle_cstride)[CA_RANK_MAX];
|
|
1004
|
+
ca_size_t *grstride;
|
|
1005
|
+
ca_size_t *band_addr;
|
|
1006
|
+
ca_size_t K_total;
|
|
1007
|
+
ca_size_t *sw_code;
|
|
1008
|
+
ca_size_t *sw_doff;
|
|
1009
|
+
ca_size_t *sw_moff;
|
|
1010
|
+
ca_size_t *sw_addr;
|
|
1011
|
+
ca_size_t *accn;
|
|
1012
|
+
char *seen;
|
|
1013
|
+
VALUE *acco;
|
|
1014
|
+
} group_scan_object_t;
|
|
1015
|
+
|
|
1016
|
+
static VALUE
|
|
1017
|
+
group_scan_object_body (VALUE arg)
|
|
1018
|
+
{
|
|
1019
|
+
group_scan_object_t *c = (group_scan_object_t *) arg;
|
|
1020
|
+
CArray *ca = c->ca;
|
|
1021
|
+
CArray *co = c->co;
|
|
1022
|
+
int op = c->op;
|
|
1023
|
+
int8_t *axes = c->axes;
|
|
1024
|
+
long ngroup = c->ngroup;
|
|
1025
|
+
int n_bundles = c->n_bundles;
|
|
1026
|
+
int32_t **bundle_codes = c->bundle_codes;
|
|
1027
|
+
ca_size_t *bundle_k = c->bundle_k;
|
|
1028
|
+
ca_size_t *bundle_placeval = c->bundle_placeval;
|
|
1029
|
+
int *bundle_nconsumed = c->bundle_nconsumed;
|
|
1030
|
+
int (*bundle_slot)[CA_RANK_MAX] = c->bundle_slot;
|
|
1031
|
+
ca_size_t (*bundle_cstride)[CA_RANK_MAX] = c->bundle_cstride;
|
|
1032
|
+
ca_size_t *grstride = c->grstride;
|
|
1033
|
+
ca_size_t *band_addr = c->band_addr;
|
|
1034
|
+
ca_size_t K_total = c->K_total;
|
|
1035
|
+
ca_size_t *sw_code = c->sw_code;
|
|
1036
|
+
ca_size_t *sw_doff = c->sw_doff;
|
|
1037
|
+
ca_size_t *sw_moff = c->sw_moff;
|
|
1038
|
+
ca_size_t *sw_addr = c->sw_addr;
|
|
1039
|
+
ca_size_t *accn = c->accn;
|
|
1040
|
+
char *seen = c->seen;
|
|
1041
|
+
VALUE *acco = c->acco;
|
|
1042
|
+
boolean8_t *omask = NULL;
|
|
1043
|
+
|
|
1044
|
+
if ( op == GS_CUMCOUNT ) { /* int64 1-based running count */
|
|
1045
|
+
int64_t *outi = (int64_t *) co->ptr;
|
|
1046
|
+
GROUP_SCAN_OBJECT_WALK(
|
|
1047
|
+
accn[code] += 1; outi[addr] = (int64_t) accn[code];,
|
|
1048
|
+
outi[addr] = (int64_t) accn[code];
|
|
1049
|
+
);
|
|
1050
|
+
}
|
|
1051
|
+
else { /* object running VALUE */
|
|
1052
|
+
VALUE *outo = (VALUE *) co->ptr;
|
|
1053
|
+
switch ( op ) {
|
|
1054
|
+
case GS_CUMSUM:
|
|
1055
|
+
/* Identity 0 (matching the core object cumsum): a group's acc is lazily
|
|
1056
|
+
seeded to 0 (Fixnum, so 0 + ev promotes to ev's class), so a cell
|
|
1057
|
+
masked before any present member holds 0 unmasked and an all-masked
|
|
1058
|
+
group yields 0 everywhere. seen doubles as the per-band init flag. */
|
|
1059
|
+
GROUP_SCAN_OBJECT_WALK(
|
|
1060
|
+
if ( ! seen[code] ) { acco[code] = INT2FIX(0); seen[code] = 1; }
|
|
1061
|
+
acco[code] = rb_funcall(acco[code], rb_intern("+"), 1, ev);
|
|
1062
|
+
outo[addr] = acco[code];,
|
|
1063
|
+
if ( ! seen[code] ) { acco[code] = INT2FIX(0); seen[code] = 1; }
|
|
1064
|
+
outo[addr] = acco[code];
|
|
1065
|
+
);
|
|
1066
|
+
break;
|
|
1067
|
+
case GS_CUMPROD:
|
|
1068
|
+
/* Identity 1, same lazy-init as cumsum. */
|
|
1069
|
+
GROUP_SCAN_OBJECT_WALK(
|
|
1070
|
+
if ( ! seen[code] ) { acco[code] = INT2FIX(1); seen[code] = 1; }
|
|
1071
|
+
acco[code] = rb_funcall(acco[code], rb_intern("*"), 1, ev);
|
|
1072
|
+
outo[addr] = acco[code];,
|
|
1073
|
+
if ( ! seen[code] ) { acco[code] = INT2FIX(1); seen[code] = 1; }
|
|
1074
|
+
outo[addr] = acco[code];
|
|
1075
|
+
);
|
|
1076
|
+
break;
|
|
1077
|
+
case GS_CUMMAX:
|
|
1078
|
+
/* No identity: seed from the first member, hold only once seen; a cell
|
|
1079
|
+
masked before any member stays UNDEF (empty-max contract). */
|
|
1080
|
+
GROUP_SCAN_OBJECT_WALK(
|
|
1081
|
+
if ( ! seen[code] ) { acco[code] = ev; seen[code] = 1; }
|
|
1082
|
+
else if ( ca_obj_extremum_replaces(ev, acco[code], rb_intern(">")) ) {
|
|
1083
|
+
acco[code] = ev;
|
|
1084
|
+
}
|
|
1085
|
+
outo[addr] = acco[code];,
|
|
1086
|
+
if ( seen[code] ) { outo[addr] = acco[code]; }
|
|
1087
|
+
else { MARK_OUT_UNDEF(addr); }
|
|
1088
|
+
);
|
|
1089
|
+
break;
|
|
1090
|
+
case GS_CUMMIN:
|
|
1091
|
+
GROUP_SCAN_OBJECT_WALK(
|
|
1092
|
+
if ( ! seen[code] ) { acco[code] = ev; seen[code] = 1; }
|
|
1093
|
+
else if ( ca_obj_extremum_replaces(ev, acco[code], rb_intern("<")) ) {
|
|
1094
|
+
acco[code] = ev;
|
|
1095
|
+
}
|
|
1096
|
+
outo[addr] = acco[code];,
|
|
1097
|
+
if ( seen[code] ) { outo[addr] = acco[code]; }
|
|
1098
|
+
else { MARK_OUT_UNDEF(addr); }
|
|
1099
|
+
);
|
|
1100
|
+
break;
|
|
1101
|
+
}
|
|
1102
|
+
}
|
|
1103
|
+
return Qnil;
|
|
1104
|
+
}
|
|
1105
|
+
|
|
881
1106
|
static int
|
|
882
1107
|
group_scan_op_code (VALUE vop)
|
|
883
1108
|
{
|
|
@@ -887,7 +1112,7 @@ group_scan_op_code (VALUE vop)
|
|
|
887
1112
|
else if ( id == rb_intern("cummax") ) return GS_CUMMAX;
|
|
888
1113
|
else if ( id == rb_intern("cummin") ) return GS_CUMMIN;
|
|
889
1114
|
else if ( id == rb_intern("cumprod") ) return GS_CUMPROD;
|
|
890
|
-
rb_raise(rb_eArgError, "axis_group_scan: unsupported op :%
|
|
1115
|
+
rb_raise(rb_eArgError, "axis_group_scan: unsupported op :%"PRIsVALUE, rb_id2str(id));
|
|
891
1116
|
}
|
|
892
1117
|
|
|
893
1118
|
/* __axis_group_scan__(group_axes, bundles, op) — group-keyed segment scan of
|
|
@@ -930,7 +1155,7 @@ rb_ca_axis_group_scan (VALUE self, VALUE vgaxes, VALUE vbundles, VALUE vop)
|
|
|
930
1155
|
GetCArray(self, src);
|
|
931
1156
|
|
|
932
1157
|
if ( src->ndim <= 0 ) {
|
|
933
|
-
rb_raise(
|
|
1158
|
+
rb_raise(rb_eArgError, "axis_group_scan: scalar source");
|
|
934
1159
|
}
|
|
935
1160
|
|
|
936
1161
|
/* --- group (slab) axes (same validation as reduce) --- */
|
|
@@ -990,11 +1215,23 @@ rb_ca_axis_group_scan (VALUE self, VALUE vgaxes, VALUE vbundles, VALUE vop)
|
|
|
990
1215
|
rb_raise(rb_eArgError, "axis_group_scan: bundle k must be positive");
|
|
991
1216
|
}
|
|
992
1217
|
|
|
1218
|
+
/* The code table is copied out and the source let go of at once, rather
|
|
1219
|
+
than held attached for the length of the walk. Everything between here
|
|
1220
|
+
and the end of the walk can raise -- a validation just below, a gather
|
|
1221
|
+
that cannot convert a cell, an object-lane callback into Ruby -- and a
|
|
1222
|
+
raise jumps over every detach, leaving the source materialised with
|
|
1223
|
+
nothing left to release it. The copy rides a temporary buffer that the
|
|
1224
|
+
unwind collects. */
|
|
993
1225
|
VALUE v32 = rb_ca_wrap_readonly(vcodes, INT2NUM(CA_INT32));
|
|
994
1226
|
rb_ary_push((VALUE) keep, v32);
|
|
995
1227
|
GetCArray(v32, bundle_ca[bi]);
|
|
996
|
-
|
|
997
|
-
|
|
1228
|
+
{
|
|
1229
|
+
size_t nbytes = (size_t) bundle_ca[bi]->elements * sizeof(int32_t);
|
|
1230
|
+
VALUE vbuf = rb_str_tmp_new((long) nbytes);
|
|
1231
|
+
rb_ary_push((VALUE) keep, vbuf);
|
|
1232
|
+
ca_copy_data(bundle_ca[bi], RSTRING_PTR(vbuf));
|
|
1233
|
+
bundle_codes[bi] = (int32_t *) RSTRING_PTR(vbuf);
|
|
1234
|
+
}
|
|
998
1235
|
|
|
999
1236
|
int nb = (int) RARRAY_LEN(vbaxes);
|
|
1000
1237
|
if ( nb <= 0 || nb > src->ndim ) {
|
|
@@ -1054,8 +1291,7 @@ rb_ca_axis_group_scan (VALUE self, VALUE vgaxes, VALUE vbundles, VALUE vop)
|
|
|
1054
1291
|
case CA_INT32: case CA_UINT32: case CA_INT64: case CA_UINT64:
|
|
1055
1292
|
case CA_FLOAT32: case CA_FLOAT64: case CA_OBJECT: break;
|
|
1056
1293
|
default:
|
|
1057
|
-
|
|
1058
|
-
rb_raise(rb_eRuntimeError,
|
|
1294
|
+
rb_raise(rb_eCADataTypeError,
|
|
1059
1295
|
"axis_group_scan: unsupported source data_type %d",
|
|
1060
1296
|
src->data_type);
|
|
1061
1297
|
}
|
|
@@ -1091,7 +1327,8 @@ rb_ca_axis_group_scan (VALUE self, VALUE vgaxes, VALUE vbundles, VALUE vop)
|
|
|
1091
1327
|
int band_axis[CA_RANK_MAX]; int nband_axes = 0;
|
|
1092
1328
|
for ( int8_t k = 0; k < src->ndim; k++ )
|
|
1093
1329
|
if ( ! is_group[k] ) band_axis[nband_axes++] = k;
|
|
1094
|
-
|
|
1330
|
+
volatile VALUE h_band2 = 0;
|
|
1331
|
+
ca_size_t *band_addr = ALLOCV_N(ca_size_t, h_band2, band > 0 ? band : 1);
|
|
1095
1332
|
for ( ca_size_t bb = 0; bb < band; bb++ ) {
|
|
1096
1333
|
ca_size_t rem = bb, addr = 0;
|
|
1097
1334
|
for ( int j = nband_axes - 1; j >= 0; j-- ) {
|
|
@@ -1108,86 +1345,41 @@ rb_ca_axis_group_scan (VALUE self, VALUE vgaxes, VALUE vbundles, VALUE vop)
|
|
|
1108
1345
|
member / object identity init) are all tiny; acco (object running VALUE) is
|
|
1109
1346
|
allocated only for the object arithmetic / extremum ops. */
|
|
1110
1347
|
ca_size_t plan_n = (group_prod > 0) ? group_prod : 1;
|
|
1111
|
-
|
|
1112
|
-
|
|
1113
|
-
|
|
1114
|
-
|
|
1115
|
-
|
|
1116
|
-
|
|
1117
|
-
|
|
1348
|
+
/* Scratch that the unwind collects. The object lane calls back into Ruby
|
|
1349
|
+
for every cell, so a raise in the middle of the walk is ordinary here --
|
|
1350
|
+
an operand that will not coerce, a <=> that answers nil -- and it jumps
|
|
1351
|
+
over every release below. ALLOCV frees the buffer behind each holder
|
|
1352
|
+
whether the function returns or unwinds. */
|
|
1353
|
+
volatile VALUE h_code = 0, h_doff = 0, h_moff = 0, h_addr = 0,
|
|
1354
|
+
h_accd = 0, h_accn = 0, h_seen = 0, h_acco = 0;
|
|
1355
|
+
ca_size_t *sw_code = ALLOCV_N(ca_size_t, h_code, plan_n);
|
|
1356
|
+
ca_size_t *sw_doff = ALLOCV_N(ca_size_t, h_doff, plan_n);
|
|
1357
|
+
ca_size_t *sw_moff = ALLOCV_N(ca_size_t, h_moff, plan_n);
|
|
1358
|
+
ca_size_t *sw_addr = ALLOCV_N(ca_size_t, h_addr, plan_n);
|
|
1359
|
+
double *accd = ALLOCV_N(double, h_accd, K_total);
|
|
1360
|
+
ca_size_t *accn = ALLOCV_N(ca_size_t, h_accn, K_total);
|
|
1361
|
+
char *seen = ALLOCV_N(char, h_seen, K_total);
|
|
1118
1362
|
VALUE *acco = NULL;
|
|
1119
1363
|
double acc_init = ( op == GS_CUMPROD ) ? 1.0 : 0.0;
|
|
1120
1364
|
|
|
1121
1365
|
boolean8_t *omask = NULL;
|
|
1122
|
-
#define MARK_OUT_UNDEF(o) do { \
|
|
1123
|
-
if ( ! omask ) { \
|
|
1124
|
-
ca_create_mask(co); \
|
|
1125
|
-
omask = (boolean8_t *) co->mask->ptr; \
|
|
1126
|
-
} \
|
|
1127
|
-
omask[o] = 1; \
|
|
1128
|
-
} while (0)
|
|
1129
1366
|
|
|
1130
1367
|
if ( src->data_type == CA_OBJECT ) {
|
|
1131
|
-
|
|
1132
|
-
|
|
1133
|
-
|
|
1134
|
-
accn[code] += 1; outi[addr] = (int64_t) accn[code];,
|
|
1135
|
-
outi[addr] = (int64_t) accn[code];
|
|
1136
|
-
);
|
|
1137
|
-
}
|
|
1138
|
-
else { /* object running VALUE */
|
|
1139
|
-
VALUE *outo = (VALUE *) co->ptr;
|
|
1140
|
-
acco = ALLOC_N(VALUE, K_total);
|
|
1141
|
-
switch ( op ) {
|
|
1142
|
-
case GS_CUMSUM:
|
|
1143
|
-
/* Identity 0 (matching the core object cumsum): a group's acc is lazily
|
|
1144
|
-
seeded to 0 (Fixnum, so 0 + ev promotes to ev's class), so a cell
|
|
1145
|
-
masked before any present member holds 0 unmasked and an all-masked
|
|
1146
|
-
group yields 0 everywhere. seen doubles as the per-band init flag. */
|
|
1147
|
-
GROUP_SCAN_OBJECT_WALK(
|
|
1148
|
-
if ( ! seen[code] ) { acco[code] = INT2FIX(0); seen[code] = 1; }
|
|
1149
|
-
acco[code] = rb_funcall(acco[code], rb_intern("+"), 1, ev);
|
|
1150
|
-
outo[addr] = acco[code];,
|
|
1151
|
-
if ( ! seen[code] ) { acco[code] = INT2FIX(0); seen[code] = 1; }
|
|
1152
|
-
outo[addr] = acco[code];
|
|
1153
|
-
);
|
|
1154
|
-
break;
|
|
1155
|
-
case GS_CUMPROD:
|
|
1156
|
-
/* Identity 1, same lazy-init as cumsum. */
|
|
1157
|
-
GROUP_SCAN_OBJECT_WALK(
|
|
1158
|
-
if ( ! seen[code] ) { acco[code] = INT2FIX(1); seen[code] = 1; }
|
|
1159
|
-
acco[code] = rb_funcall(acco[code], rb_intern("*"), 1, ev);
|
|
1160
|
-
outo[addr] = acco[code];,
|
|
1161
|
-
if ( ! seen[code] ) { acco[code] = INT2FIX(1); seen[code] = 1; }
|
|
1162
|
-
outo[addr] = acco[code];
|
|
1163
|
-
);
|
|
1164
|
-
break;
|
|
1165
|
-
case GS_CUMMAX:
|
|
1166
|
-
/* No identity: seed from the first member, hold only once seen; a cell
|
|
1167
|
-
masked before any member stays UNDEF (empty-max contract). */
|
|
1168
|
-
GROUP_SCAN_OBJECT_WALK(
|
|
1169
|
-
if ( ! seen[code] ) { acco[code] = ev; seen[code] = 1; }
|
|
1170
|
-
else if ( NUM2INT(rb_funcall(ev, rb_intern("<=>"), 1, acco[code])) > 0 ) {
|
|
1171
|
-
acco[code] = ev;
|
|
1172
|
-
}
|
|
1173
|
-
outo[addr] = acco[code];,
|
|
1174
|
-
if ( seen[code] ) { outo[addr] = acco[code]; }
|
|
1175
|
-
else { MARK_OUT_UNDEF(addr); }
|
|
1176
|
-
);
|
|
1177
|
-
break;
|
|
1178
|
-
case GS_CUMMIN:
|
|
1179
|
-
GROUP_SCAN_OBJECT_WALK(
|
|
1180
|
-
if ( ! seen[code] ) { acco[code] = ev; seen[code] = 1; }
|
|
1181
|
-
else if ( NUM2INT(rb_funcall(ev, rb_intern("<=>"), 1, acco[code])) < 0 ) {
|
|
1182
|
-
acco[code] = ev;
|
|
1183
|
-
}
|
|
1184
|
-
outo[addr] = acco[code];,
|
|
1185
|
-
if ( seen[code] ) { outo[addr] = acco[code]; }
|
|
1186
|
-
else { MARK_OUT_UNDEF(addr); }
|
|
1187
|
-
);
|
|
1188
|
-
break;
|
|
1189
|
-
}
|
|
1368
|
+
group_scan_object_t c;
|
|
1369
|
+
if ( op != GS_CUMCOUNT ) {
|
|
1370
|
+
acco = ALLOCV_N(VALUE, h_acco, K_total);
|
|
1190
1371
|
}
|
|
1372
|
+
c.ca = ca; c.co = co; c.op = op;
|
|
1373
|
+
c.axes = axes; c.ngroup = ngroup;
|
|
1374
|
+
c.n_bundles = n_bundles; c.bundle_codes = bundle_codes;
|
|
1375
|
+
c.bundle_k = bundle_k; c.bundle_placeval = bundle_placeval;
|
|
1376
|
+
c.bundle_nconsumed = bundle_nconsumed;
|
|
1377
|
+
c.bundle_slot = bundle_slot; c.bundle_cstride = bundle_cstride;
|
|
1378
|
+
c.grstride = grstride; c.band_addr = band_addr; c.K_total = K_total;
|
|
1379
|
+
c.sw_code = sw_code; c.sw_doff = sw_doff;
|
|
1380
|
+
c.sw_moff = sw_moff; c.sw_addr = sw_addr;
|
|
1381
|
+
c.accn = accn; c.seen = seen; c.acco = acco;
|
|
1382
|
+
ca_iter_ensure(1, &c.st, group_scan_object_body, (VALUE) &c);
|
|
1191
1383
|
}
|
|
1192
1384
|
else { /* native data type dispatch */
|
|
1193
1385
|
switch ( op ) {
|
|
@@ -1217,18 +1409,10 @@ rb_ca_axis_group_scan (VALUE self, VALUE vgaxes, VALUE vbundles, VALUE vop)
|
|
|
1217
1409
|
break;
|
|
1218
1410
|
}
|
|
1219
1411
|
}
|
|
1220
|
-
|
|
1221
|
-
|
|
1222
|
-
|
|
1223
|
-
|
|
1224
|
-
xfree(sw_code);
|
|
1225
|
-
xfree(sw_doff);
|
|
1226
|
-
xfree(sw_moff);
|
|
1227
|
-
xfree(sw_addr);
|
|
1228
|
-
xfree(accd);
|
|
1229
|
-
xfree(accn);
|
|
1230
|
-
xfree(seen);
|
|
1231
|
-
if ( acco ) xfree(acco);
|
|
1412
|
+
|
|
1413
|
+
ALLOCV_END(h_band2); ALLOCV_END(h_code); ALLOCV_END(h_doff);
|
|
1414
|
+
ALLOCV_END(h_moff); ALLOCV_END(h_addr); ALLOCV_END(h_accd);
|
|
1415
|
+
ALLOCV_END(h_accn); ALLOCV_END(h_seen); ALLOCV_END(h_acco);
|
|
1232
1416
|
|
|
1233
1417
|
RB_GC_GUARD(keep);
|
|
1234
1418
|
return vout;
|