carray 3.0.2 → 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 +304 -0
- data/README.md +3 -4
- data/carray.gemspec +1 -1
- data/ext/ca_axis_dispatch.c +225 -0
- data/ext/ca_axis_group.c +177 -99
- data/ext/ca_binop_dispatch.c +54 -32
- data/ext/ca_binop_dispatch.h +1 -0
- data/ext/ca_categorical_iterator.c +66 -71
- 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 +663 -214
- data/ext/ca_kernel_iterator.h +231 -167
- data/ext/ca_monop_dispatch.c +4 -4
- data/ext/ca_monop_dispatch.h +4 -0
- data/ext/ca_obj_array.c +132 -22
- 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 +1 -0
- data/ext/ca_obj_byte_swap.c +40 -40
- data/ext/ca_obj_const_string.c +96 -25
- data/ext/ca_obj_face.c +406 -64
- data/ext/ca_obj_face.h +92 -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 +21 -21
- data/ext/ca_obj_grid.c +181 -124
- data/ext/ca_obj_meld.c +189 -107
- data/ext/ca_obj_moncmp.c +61 -61
- data/ext/ca_obj_monop.c +125 -107
- data/ext/ca_obj_object.c +543 -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 +47 -49
- data/ext/ca_obj_select_axis.c +58 -60
- data/ext/ca_obj_shift.c +22 -102
- data/ext/ca_obj_stack.c +223 -118
- data/ext/ca_obj_stride.c +195 -34
- data/ext/ca_obj_string.c +17 -18
- 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 +149 -27
- data/ext/ca_op_ipower.c +24 -11
- data/ext/ca_op_powi.h +57 -30
- data/ext/ca_pow5_table.h +656 -0
- data/ext/ca_sort_kernels.h +70 -0
- data/ext/ca_sweep_engine.c +102 -79
- data/ext/ca_sweep_engine.h +7 -20
- 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 +70 -27
- data/ext/carray_access.c +455 -211
- data/ext/carray_address_basis.c +177 -36
- data/ext/carray_attribute.c +45 -3
- data/ext/carray_bincount.c +32 -2
- data/ext/carray_broadcast.c +46 -14
- data/ext/carray_call_cfunc.c +0 -28
- data/ext/carray_cast.c +1444 -209
- data/ext/carray_cast_func.rb +34 -21
- data/ext/carray_conversion.c +197 -73
- data/ext/carray_copy.c +32 -11
- data/ext/carray_core.c +514 -122
- data/ext/carray_count.c +140 -87
- data/ext/carray_element.c +18 -27
- data/ext/carray_factorize.c +569 -277
- 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 +385 -3
- data/ext/carray_lazy.c +834 -79
- data/ext/carray_loop.c +81 -65
- data/ext/carray_mask.c +682 -316
- data/ext/carray_math_kernel.h +1 -0
- data/ext/carray_median_percentile.c +221 -76
- data/ext/carray_memory_view.c +254 -248
- data/ext/carray_operator.c +1089 -359
- data/ext/carray_order.c +133 -69
- data/ext/carray_partition.c +102 -56
- data/ext/carray_random.c +273 -239
- data/ext/carray_scatter.c +308 -65
- data/ext/carray_segment.c +257 -0
- data/ext/carray_slab.c +170 -70
- data/ext/carray_sort.c +215 -218
- 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 +0 -2
- data/ext/mk_pow5_table.rb +35 -0
- data/ext/mkkernel.rb +1076 -846
- data/ext/ruby_carray.c +7 -1
- 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 +19 -0
- data/lib/carray/autoload_method_extension.rb +6 -0
- data/lib/carray/axis_group.rb +174 -26
- data/lib/carray/basics.rb +91 -25
- data/lib/carray/bincount_nd.rb +6 -0
- data/lib/carray/block_iterator.rb +118 -48
- data/lib/carray/boolean_reduce.rb +21 -29
- data/lib/carray/categorical.rb +94 -26
- data/lib/carray/categorical_iterator.rb +333 -826
- data/lib/carray/complex.rb +36 -29
- data/lib/carray/conditional.rb +94 -7
- data/lib/carray/const_string.rb +46 -0
- data/lib/carray/frame/concat.rb +157 -6
- data/lib/carray/frame/convert.rb +13 -7
- data/lib/carray/frame/csv_parser.rb +589 -82
- data/lib/carray/frame/describe.rb +79 -0
- data/lib/carray/frame/frame.rb +124 -25
- data/lib/carray/frame/group.rb +189 -63
- data/lib/carray/frame/io.rb +412 -72
- data/lib/carray/frame/join.rb +134 -46
- data/lib/carray/frame/records.rb +37 -16
- data/lib/carray/frame/reshape.rb +448 -0
- data/lib/carray/frame/sort.rb +26 -2
- data/lib/carray/frame/verbs.rb +715 -48
- 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 +29 -11
- data/lib/carray/iterator.rb +14 -2
- data/lib/carray/lazy.rb +54 -30
- data/lib/carray/mask_gap_fill.rb +66 -52
- 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/factorize.rb +2 -2
- 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 +117 -4
- data/lib/carray/methods/join.rb +1 -1
- data/lib/carray/methods/locate_addr.rb +177 -36
- data/lib/carray/methods/mode.rb +18 -1
- data/lib/carray/methods/repeat.rb +1 -20
- data/lib/carray/methods/resize.rb +21 -23
- data/lib/carray/methods/snap.rb +34 -12
- data/lib/carray/methods/unique.rb +11 -2
- data/lib/carray/record_order.rb +208 -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 +51 -27
- data/lib/carray/stack.rb +10 -4
- data/lib/carray/string_operation_extension.rb +4 -3
- 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 +517 -103
- data/lib/carray/window_iterator.rb +127 -72
- data/lib/carray.rb +2 -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_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 +1 -2
- data/yard-stubs/carray_access.rb +5 -4
- data/yard-stubs/carray_broadcast.rb +1 -1
- 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 -3
- data/yard-stubs/carray_count.rb +9 -0
- data/yard-stubs/carray_lazy.rb +5 -1
- data/yard-stubs/carray_loop.rb +4 -1
- data/yard-stubs/carray_mask.rb +34 -10
- data/yard-stubs/carray_math.rb +25 -13
- data/yard-stubs/carray_median_percentile.rb +10 -6
- data/yard-stubs/carray_order.rb +34 -13
- data/yard-stubs/carray_scatter.rb +10 -3
- data/yard-stubs/carray_segment.rb +72 -0
- data/yard-stubs/carray_slab.rb +2 -1
- data/yard-stubs/carray_sort.rb +43 -3
- data/yard-stubs/carray_test.rb +10 -0
- data/yard-stubs/carray_utils.rb +0 -26
- metadata +11 -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 -26005
- data/ext/carray_kernels_reduce_boolean.c +0 -330
- data/ext/carray_kernels_reduce_cumulative.c +0 -14862
- data/ext/carray_kernels_reduce_extreme.c +0 -17494
- data/ext/carray_kernels_reduce_variance.c +0 -3910
- data/ext/carray_kernels_scan.c +0 -3693
- data/ext/carray_kernels_search.c +0 -32218
- data/ext/carray_kernels_sort.c +0 -10626
- data/ext/carray_kernels_triop.c +0 -1392
|
@@ -35,6 +35,7 @@
|
|
|
35
35
|
--------------------------------------------------------------------------- */
|
|
36
36
|
|
|
37
37
|
#include "carray.h"
|
|
38
|
+
#include "carray_internal.h" /* CA_EXTREMUM_REPLACES */
|
|
38
39
|
#include <stdlib.h> /* qsort */
|
|
39
40
|
#include <math.h> /* floor */
|
|
40
41
|
|
|
@@ -82,8 +83,7 @@ rb_ca_categorical_scatter (VALUE self, VALUE rvalue, VALUE rcursor,
|
|
|
82
83
|
rb_raise(rb_eArgError, "__categorical_scatter__: grouped/value data type mismatch");
|
|
83
84
|
}
|
|
84
85
|
|
|
85
|
-
|
|
86
|
-
ca_attach(value);
|
|
86
|
+
ca_attach_n(2, codes, value); /* window: detached before each raise inside */
|
|
87
87
|
cmask = ca_mask_ptr(codes);
|
|
88
88
|
vmask = ca_mask_ptr(value);
|
|
89
89
|
cur = (int64_t *) cursor->ptr;
|
|
@@ -151,7 +151,7 @@ rb_ca_categorical_scatter (VALUE self, VALUE rvalue, VALUE rcursor,
|
|
|
151
151
|
#define CAT_NAN_NEVER(v) (0)
|
|
152
152
|
#define CAT_NAN_CHECK(v) ((v) != (v))
|
|
153
153
|
|
|
154
|
-
#define REDUCEAT_MOMENTS_BODY(T
|
|
154
|
+
#define REDUCEAT_MOMENTS_BODY(T) \
|
|
155
155
|
do { \
|
|
156
156
|
const T *gp = (const T *) grouped->ptr; \
|
|
157
157
|
T *minv = (T *) minp, *maxv = (T *) maxp; \
|
|
@@ -160,17 +160,16 @@ rb_ca_categorical_scatter (VALUE self, VALUE rvalue, VALUE rcursor,
|
|
|
160
160
|
ca_size_t hi = (c + 1 < k) ? (ca_size_t) offs[c+1] : n; \
|
|
161
161
|
ca_size_t j, cnt = 0; \
|
|
162
162
|
double sacc = 0.0; \
|
|
163
|
-
T mn = 0, mx = 0; int seen = 0
|
|
163
|
+
T mn = 0, mx = 0; int seen = 0; \
|
|
164
164
|
for ( j = lo; j < hi; j++ ) { \
|
|
165
165
|
if ( gm && gm[j] ) continue; /* masked value cell */ \
|
|
166
166
|
{ T v = gp[j]; \
|
|
167
167
|
sacc += (double) v; \
|
|
168
|
-
if ( !
|
|
169
|
-
|
|
170
|
-
|
|
168
|
+
if ( ! seen ) { mn = v; mx = v; seen = 1; } \
|
|
169
|
+
else { \
|
|
170
|
+
if ( CA_EXTREMUM_REPLACES(v, mn, <) ) mn = v; \
|
|
171
|
+
if ( CA_EXTREMUM_REPLACES(v, mx, >) ) mx = v; \
|
|
171
172
|
} \
|
|
172
|
-
else if ( ! seen ) { mn = v; mx = v; } /* held until a number lands */\
|
|
173
|
-
seen = 1; \
|
|
174
173
|
cnt++; } \
|
|
175
174
|
} \
|
|
176
175
|
countp[c] = (int64_t) cnt; \
|
|
@@ -222,16 +221,16 @@ rb_ca_reduceat_moments (VALUE self, VALUE roffsets, VALUE rcounts,
|
|
|
222
221
|
maxm = (boolean8_t *) maxs->mask->ptr;
|
|
223
222
|
|
|
224
223
|
switch ( grouped->data_type ) {
|
|
225
|
-
case CA_INT8: REDUCEAT_MOMENTS_BODY(int8_t
|
|
226
|
-
case CA_UINT8: REDUCEAT_MOMENTS_BODY(uint8_t
|
|
227
|
-
case CA_INT16: REDUCEAT_MOMENTS_BODY(int16_t
|
|
228
|
-
case CA_UINT16: REDUCEAT_MOMENTS_BODY(uint16_t
|
|
229
|
-
case CA_INT32: REDUCEAT_MOMENTS_BODY(int32_t
|
|
230
|
-
case CA_UINT32: REDUCEAT_MOMENTS_BODY(uint32_t
|
|
231
|
-
case CA_INT64: REDUCEAT_MOMENTS_BODY(int64_t
|
|
232
|
-
case CA_UINT64: REDUCEAT_MOMENTS_BODY(uint64_t
|
|
233
|
-
case CA_FLOAT32: REDUCEAT_MOMENTS_BODY(float32_t
|
|
234
|
-
case CA_FLOAT64: REDUCEAT_MOMENTS_BODY(float64_t
|
|
224
|
+
case CA_INT8: REDUCEAT_MOMENTS_BODY(int8_t); break;
|
|
225
|
+
case CA_UINT8: REDUCEAT_MOMENTS_BODY(uint8_t); break;
|
|
226
|
+
case CA_INT16: REDUCEAT_MOMENTS_BODY(int16_t); break;
|
|
227
|
+
case CA_UINT16: REDUCEAT_MOMENTS_BODY(uint16_t); break;
|
|
228
|
+
case CA_INT32: REDUCEAT_MOMENTS_BODY(int32_t); break;
|
|
229
|
+
case CA_UINT32: REDUCEAT_MOMENTS_BODY(uint32_t); break;
|
|
230
|
+
case CA_INT64: REDUCEAT_MOMENTS_BODY(int64_t); break;
|
|
231
|
+
case CA_UINT64: REDUCEAT_MOMENTS_BODY(uint64_t); break;
|
|
232
|
+
case CA_FLOAT32: REDUCEAT_MOMENTS_BODY(float32_t); break;
|
|
233
|
+
case CA_FLOAT64: REDUCEAT_MOMENTS_BODY(float64_t); break;
|
|
235
234
|
default:
|
|
236
235
|
rb_raise(rb_eCADataTypeError,
|
|
237
236
|
"__reduceat_moments__: numeric value required (got data_type %d)",
|
|
@@ -298,10 +297,9 @@ ca_nth_element_double (double *a, ca_size_t n, ca_size_t kth)
|
|
|
298
297
|
/* Push NaN to the tail, which is where CArray's sort puts it, so the \
|
|
299
298
|
ki-th element here is the ki-th of the sorted segment. Selection \
|
|
300
299
|
runs on the numeric prefix, since a comparison-based quickselect \
|
|
301
|
-
has nothing to say about a NaN
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
and answers vlo, which is why the median of [NaN, -9.0] is -9.0. */ \
|
|
300
|
+
has nothing to say about a NaN; a position or an upper neighbour \
|
|
301
|
+
landing in the tail is NaN. The blend is CArray#percentile's, so \
|
|
302
|
+
both answer alike for NaN and infinities. */ \
|
|
305
303
|
ca_size_t nnum = m, t0 = 0; \
|
|
306
304
|
while ( t0 < nnum ) { \
|
|
307
305
|
if ( scratch[t0] != scratch[t0] ) { \
|
|
@@ -317,8 +315,10 @@ ca_nth_element_double (double *a, ca_size_t n, ca_size_t kth)
|
|
|
317
315
|
double mn = scratch[ki+1]; ca_size_t t; \
|
|
318
316
|
for ( t = ki + 2; t < nnum; t++ ) if ( scratch[t] < mn ) mn = scratch[t]; \
|
|
319
317
|
vhi = mn; \
|
|
320
|
-
} else vhi =
|
|
321
|
-
|
|
318
|
+
} else vhi = (double) NAN; \
|
|
319
|
+
{ double r = f - (double) ki; \
|
|
320
|
+
outp[c] = ( r == 0.0 || ki + 1 >= m ) ? vlo \
|
|
321
|
+
: vlo * (1.0 - r) + vhi * r; } } \
|
|
322
322
|
} \
|
|
323
323
|
} while (0)
|
|
324
324
|
|
|
@@ -335,6 +335,11 @@ rb_ca_reduceat_percentile (VALUE self, VALUE roffsets, VALUE rp, VALUE rout)
|
|
|
335
335
|
GetCArray(roffsets, offsets);
|
|
336
336
|
GetCArray(rout, out);
|
|
337
337
|
p = NUM2DBL(rp);
|
|
338
|
+
if ( !(p >= 0.0 && p <= 100.0) ) {
|
|
339
|
+
rb_raise(rb_eArgError,
|
|
340
|
+
"percentile: p must be Numeric in [0,100] (got %"PRIsVALUE")",
|
|
341
|
+
rb_inspect(rp));
|
|
342
|
+
}
|
|
338
343
|
k = (int64_t) offsets->elements;
|
|
339
344
|
n = grouped->elements;
|
|
340
345
|
if ( offsets->data_type != CA_INT64 || out->data_type != CA_FLOAT64 ||
|
|
@@ -383,7 +388,7 @@ rb_ca_reduceat_percentile (VALUE self, VALUE roffsets, VALUE rp, VALUE rout)
|
|
|
383
388
|
accumulates the per-segment centred sum of squares; variance = SS / (n-1).
|
|
384
389
|
Centred (not the one-pass sum-of-squares) so the ε-close contract holds — a
|
|
385
390
|
one-pass formula cancels catastrophically. Matches CArray#variance per group:
|
|
386
|
-
count 0
|
|
391
|
+
count 0 or 1 -> masked (the sample variance is undefined), count >= 2 ->
|
|
387
392
|
SS / (count-1).
|
|
388
393
|
|
|
389
394
|
Surface (private): grouped.__reduceat_variance__(offsets, means, counts, out)
|
|
@@ -391,7 +396,7 @@ rb_ca_reduceat_percentile (VALUE self, VALUE roffsets, VALUE rp, VALUE rout)
|
|
|
391
396
|
offsets = int64[k] segment STARTS
|
|
392
397
|
means = float64[k] per-segment mean (ignored where count < 2)
|
|
393
398
|
counts = int64[k] per-segment present count
|
|
394
|
-
out = float64[k] output: variance; count 0
|
|
399
|
+
out = float64[k] output: variance; count 0 or 1 masked
|
|
395
400
|
|
|
396
401
|
--------------------------------------------------------------------------- */
|
|
397
402
|
|
|
@@ -400,8 +405,7 @@ rb_ca_reduceat_percentile (VALUE self, VALUE roffsets, VALUE rp, VALUE rout)
|
|
|
400
405
|
const T *gp = (const T *) grouped->ptr; \
|
|
401
406
|
for ( c = 0; c < k; c++ ) { \
|
|
402
407
|
int64_t cnt = countp[c]; \
|
|
403
|
-
if ( cnt
|
|
404
|
-
if ( cnt == 1 ) { outp[c] = 0.0; continue; } /* n=1 contract: 0.0 */ \
|
|
408
|
+
if ( cnt < 2 ) { outp[c] = 0.0; outm[c] = 1; continue; } /* undefined */ \
|
|
405
409
|
{ double mean = meanp[c], ss = 0.0; \
|
|
406
410
|
ca_size_t lo = (ca_size_t) offs[c]; \
|
|
407
411
|
ca_size_t hi = (c + 1 < k) ? (ca_size_t) offs[c+1] : n, j; \
|
|
@@ -869,7 +873,7 @@ rb_ca_reduceat_wsum_wmean (VALUE self, VALUE roffsets, VALUE rwg,
|
|
|
869
873
|
(empty group cell → 0 + masked, matching __reduceat_moments__).
|
|
870
874
|
--------------------------------------------------------------------------- */
|
|
871
875
|
|
|
872
|
-
#define FIBER_SCATTER_BODY(H_T, C_T
|
|
876
|
+
#define FIBER_SCATTER_BODY(H_T, C_T) \
|
|
873
877
|
do { \
|
|
874
878
|
const H_T *hp = (const H_T *) h->ptr; \
|
|
875
879
|
const C_T *cp = (const C_T *) codes->ptr; \
|
|
@@ -891,18 +895,11 @@ rb_ca_reduceat_wsum_wmean (VALUE self, VALUE roffsets, VALUE rwg,
|
|
|
891
895
|
if ( c < 0 || c >= K ) continue; /* out-of-vocabulary */ \
|
|
892
896
|
out_off = c * band_size + out_outer + inn; \
|
|
893
897
|
v = hp[off]; \
|
|
894
|
-
/* NaN loses every contest, as in the flat twin and the core. min \
|
|
895
|
-
and max are seeded together and only ever move on a number, so a \
|
|
896
|
-
NaN sitting in min means this cell has met nothing but NaN yet. */\
|
|
897
898
|
if ( countp[out_off] == 0 ) { \
|
|
898
899
|
minv[out_off] = v; maxv[out_off] = v; \
|
|
899
|
-
} else
|
|
900
|
-
if (
|
|
901
|
-
|
|
902
|
-
} else { \
|
|
903
|
-
if ( v < minv[out_off] ) minv[out_off] = v; \
|
|
904
|
-
if ( v > maxv[out_off] ) maxv[out_off] = v; \
|
|
905
|
-
} \
|
|
900
|
+
} else { \
|
|
901
|
+
if ( CA_EXTREMUM_REPLACES(v, minv[out_off], <) ) minv[out_off] = v;\
|
|
902
|
+
if ( CA_EXTREMUM_REPLACES(v, maxv[out_off], >) ) maxv[out_off] = v;\
|
|
906
903
|
} \
|
|
907
904
|
countp[out_off]++; \
|
|
908
905
|
sump[out_off] += (double) v; \
|
|
@@ -911,16 +908,16 @@ rb_ca_reduceat_wsum_wmean (VALUE self, VALUE roffsets, VALUE rwg,
|
|
|
911
908
|
} \
|
|
912
909
|
} while (0)
|
|
913
910
|
|
|
914
|
-
#define FIBER_SCATTER_DISPATCH_C(H_T
|
|
911
|
+
#define FIBER_SCATTER_DISPATCH_C(H_T) \
|
|
915
912
|
switch ( codes->data_type ) { \
|
|
916
|
-
case CA_INT8: FIBER_SCATTER_BODY(H_T, int8_t
|
|
917
|
-
case CA_UINT8: FIBER_SCATTER_BODY(H_T, uint8_t
|
|
918
|
-
case CA_INT16: FIBER_SCATTER_BODY(H_T, int16_t
|
|
919
|
-
case CA_UINT16: FIBER_SCATTER_BODY(H_T, uint16_t
|
|
920
|
-
case CA_INT32: FIBER_SCATTER_BODY(H_T, int32_t
|
|
921
|
-
case CA_UINT32: FIBER_SCATTER_BODY(H_T, uint32_t
|
|
922
|
-
case CA_INT64: FIBER_SCATTER_BODY(H_T, int64_t
|
|
923
|
-
case CA_UINT64: FIBER_SCATTER_BODY(H_T, uint64_t
|
|
913
|
+
case CA_INT8: FIBER_SCATTER_BODY(H_T, int8_t); break; \
|
|
914
|
+
case CA_UINT8: FIBER_SCATTER_BODY(H_T, uint8_t); break; \
|
|
915
|
+
case CA_INT16: FIBER_SCATTER_BODY(H_T, int16_t); break; \
|
|
916
|
+
case CA_UINT16: FIBER_SCATTER_BODY(H_T, uint16_t); break; \
|
|
917
|
+
case CA_INT32: FIBER_SCATTER_BODY(H_T, int32_t); break; \
|
|
918
|
+
case CA_UINT32: FIBER_SCATTER_BODY(H_T, uint32_t); break; \
|
|
919
|
+
case CA_INT64: FIBER_SCATTER_BODY(H_T, int64_t); break; \
|
|
920
|
+
case CA_UINT64: FIBER_SCATTER_BODY(H_T, uint64_t); break; \
|
|
924
921
|
default: \
|
|
925
922
|
ca_detach(h); ca_detach(codes); \
|
|
926
923
|
rb_raise(rb_eCADataTypeError, \
|
|
@@ -1005,8 +1002,7 @@ rb_ca_fiber_scatter_moments (VALUE self, VALUE rcodes, VALUE raxis, VALUE rk,
|
|
|
1005
1002
|
band_size = outer_prod * inner_prod;
|
|
1006
1003
|
total = (ca_size_t)(K * band_size);
|
|
1007
1004
|
|
|
1008
|
-
|
|
1009
|
-
ca_attach(codes);
|
|
1005
|
+
ca_attach_n(2, h, codes); /* window: detached before each raise inside */
|
|
1010
1006
|
hm = ca_mask_ptr(h);
|
|
1011
1007
|
cm = ca_mask_ptr(codes);
|
|
1012
1008
|
countp = (int64_t *) counts->ptr;
|
|
@@ -1020,16 +1016,16 @@ rb_ca_fiber_scatter_moments (VALUE self, VALUE rcodes, VALUE raxis, VALUE rk,
|
|
|
1020
1016
|
memset(maxp, 0, (size_t) total * (size_t) h->bytes);
|
|
1021
1017
|
|
|
1022
1018
|
switch ( h->data_type ) {
|
|
1023
|
-
case CA_INT8: FIBER_SCATTER_DISPATCH_C(int8_t
|
|
1024
|
-
case CA_UINT8: FIBER_SCATTER_DISPATCH_C(uint8_t
|
|
1025
|
-
case CA_INT16: FIBER_SCATTER_DISPATCH_C(int16_t
|
|
1026
|
-
case CA_UINT16: FIBER_SCATTER_DISPATCH_C(uint16_t
|
|
1027
|
-
case CA_INT32: FIBER_SCATTER_DISPATCH_C(int32_t
|
|
1028
|
-
case CA_UINT32: FIBER_SCATTER_DISPATCH_C(uint32_t
|
|
1029
|
-
case CA_INT64: FIBER_SCATTER_DISPATCH_C(int64_t
|
|
1030
|
-
case CA_UINT64: FIBER_SCATTER_DISPATCH_C(uint64_t
|
|
1031
|
-
case CA_FLOAT32: FIBER_SCATTER_DISPATCH_C(float32_t
|
|
1032
|
-
case CA_FLOAT64: FIBER_SCATTER_DISPATCH_C(float64_t
|
|
1019
|
+
case CA_INT8: FIBER_SCATTER_DISPATCH_C(int8_t); break;
|
|
1020
|
+
case CA_UINT8: FIBER_SCATTER_DISPATCH_C(uint8_t); break;
|
|
1021
|
+
case CA_INT16: FIBER_SCATTER_DISPATCH_C(int16_t); break;
|
|
1022
|
+
case CA_UINT16: FIBER_SCATTER_DISPATCH_C(uint16_t); break;
|
|
1023
|
+
case CA_INT32: FIBER_SCATTER_DISPATCH_C(int32_t); break;
|
|
1024
|
+
case CA_UINT32: FIBER_SCATTER_DISPATCH_C(uint32_t); break;
|
|
1025
|
+
case CA_INT64: FIBER_SCATTER_DISPATCH_C(int64_t); break;
|
|
1026
|
+
case CA_UINT64: FIBER_SCATTER_DISPATCH_C(uint64_t); break;
|
|
1027
|
+
case CA_FLOAT32: FIBER_SCATTER_DISPATCH_C(float32_t); break;
|
|
1028
|
+
case CA_FLOAT64: FIBER_SCATTER_DISPATCH_C(float64_t); break;
|
|
1033
1029
|
default:
|
|
1034
1030
|
ca_detach(h); ca_detach(codes);
|
|
1035
1031
|
rb_raise(rb_eCADataTypeError,
|
|
@@ -1172,8 +1168,7 @@ rb_ca_fiber_scatter_prod (VALUE self, VALUE rcodes, VALUE raxis,
|
|
|
1172
1168
|
band_size = outer_prod * inner_prod;
|
|
1173
1169
|
total = (ca_size_t)(K * band_size);
|
|
1174
1170
|
|
|
1175
|
-
|
|
1176
|
-
ca_attach(codes);
|
|
1171
|
+
ca_attach_n(2, h, codes); /* window: detached before each raise inside */
|
|
1177
1172
|
hm = ca_mask_ptr(h);
|
|
1178
1173
|
cm = ca_mask_ptr(codes);
|
|
1179
1174
|
outp = (double *) out->ptr;
|
|
@@ -1281,6 +1276,7 @@ rb_ca_fiber_scatter_wsum_wmean (VALUE self, VALUE rcodes, VALUE rweights,
|
|
|
1281
1276
|
ca_size_t ax, inn, outer, cell;
|
|
1282
1277
|
ca_size_t axis_size, inner_prod, outer_prod, band_size, total;
|
|
1283
1278
|
double *wp, *wsp, *wsw, *wmp;
|
|
1279
|
+
volatile VALUE hwsw = 0, hcnt = 0;
|
|
1284
1280
|
boolean8_t *hm, *cm, *wm, *wmm;
|
|
1285
1281
|
int8_t i, j;
|
|
1286
1282
|
int64_t *cnt_scratch = NULL;
|
|
@@ -1341,9 +1337,7 @@ rb_ca_fiber_scatter_wsum_wmean (VALUE self, VALUE rcodes, VALUE rweights,
|
|
|
1341
1337
|
band_size = outer_prod * inner_prod;
|
|
1342
1338
|
total = (ca_size_t)(K * band_size);
|
|
1343
1339
|
|
|
1344
|
-
|
|
1345
|
-
ca_attach(codes);
|
|
1346
|
-
ca_attach(weights);
|
|
1340
|
+
ca_attach_n(3, h, codes, weights); /* window: detached before each raise inside */
|
|
1347
1341
|
hm = ca_mask_ptr(h);
|
|
1348
1342
|
cm = ca_mask_ptr(codes);
|
|
1349
1343
|
wm = ca_mask_ptr(weights);
|
|
@@ -1352,8 +1346,9 @@ rb_ca_fiber_scatter_wsum_wmean (VALUE self, VALUE rcodes, VALUE rweights,
|
|
|
1352
1346
|
wmp = (double *) wmean->ptr; /* wmean output (temp = sum-of-weights, then divide) */
|
|
1353
1347
|
|
|
1354
1348
|
/* Two auxiliary scratches: sum-of-weights (per cell) and present-pair count. */
|
|
1355
|
-
|
|
1356
|
-
|
|
1349
|
+
/* ALLOCV, so a raise below (codes of the wrong type) leaves nothing. */
|
|
1350
|
+
wsw = ALLOCV_N(double, hwsw, total);
|
|
1351
|
+
cnt_scratch = ALLOCV_N(int64_t, hcnt, total);
|
|
1357
1352
|
|
|
1358
1353
|
memset(wsp, 0, (size_t) total * sizeof(double));
|
|
1359
1354
|
memset(wsw, 0, (size_t) total * sizeof(double));
|
|
@@ -1372,7 +1367,7 @@ rb_ca_fiber_scatter_wsum_wmean (VALUE self, VALUE rcodes, VALUE rweights,
|
|
|
1372
1367
|
case CA_FLOAT32: FIBER_SCATTER_WSUM_DISPATCH_C(float32_t); break;
|
|
1373
1368
|
case CA_FLOAT64: FIBER_SCATTER_WSUM_DISPATCH_C(float64_t); break;
|
|
1374
1369
|
default:
|
|
1375
|
-
|
|
1370
|
+
ALLOCV_END(hwsw); ALLOCV_END(hcnt);
|
|
1376
1371
|
ca_detach(h); ca_detach(codes); ca_detach(weights);
|
|
1377
1372
|
rb_raise(rb_eCADataTypeError,
|
|
1378
1373
|
"__fiber_scatter_wsum_wmean__: numeric value required (got %d)",
|
|
@@ -1391,8 +1386,8 @@ rb_ca_fiber_scatter_wsum_wmean (VALUE self, VALUE rcodes, VALUE rweights,
|
|
|
1391
1386
|
}
|
|
1392
1387
|
}
|
|
1393
1388
|
|
|
1394
|
-
|
|
1395
|
-
|
|
1389
|
+
ALLOCV_END(hwsw);
|
|
1390
|
+
ALLOCV_END(hcnt);
|
|
1396
1391
|
ca_detach(h);
|
|
1397
1392
|
ca_detach(codes);
|
|
1398
1393
|
ca_detach(weights);
|
data/ext/ca_compare.c
CHANGED
|
@@ -14,6 +14,7 @@
|
|
|
14
14
|
#include "ruby.h"
|
|
15
15
|
#include "carray.h"
|
|
16
16
|
#include "ca_compare.h"
|
|
17
|
+
#include "carray_internal.h" /* ca_obj_cmp, CA_OBJ_ISNAN */
|
|
17
18
|
#include <math.h>
|
|
18
19
|
|
|
19
20
|
#define DEFINE_ELEM_CMP(type) \
|
|
@@ -53,13 +54,18 @@ DEFINE_ELEM_CMP(uint64_t)
|
|
|
53
54
|
DEFINE_ELEM_CMP_FLOAT(float32_t)
|
|
54
55
|
DEFINE_ELEM_CMP_FLOAT(float64_t)
|
|
55
56
|
|
|
56
|
-
/* CA_OBJECT: compare
|
|
57
|
+
/* CA_OBJECT: compare by `<=>`, as sort orders object cells: a Float NaN
|
|
58
|
+
after every other value, and a pair `<=>` cannot order raises
|
|
59
|
+
"comparison of A with B failed". */
|
|
57
60
|
static int
|
|
58
61
|
elem_cmp_VALUE (const void *va, const void *vb)
|
|
59
62
|
{
|
|
60
63
|
const VALUE *a = (const VALUE *) va;
|
|
61
64
|
const VALUE *b = (const VALUE *) vb;
|
|
62
|
-
|
|
65
|
+
int nan_a = CA_OBJ_ISNAN(*a);
|
|
66
|
+
int nan_b = CA_OBJ_ISNAN(*b);
|
|
67
|
+
if ( nan_a || nan_b ) return nan_a - nan_b;
|
|
68
|
+
return ca_obj_cmp(*a, *b);
|
|
63
69
|
}
|
|
64
70
|
|
|
65
71
|
/* FIXLEN / retired / otherwise unorderable data_types: callers must handle
|
data/ext/ca_for_buffer.h
CHANGED
|
@@ -1,43 +1,33 @@
|
|
|
1
1
|
/* ---------------------------------------------------------------------------
|
|
2
2
|
*
|
|
3
|
-
* ca_for_buffer.h --
|
|
4
|
-
*
|
|
3
|
+
* ca_for_buffer.h -- whole contig buffer handed to author / third-party
|
|
4
|
+
* library
|
|
5
5
|
*
|
|
6
|
-
*
|
|
7
|
-
*
|
|
8
|
-
*
|
|
9
|
-
*
|
|
10
|
-
*
|
|
6
|
+
* C-side counterpart of Ruby's `ca.attach! { |a| ... }` block: the
|
|
7
|
+
* attach / sync / detach lifecycle is scoped to a body function, which
|
|
8
|
+
* receives a contig buffer ptr and element count and may do whatever
|
|
9
|
+
* (typically pass the ptr to a third-party library: FFTW, akima init,
|
|
10
|
+
* fitpack surf1, ...).
|
|
11
11
|
*
|
|
12
|
-
*
|
|
13
|
-
*
|
|
12
|
+
* The buffer aliases ca->ptr when the array is a contig entity and is a
|
|
13
|
+
* materialised scratch otherwise. A writable call syncs the buffer back
|
|
14
|
+
* to the array's storage when the body returns.
|
|
14
15
|
*
|
|
15
|
-
*
|
|
16
|
-
*
|
|
17
|
-
*
|
|
18
|
-
*
|
|
19
|
-
*
|
|
20
|
-
* CA_WITH_BUFFER_WRITABLE(ca, T, ptr, n)
|
|
21
|
-
* Writable; same ptr/n semantics, plus ca_sync on block exit so
|
|
22
|
-
* author writes propagate back to the view's storage.
|
|
16
|
+
* The body may raise. The view is closed however the body is left: a
|
|
17
|
+
* writable view is synced first, so what the body wrote before raising
|
|
18
|
+
* reaches the view's storage, and the view is detached even if that sync
|
|
19
|
+
* raises. The body's exception is the one that propagates.
|
|
23
20
|
*
|
|
24
21
|
* Author pattern:
|
|
25
22
|
*
|
|
26
|
-
*
|
|
27
|
-
* ca_size_t n
|
|
28
|
-
*
|
|
23
|
+
* static void
|
|
24
|
+
* fft_body (void *user_data, void *ptr, ca_size_t n)
|
|
25
|
+
* {
|
|
26
|
+
* fftw_plan plan = (fftw_plan) user_data;
|
|
29
27
|
* fftw_execute_dft(plan, ptr, ptr);
|
|
30
28
|
* }
|
|
31
29
|
*
|
|
32
|
-
*
|
|
33
|
-
* - `break;` from body exits cleanly (= outer for's advance clause
|
|
34
|
-
* runs ca_sync / ca_detach).
|
|
35
|
-
* - `return;` from body LEAKS the attach; restructure to break.
|
|
36
|
-
* - Macros are NOT statement-equivalent (= nested for); no trailing
|
|
37
|
-
* `else`.
|
|
38
|
-
* - For Ruby-exception-safe lifecycle (e.g. when calling Ruby code or
|
|
39
|
-
* anything that may raise from inside the body), use the function
|
|
40
|
-
* form `rb_ca_call_with_buffer` instead (= rb_ensure-protected).
|
|
30
|
+
* rb_ca_call_with_buffer(rca, 1, fft_body, plan);
|
|
41
31
|
*
|
|
42
32
|
* --------------------------------------------------------------------------- */
|
|
43
33
|
|
|
@@ -47,46 +37,9 @@
|
|
|
47
37
|
#include "carray.h"
|
|
48
38
|
#include "ca_sweep_engine.h"
|
|
49
39
|
|
|
50
|
-
/*
|
|
51
|
-
|
|
52
|
-
/* Read-only: ca_attach (alias-when-possible) + author body + ca_detach. */
|
|
53
|
-
#define CA_WITH_BUFFER(_ca, T, _ptr, _n) \
|
|
54
|
-
for ( CArray *__cwv_ca = (CArray *)(_ca); \
|
|
55
|
-
__cwv_ca; \
|
|
56
|
-
ca_detach(__cwv_ca), __cwv_ca = NULL ) \
|
|
57
|
-
for ( int __cwv_once = (ca_attach(__cwv_ca), \
|
|
58
|
-
(_ptr) = (T *)__cwv_ca->ptr, \
|
|
59
|
-
(_n) = __cwv_ca->elements, \
|
|
60
|
-
1); \
|
|
61
|
-
__cwv_once; \
|
|
62
|
-
__cwv_once = 0 )
|
|
63
|
-
|
|
64
|
-
/* Writable: same as above + ca_sync on block exit. */
|
|
65
|
-
#define CA_WITH_BUFFER_WRITABLE(_ca, T, _ptr, _n) \
|
|
66
|
-
for ( CArray *__cwvw_ca = (CArray *)(_ca); \
|
|
67
|
-
__cwvw_ca; \
|
|
68
|
-
ca_sync(__cwvw_ca), ca_detach(__cwvw_ca), __cwvw_ca = NULL ) \
|
|
69
|
-
for ( int __cwvw_once = (ca_attach(__cwvw_ca), \
|
|
70
|
-
(_ptr) = (T *)__cwvw_ca->ptr, \
|
|
71
|
-
(_n) = __cwvw_ca->elements, \
|
|
72
|
-
1); \
|
|
73
|
-
__cwvw_once; \
|
|
74
|
-
__cwvw_once = 0 )
|
|
75
|
-
|
|
76
|
-
/* ---------- function form (rb_ensure-protected, AC8) ----------
|
|
77
|
-
*
|
|
78
|
-
* Use this when the body may raise a Ruby exception (= calling rb_funcall,
|
|
79
|
-
* type-checking with rb_check_type, indirect Ruby code, etc.). The
|
|
80
|
-
* engine ca_attach's the view, runs body via rb_ensure, then guarantees
|
|
81
|
-
* ca_sync (if writable) and ca_detach run before the exception
|
|
82
|
-
* propagates.
|
|
83
|
-
*
|
|
84
|
-
* body_fn(user_data, ptr, n_elements) -> may raise
|
|
40
|
+
/* body_fn(user_data, ptr, n_elements) -> may raise
|
|
85
41
|
*
|
|
86
|
-
* Returns
|
|
87
|
-
* function itself returns Qnil since rb_ensure body must be VALUE).
|
|
88
|
-
* For richer return semantics, box your result in user_data.
|
|
89
|
-
*/
|
|
42
|
+
* Returns nothing; box a result in user_data. */
|
|
90
43
|
typedef void (*ca_with_buffer_body_fn) (void *user_data, void *ptr,
|
|
91
44
|
ca_size_t n_elements);
|
|
92
45
|
|
data/ext/ca_group_iter.c
CHANGED
|
@@ -40,10 +40,14 @@ static ID id_axis_group_reduce, id_reshape, id_transpose, id_labels;
|
|
|
40
40
|
static VALUE
|
|
41
41
|
rb_ca_register_axis_group_classes (VALUE klass, VALUE cat, VALUE ag)
|
|
42
42
|
{
|
|
43
|
+
static int registered = 0;
|
|
43
44
|
rb_cCategorical = cat;
|
|
44
45
|
rb_cAxisGroup = ag;
|
|
45
|
-
|
|
46
|
-
|
|
46
|
+
if ( ! registered ) { /* one GC root per address, not per call */
|
|
47
|
+
rb_gc_register_address(&rb_cCategorical);
|
|
48
|
+
rb_gc_register_address(&rb_cAxisGroup);
|
|
49
|
+
registered = 1;
|
|
50
|
+
}
|
|
47
51
|
return Qnil;
|
|
48
52
|
}
|
|
49
53
|
|
|
@@ -172,7 +176,7 @@ group_iter_shape_output (VALUE result, VALUE group_dims, VALUE perm, VALUE squee
|
|
|
172
176
|
nband = cr->ndim - 1;
|
|
173
177
|
nd = ng + nband;
|
|
174
178
|
if ( nd > CA_RANK_MAX ) {
|
|
175
|
-
rb_raise(
|
|
179
|
+
rb_raise(rb_eArgError, "axis_group: output ndim %ld too large", nd);
|
|
176
180
|
}
|
|
177
181
|
for ( i = 0; i < ng; i++ ) {
|
|
178
182
|
dims[i] = RARRAY_AREF(group_dims, i);
|
|
@@ -233,17 +237,19 @@ group_iter_reduce (int argc, VALUE *argv, VALUE self)
|
|
|
233
237
|
parsed = rb_funcall(rb_cAxisGroup, id_parse_axis,
|
|
234
238
|
1, (vaxis == Qundef) ? Qnil : vaxis);
|
|
235
239
|
if ( RTEST(RARRAY_AREF(parsed, 0)) == 0 ) {
|
|
236
|
-
/* no :group -> plain reduction on the value (grouping not engaged)
|
|
237
|
-
|
|
240
|
+
/* no :group -> plain reduction on the value (grouping not engaged),
|
|
241
|
+
with every keyword the caller gave */
|
|
242
|
+
if ( NIL_P(kw) ) {
|
|
238
243
|
return rb_funcall(value, op_id, 0);
|
|
239
244
|
}
|
|
240
|
-
|
|
241
|
-
VALUE h = rb_hash_new();
|
|
242
|
-
rb_hash_aset(h, ID2SYM(rb_intern("axis")), vaxis);
|
|
243
|
-
return rb_funcallv_kw(value, op_id, 1, &h, RB_PASS_KEYWORDS);
|
|
244
|
-
}
|
|
245
|
+
return rb_funcallv_kw(value, op_id, 1, &kw, RB_PASS_KEYWORDS);
|
|
245
246
|
}
|
|
246
247
|
|
|
248
|
+
/* min_count: / fill_value: are applied to the grouped result; any other
|
|
249
|
+
keyword is refused before the work is done. */
|
|
250
|
+
rb_funcall(rb_cAxisGroup, rb_intern("__check_reduce_keywords__"), 2,
|
|
251
|
+
op_sym, NIL_P(kw) ? rb_hash_new() : kw);
|
|
252
|
+
|
|
247
253
|
/* group reduction */
|
|
248
254
|
fused = RARRAY_AREF(parsed, 1);
|
|
249
255
|
plan = rb_funcall(spec, id_reduce_plan, 1, fused);
|
|
@@ -253,9 +259,19 @@ group_iter_reduce (int argc, VALUE *argv, VALUE self)
|
|
|
253
259
|
perm = RARRAY_AREF(plan, 3);
|
|
254
260
|
squeeze = RARRAY_AREF(plan, 4);
|
|
255
261
|
|
|
262
|
+
/* a count reads only the mask, so a payload the kernel cannot read
|
|
263
|
+
(object, fixlen, ...) is counted through a boolean stand-in */
|
|
264
|
+
if ( op_id == rb_intern("count") || op_id == rb_intern("count_not_masked") ) {
|
|
265
|
+
value = rb_funcall(rb_cAxisGroup, rb_intern("__count_stand_in__"), 1, value);
|
|
266
|
+
}
|
|
256
267
|
result = rb_funcall(value, id_axis_group_reduce, 3,
|
|
257
268
|
group_axes, bundles, op_sym);
|
|
258
|
-
|
|
269
|
+
result = group_iter_shape_output(result, group_dims, perm, squeeze);
|
|
270
|
+
if ( ! NIL_P(kw) ) {
|
|
271
|
+
result = rb_funcall(rb_cAxisGroup, rb_intern("__finish_reduction__"), 4,
|
|
272
|
+
self, op_sym, kw, result);
|
|
273
|
+
}
|
|
274
|
+
return result;
|
|
259
275
|
}
|
|
260
276
|
|
|
261
277
|
void
|