carray 2.0.0 → 3.0.0
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/.yardopts +5 -25
- data/CHANGELOG.md +16 -0
- data/LICENSE +1 -1
- data/NEWS.md +3 -0
- data/README.md +128 -44
- data/carray.gemspec +22 -24
- data/ext/ca_array_pool.c +91 -0
- data/ext/ca_axis_descriptor.h +186 -0
- data/ext/ca_axis_dispatch.c +924 -0
- data/ext/ca_axis_group.c +1208 -0
- data/ext/ca_bincmp_dispatch.c +76 -0
- data/ext/ca_bincmp_dispatch.h +85 -0
- data/ext/ca_binop_dispatch.c +125 -0
- data/ext/ca_binop_dispatch.h +159 -0
- data/ext/ca_categorical_iterator.c +1375 -0
- data/ext/ca_compare.c +94 -0
- data/ext/ca_compare.h +26 -0
- data/ext/ca_composite_dispatch.c +414 -0
- data/ext/ca_composite_dispatch.h +116 -0
- data/ext/ca_for_buffer.h +96 -0
- data/ext/ca_for_each_element.h +241 -0
- data/ext/ca_group_iter.c +304 -0
- data/ext/ca_iter_substrate.h +325 -0
- data/ext/ca_kernel_iterator.c +4321 -0
- data/ext/ca_kernel_iterator.h +2603 -0
- data/ext/ca_moncmp_dispatch.c +37 -0
- data/ext/ca_moncmp_dispatch.h +62 -0
- data/ext/ca_monop_dispatch.c +200 -0
- data/ext/ca_monop_dispatch.h +235 -0
- data/ext/ca_obj_array.c +355 -359
- data/ext/ca_obj_bincmp.c +809 -0
- data/ext/ca_obj_binop.c +892 -0
- data/ext/ca_obj_bitarray.c +369 -164
- data/ext/ca_obj_bitfield.c +294 -234
- data/ext/ca_obj_block.c +189 -711
- data/ext/ca_obj_byte_swap.c +766 -0
- data/ext/ca_obj_const_string.c +965 -0
- data/ext/ca_obj_face.c +670 -0
- data/ext/ca_obj_face.h +247 -0
- data/ext/ca_obj_fake.c +228 -100
- data/ext/ca_obj_farray.c +54 -441
- data/ext/ca_obj_field.c +82 -529
- data/ext/ca_obj_fixlen_string.c +306 -0
- data/ext/ca_obj_grid.c +858 -440
- data/ext/ca_obj_meld.c +1034 -0
- data/ext/ca_obj_moncmp.c +569 -0
- data/ext/ca_obj_monop.c +1111 -0
- data/ext/ca_obj_object.c +774 -298
- data/ext/ca_obj_record.c +468 -0
- data/ext/ca_obj_reduce.c +97 -82
- data/ext/ca_obj_refer.c +569 -459
- data/ext/ca_obj_remap.c +475 -0
- data/ext/ca_obj_repeat.c +92 -477
- data/ext/ca_obj_roll.c +616 -0
- data/ext/ca_obj_select.c +344 -296
- data/ext/ca_obj_select_axis.c +1296 -0
- data/ext/ca_obj_shift.c +230 -792
- data/ext/ca_obj_source.c +78 -0
- data/ext/ca_obj_stack.c +1173 -0
- data/ext/ca_obj_stride.c +2501 -0
- data/ext/ca_obj_string.c +268 -0
- data/ext/ca_obj_tile.c +614 -0
- data/ext/ca_obj_time.c +546 -0
- data/ext/ca_obj_timedelta.c +435 -0
- data/ext/ca_obj_transpose.c +62 -516
- data/ext/ca_obj_triop.c +746 -0
- data/ext/ca_obj_unbound_repeat.c +208 -241
- data/ext/ca_obj_window.c +1131 -563
- data/ext/ca_op_byte_swap.c +175 -0
- data/ext/ca_op_ipower.c +319 -0
- data/ext/ca_op_powi.h +88 -0
- data/ext/ca_sort_kernels.h +132 -0
- data/ext/ca_sweep_engine.c +430 -0
- data/ext/ca_sweep_engine.h +157 -0
- data/ext/ca_transform_common.c +228 -0
- data/ext/ca_triop_dispatch.c +55 -0
- data/ext/ca_triop_dispatch.h +62 -0
- data/ext/carray.h +795 -402
- data/ext/carray_access.c +831 -711
- data/ext/carray_attribute.c +98 -330
- data/ext/carray_bincount.c +255 -0
- data/ext/carray_broadcast.c +283 -0
- data/ext/carray_call_cfunc.c +1360 -828
- data/ext/carray_call_cfunc.h +160 -0
- data/ext/carray_cast.c +1212 -301
- data/ext/carray_cast_func.rb +81 -40
- data/ext/carray_class.c +53 -63
- data/ext/carray_config.h +28 -0
- data/ext/carray_conversion.c +350 -346
- data/ext/carray_copy.c +156 -268
- data/ext/carray_core.c +1342 -199
- data/ext/carray_count.c +312 -0
- data/ext/carray_data_type.c +43 -19
- data/ext/carray_element.c +585 -213
- data/ext/carray_factorize.c +2542 -0
- data/ext/carray_generate.c +230 -559
- data/ext/carray_histogram.c +490 -0
- data/ext/carray_hold.c +228 -0
- data/ext/carray_index_classifier.c +1035 -0
- data/ext/carray_index_classifier.h +27 -0
- data/ext/carray_internal.h +120 -0
- data/ext/carray_kernels_bincmp.c +4445 -0
- data/ext/carray_kernels_binop.c +10979 -0
- data/ext/carray_kernels_init.c +36 -0
- data/ext/carray_kernels_map.c +3466 -0
- data/ext/carray_kernels_moncmp.c +2096 -0
- data/ext/carray_kernels_monop.c +18312 -0
- data/ext/carray_kernels_reduce_aggregate.c +25836 -0
- data/ext/carray_kernels_reduce_boolean.c +329 -0
- data/ext/carray_kernels_reduce_cumulative.c +14592 -0
- data/ext/carray_kernels_reduce_extreme.c +16947 -0
- data/ext/carray_kernels_reduce_variance.c +3909 -0
- data/ext/carray_kernels_scan.c +3692 -0
- data/ext/carray_kernels_search.c +32137 -0
- data/ext/carray_kernels_sort.c +10625 -0
- data/ext/carray_kernels_triop.c +1391 -0
- data/ext/carray_lazy.c +567 -0
- data/ext/carray_loop.c +88 -200
- data/ext/carray_mask.c +848 -154
- data/ext/carray_math_kernel.h +120 -0
- data/ext/carray_mathfunc.c +10 -241
- data/ext/carray_median_percentile.c +1257 -0
- data/ext/carray_memory_view.c +1625 -0
- data/ext/carray_operator.c +1526 -318
- data/ext/carray_order.c +664 -1394
- data/ext/carray_partition.c +416 -0
- data/ext/carray_random.c +518 -0
- data/ext/carray_scatter.c +357 -0
- data/ext/carray_slab.c +1219 -0
- data/ext/carray_slab.h +84 -0
- data/ext/carray_sort.c +829 -0
- data/ext/carray_sort_kernel.c +620 -0
- data/ext/carray_struct.c +695 -0
- data/ext/carray_test.c +343 -229
- data/ext/carray_undef.c +34 -17
- data/ext/carray_utils.c +175 -74
- data/ext/extconf.rb +216 -55
- data/ext/mk_call_cfunc.rb +480 -0
- data/ext/mkkernel.rb +8842 -0
- data/ext/ruby_carray.c +202 -101
- data/ext/version.h +4 -14
- data/ext/version.rb +5 -13
- data/lib/carray/arrow_tensor.rb +401 -0
- data/lib/carray/attribute.rb +166 -0
- data/lib/carray/autoload_carray.rb +220 -0
- data/lib/carray/autoload_method_extension.rb +44 -0
- data/lib/carray/axis_group.rb +711 -0
- data/lib/carray/basics.rb +481 -0
- data/lib/carray/bincount_nd.rb +358 -0
- data/lib/carray/block_iterator.rb +604 -0
- data/lib/carray/boolean_reduce.rb +109 -0
- data/lib/carray/categorical.rb +561 -0
- data/lib/carray/categorical_iterator.rb +1062 -0
- data/lib/carray/complex.rb +150 -0
- data/lib/carray/conditional.rb +216 -0
- data/lib/carray/const_string.rb +228 -0
- data/lib/carray/construct.rb +139 -328
- data/lib/carray/core_extensions.rb +240 -0
- data/lib/carray/data_type_extension.rb +233 -0
- data/lib/carray/fixlen_string.rb +95 -0
- data/lib/carray/frame/concat.rb +132 -0
- data/lib/carray/frame/convert.rb +95 -0
- data/lib/carray/frame/csv_parser.rb +211 -0
- data/lib/carray/frame/frame.rb +649 -0
- data/lib/carray/frame/group.rb +186 -0
- data/lib/carray/frame/io.rb +164 -0
- data/lib/carray/frame/join.rb +248 -0
- data/lib/carray/frame/records.rb +99 -0
- data/lib/carray/frame/sort.rb +113 -0
- data/lib/carray/frame/verbs.rb +299 -0
- data/lib/carray/frame.rb +16 -0
- data/lib/carray/histogram.rb +512 -0
- data/lib/carray/inspect.rb +37 -20
- data/lib/carray/iterator.rb +57 -349
- data/lib/carray/lazy.rb +889 -0
- data/lib/carray/mask_gap_fill.rb +200 -0
- data/lib/carray/math.rb +78 -342
- data/lib/carray/meld_reduce.rb +289 -0
- data/lib/carray/methods/align_addr.rb +116 -0
- data/lib/carray/methods/bin.rb +128 -0
- data/lib/carray/methods/bincount.rb +87 -0
- data/lib/carray/methods/bit_string.rb +92 -0
- data/lib/carray/methods/broadcast.rb +63 -0
- data/lib/carray/methods/choose.rb +39 -0
- data/lib/carray/methods/composition.rb +280 -0
- data/lib/carray/methods/gather_nd.rb +206 -0
- data/lib/carray/methods/index.rb +39 -0
- data/lib/carray/methods/insert_block.rb +99 -0
- data/lib/carray/methods/is_in.rb +141 -0
- data/lib/carray/methods/join.rb +90 -0
- data/lib/carray/methods/locate_addr.rb +47 -0
- data/lib/carray/methods/mask_duplicates.rb +41 -0
- data/lib/carray/methods/meshgrid.rb +91 -0
- data/lib/carray/methods/mode.rb +126 -0
- data/lib/carray/methods/nunique.rb +46 -0
- data/lib/carray/methods/resize.rb +56 -0
- data/lib/carray/methods/snap.rb +156 -0
- data/lib/carray/methods/string_format.rb +57 -0
- data/lib/carray/methods/unique.rb +47 -0
- data/lib/carray/methods/value_counts.rb +71 -0
- data/lib/carray/mkmf.rb +124 -101
- data/lib/carray/runtime.rb +108 -0
- data/lib/carray/serialize.rb +478 -167
- data/lib/carray/slab_iterator.rb +292 -0
- data/lib/carray/stack.rb +291 -0
- data/lib/carray/string.rb +56 -180
- data/lib/carray/string_operation_extension.rb +289 -0
- data/lib/carray/struct.rb +335 -323
- data/lib/carray/struct_builder.rb +697 -0
- data/lib/carray/table.rb +41 -2
- data/lib/carray/time.rb +2255 -38
- data/lib/carray/window_iterator.rb +655 -0
- data/lib/carray.rb +55 -57
- metadata +163 -130
- data/Rakefile +0 -51
- data/TODO.md +0 -18
- data/ext/ca_iter_block.c +0 -257
- data/ext/ca_iter_dimension.c +0 -299
- data/ext/ca_iter_window.c +0 -214
- data/ext/ca_obj_mapping.c +0 -644
- data/ext/carray_iterator.c +0 -641
- data/ext/carray_math.rb +0 -850
- data/ext/carray_numeric.c +0 -259
- data/ext/carray_sort_addr.c +0 -254
- data/ext/carray_stat.c +0 -2100
- data/ext/carray_stat_proc.rb +0 -1999
- data/ext/mkmath.rb +0 -741
- data/ext/ruby_ccomplex.c +0 -509
- data/ext/ruby_float_func.c +0 -86
- data/lib/carray/array.rb +0 -8
- data/lib/carray/autoload/autoload_base.rb +0 -19
- data/lib/carray/autoload/autoload_gem_cairo.rb +0 -9
- data/lib/carray/autoload/autoload_gem_ffi.rb +0 -9
- data/lib/carray/autoload/autoload_gem_gnuplot.rb +0 -2
- data/lib/carray/autoload/autoload_gem_io_csv.rb +0 -14
- data/lib/carray/autoload/autoload_gem_io_pg.rb +0 -6
- data/lib/carray/autoload/autoload_gem_io_sqlite3.rb +0 -12
- data/lib/carray/autoload/autoload_gem_narray.rb +0 -10
- data/lib/carray/autoload/autoload_gem_numo_narray.rb +0 -15
- data/lib/carray/autoload/autoload_gem_opencv.rb +0 -16
- data/lib/carray/autoload/autoload_gem_random.rb +0 -8
- data/lib/carray/autoload/autoload_gem_rmagick.rb +0 -23
- data/lib/carray/autoload/autoload_gem_zimg.rb +0 -3
- data/lib/carray/autoload/autoload_io_imagemagick.rb +0 -6
- data/lib/carray/autoload/autoload_math_histogram.rb +0 -5
- data/lib/carray/autoload/autoload_math_recurrence.rb +0 -6
- data/lib/carray/autoload/autoload_object_iterator.rb +0 -1
- data/lib/carray/autoload/autoload_object_link.rb +0 -1
- data/lib/carray/autoload/autoload_object_pack.rb +0 -2
- data/lib/carray/autoload.rb +0 -141
- data/lib/carray/basic.rb +0 -191
- data/lib/carray/broadcast.rb +0 -101
- data/lib/carray/compose.rb +0 -315
- data/lib/carray/convert.rb +0 -115
- data/lib/carray/info.rb +0 -110
- data/lib/carray/io/imagemagick.rb +0 -235
- data/lib/carray/mask.rb +0 -102
- data/lib/carray/math/histogram.rb +0 -177
- data/lib/carray/math/recurrence.rb +0 -93
- data/lib/carray/object/ca_obj_iterator.rb +0 -50
- data/lib/carray/object/ca_obj_link.rb +0 -50
- data/lib/carray/object/ca_obj_pack.rb +0 -99
- data/lib/carray/obsolete.rb +0 -256
- data/lib/carray/ordering.rb +0 -181
- data/lib/carray/testing.rb +0 -51
- data/lib/carray/transform.rb +0 -109
- data/misc/Methods.ja.md +0 -182
- data/misc/NOTE +0 -51
- data/spec/Classes/CABitfield_spec.rb +0 -58
- data/spec/Classes/CABlockIterator_spec.rb +0 -114
- data/spec/Classes/CABlock_spec.rb +0 -205
- data/spec/Classes/CAField_spec.rb +0 -39
- data/spec/Classes/CAGrid_spec.rb +0 -75
- data/spec/Classes/CAMap_spec.rb +0 -0
- data/spec/Classes/CAMapping_spec.rb +0 -105
- data/spec/Classes/CAObject_attribute_spec.rb +0 -33
- data/spec/Classes/CAObject_spec.rb +0 -33
- data/spec/Classes/CARefer_spec.rb +0 -93
- data/spec/Classes/CARepeat_spec.rb +0 -65
- data/spec/Classes/CASelect_spec.rb +0 -22
- data/spec/Classes/CAShift_spec.rb +0 -16
- data/spec/Classes/CAStruct_spec.rb +0 -71
- data/spec/Classes/CATranspose_spec.rb +0 -60
- data/spec/Classes/CAUnboudRepeat_spec.rb +0 -102
- data/spec/Classes/CAWindow_spec.rb +0 -54
- data/spec/Classes/CAWrap_spec.rb +0 -8
- data/spec/Classes/CArray_spec.rb +0 -184
- data/spec/Classes/CScalar_spec.rb +0 -55
- data/spec/Classes/ex1.rb +0 -46
- data/spec/Features/feature_130_spec.rb +0 -19
- data/spec/Features/feature_attributes_spec.rb +0 -280
- data/spec/Features/feature_boolean_spec.rb +0 -98
- data/spec/Features/feature_broadcast.rb +0 -116
- data/spec/Features/feature_cast_function.rb +0 -19
- data/spec/Features/feature_cast_spec.rb +0 -33
- data/spec/Features/feature_class_spec.rb +0 -84
- data/spec/Features/feature_complex_spec.rb +0 -42
- data/spec/Features/feature_composite_spec.rb +0 -124
- data/spec/Features/feature_convert_spec.rb +0 -46
- data/spec/Features/feature_copy_spec.rb +0 -123
- data/spec/Features/feature_creation_spec.rb +0 -84
- data/spec/Features/feature_element_spec.rb +0 -144
- data/spec/Features/feature_extream_spec.rb +0 -54
- data/spec/Features/feature_generate_spec.rb +0 -74
- data/spec/Features/feature_index_spec.rb +0 -69
- data/spec/Features/feature_mask_spec.rb +0 -580
- data/spec/Features/feature_math_spec.rb +0 -97
- data/spec/Features/feature_order_spec.rb +0 -146
- data/spec/Features/feature_ref_store_spec.rb +0 -209
- data/spec/Features/feature_serialization_spec.rb +0 -125
- data/spec/Features/feature_stat_spec.rb +0 -397
- data/spec/Features/feature_virtual_spec.rb +0 -48
- data/spec/Features/method_eq_spec.rb +0 -81
- data/spec/Features/method_is_nan_spec.rb +0 -12
- data/spec/Features/method_map_spec.rb +0 -54
- data/spec/Features/method_max_with.rb +0 -20
- data/spec/Features/method_min_with.rb +0 -19
- data/spec/Features/method_ne_spec.rb +0 -18
- data/spec/Features/method_project_spec.rb +0 -188
- data/spec/Features/method_ref_spec.rb +0 -27
- data/spec/Features/method_round_spec.rb +0 -11
- data/spec/Features/method_s_linspace_spec.rb +0 -48
- data/spec/Features/method_s_span_spec.rb +0 -14
- data/spec/Features/method_seq_spec.rb +0 -47
- data/spec/Features/method_sort_with.rb +0 -43
- data/spec/Features/method_sorted_with.rb +0 -29
- data/spec/Features/method_span_spec.rb +0 -42
- data/spec/Features/method_wrap_readonly_spec.rb +0 -43
- data/spec/UnitTest/test_CAVirtual.rb +0 -214
- data/spec/spec_all.rb +0 -10
- data/utils/ca_ase.rb +0 -21
- data/utils/ca_methods.rb +0 -15
- data/utils/cast_checker.rb +0 -30
- data/utils/convert_test.rb +0 -73
- data/utils/extract_yard.rb +0 -22
- data/utils/guess_shape.rb +0 -76
- data/utils/monkey_patch_methods.rb +0 -62
- data/utils/remove_resource_fork.sh +0 -5
data/ext/carray_mask.c
CHANGED
|
@@ -1,16 +1,253 @@
|
|
|
1
1
|
/* ---------------------------------------------------------------------------
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
Copyright (C) 2005-2020 Hiroki Motoyoshi
|
|
3
|
+
Mask machinery shared by every CArray subtype: has_mask / any-masked /
|
|
4
|
+
all-masked scans, mask creation and propagation across the view chain,
|
|
5
|
+
the mask SET family (mask_eq / mask_invalid / mask_where / strip_mask),
|
|
6
|
+
count_masked, and the word-bulk boolean bit-op fast paths.
|
|
8
7
|
|
|
9
8
|
---------------------------------------------------------------------------- */
|
|
10
9
|
|
|
11
10
|
#include <stdarg.h>
|
|
11
|
+
#include <stdint.h>
|
|
12
12
|
#include "ruby.h"
|
|
13
13
|
#include "carray.h"
|
|
14
|
+
#include "carray_internal.h" /* ca_lazy_arena_* */
|
|
15
|
+
#include "ca_obj_face.h" /* CA_FACE_LIFT_IF_FACE for the mask-lift path */
|
|
16
|
+
|
|
17
|
+
/* ============================================================================
|
|
18
|
+
* Word-bulk mask substrate helpers.
|
|
19
|
+
*
|
|
20
|
+
* CAREFUL: every helper here relies on the invariant that a mask / bool
|
|
21
|
+
* byte is in {0, 1} throughout all production code paths (OBJ2BOOL strict,
|
|
22
|
+
* integer->bool cast raises on out-of-range, comparison output is
|
|
23
|
+
* C-semantic 0/1, the mask combiner forces *ma=1, and an external mask via
|
|
24
|
+
* ca_wrap_readonly normalises). Breaking it corrupts the word-level ops.
|
|
25
|
+
*
|
|
26
|
+
* Word loop: uint64_t (8 byte/op) + tail byte fallback (n % 8).
|
|
27
|
+
* NOT uses an LSB XOR pattern (w ^ 0x0101...01ULL) to preserve the
|
|
28
|
+
* invariant (byte ~ would produce 0xFE, violating {0,1}).
|
|
29
|
+
*
|
|
30
|
+
* Helpers are static inline (file-local), inlinable into callers within
|
|
31
|
+
* this translation unit (overlay_n, is_any_masked, is_all_masked, etc.).
|
|
32
|
+
* extern + header extraction can wait until a cross-file need arises.
|
|
33
|
+
* Placed at the top of the TU so the inline body is visible at every call
|
|
34
|
+
* site.
|
|
35
|
+
* ============================================================================ */
|
|
36
|
+
|
|
37
|
+
#define CA_MASK_WORD_BYTES 8
|
|
38
|
+
#define CA_MASK_WORD_LSB 0x0101010101010101ULL
|
|
39
|
+
|
|
40
|
+
/* ma[i] |= ms[i] for i in [0, n) -- used by ca_copy_mask_overlay_n */
|
|
41
|
+
static inline void
|
|
42
|
+
ca_mask_word_or (boolean8_t *ma, const boolean8_t *ms, ca_size_t n)
|
|
43
|
+
{
|
|
44
|
+
ca_size_t nw = n / CA_MASK_WORD_BYTES;
|
|
45
|
+
ca_size_t i;
|
|
46
|
+
uint64_t *wa = (uint64_t *) ma;
|
|
47
|
+
const uint64_t *ws = (const uint64_t *) ms;
|
|
48
|
+
for (i = 0; i < nw; i++) {
|
|
49
|
+
wa[i] |= ws[i];
|
|
50
|
+
}
|
|
51
|
+
for (i = nw * CA_MASK_WORD_BYTES; i < n; i++) {
|
|
52
|
+
ma[i] |= ms[i];
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
/* md[i] = ma[i] & mb[i] for i in [0, n) */
|
|
57
|
+
static inline void
|
|
58
|
+
ca_mask_word_and (boolean8_t *md,
|
|
59
|
+
const boolean8_t *ma,
|
|
60
|
+
const boolean8_t *mb,
|
|
61
|
+
ca_size_t n)
|
|
62
|
+
{
|
|
63
|
+
ca_size_t nw = n / CA_MASK_WORD_BYTES;
|
|
64
|
+
ca_size_t i;
|
|
65
|
+
uint64_t *wd = (uint64_t *) md;
|
|
66
|
+
const uint64_t *wa = (const uint64_t *) ma;
|
|
67
|
+
const uint64_t *wb = (const uint64_t *) mb;
|
|
68
|
+
for (i = 0; i < nw; i++) {
|
|
69
|
+
wd[i] = wa[i] & wb[i];
|
|
70
|
+
}
|
|
71
|
+
for (i = nw * CA_MASK_WORD_BYTES; i < n; i++) {
|
|
72
|
+
md[i] = ma[i] & mb[i];
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
/* md[i] = ma[i] ^ mb[i] for i in [0, n) */
|
|
77
|
+
static inline void
|
|
78
|
+
ca_mask_word_xor (boolean8_t *md,
|
|
79
|
+
const boolean8_t *ma,
|
|
80
|
+
const boolean8_t *mb,
|
|
81
|
+
ca_size_t n)
|
|
82
|
+
{
|
|
83
|
+
ca_size_t nw = n / CA_MASK_WORD_BYTES;
|
|
84
|
+
ca_size_t i;
|
|
85
|
+
uint64_t *wd = (uint64_t *) md;
|
|
86
|
+
const uint64_t *wa = (const uint64_t *) ma;
|
|
87
|
+
const uint64_t *wb = (const uint64_t *) mb;
|
|
88
|
+
for (i = 0; i < nw; i++) {
|
|
89
|
+
wd[i] = wa[i] ^ wb[i];
|
|
90
|
+
}
|
|
91
|
+
for (i = nw * CA_MASK_WORD_BYTES; i < n; i++) {
|
|
92
|
+
md[i] = ma[i] ^ mb[i];
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
/* md[i] = 1 - ms[i] (LSB XOR pattern; preserves byte ∈ {0,1} invariant) */
|
|
97
|
+
static inline void
|
|
98
|
+
ca_mask_word_not (boolean8_t *md, const boolean8_t *ms, ca_size_t n)
|
|
99
|
+
{
|
|
100
|
+
ca_size_t nw = n / CA_MASK_WORD_BYTES;
|
|
101
|
+
ca_size_t i;
|
|
102
|
+
uint64_t *wd = (uint64_t *) md;
|
|
103
|
+
const uint64_t *ws = (const uint64_t *) ms;
|
|
104
|
+
for (i = 0; i < nw; i++) {
|
|
105
|
+
wd[i] = ws[i] ^ CA_MASK_WORD_LSB;
|
|
106
|
+
}
|
|
107
|
+
for (i = nw * CA_MASK_WORD_BYTES; i < n; i++) {
|
|
108
|
+
md[i] = ms[i] ^ (boolean8_t) 1;
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
/* 1 if any byte in [0, n) is non-zero, 0 otherwise (with early exit).
|
|
113
|
+
Consumer: ca_is_any_masked / any_masked? scan. */
|
|
114
|
+
static inline int
|
|
115
|
+
ca_mask_word_any (const boolean8_t *m, ca_size_t n)
|
|
116
|
+
{
|
|
117
|
+
ca_size_t nw = n / CA_MASK_WORD_BYTES;
|
|
118
|
+
ca_size_t i;
|
|
119
|
+
const uint64_t *w = (const uint64_t *) m;
|
|
120
|
+
for (i = 0; i < nw; i++) {
|
|
121
|
+
if ( w[i] != 0 ) return 1;
|
|
122
|
+
}
|
|
123
|
+
for (i = nw * CA_MASK_WORD_BYTES; i < n; i++) {
|
|
124
|
+
if ( m[i] ) return 1;
|
|
125
|
+
}
|
|
126
|
+
return 0;
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
/* 1 if every byte in [0, n) is non-zero, 0 otherwise (with early exit).
|
|
130
|
+
The {0,1} invariant makes a word check (w == LSB pattern) sufficient.
|
|
131
|
+
Consumer: ca_is_all_masked / all_masked? scan. */
|
|
132
|
+
static inline int
|
|
133
|
+
ca_mask_word_all_set (const boolean8_t *m, ca_size_t n)
|
|
134
|
+
{
|
|
135
|
+
ca_size_t nw = n / CA_MASK_WORD_BYTES;
|
|
136
|
+
ca_size_t i;
|
|
137
|
+
const uint64_t *w = (const uint64_t *) m;
|
|
138
|
+
for (i = 0; i < nw; i++) {
|
|
139
|
+
if ( w[i] != CA_MASK_WORD_LSB ) return 0;
|
|
140
|
+
}
|
|
141
|
+
for (i = nw * CA_MASK_WORD_BYTES; i < n; i++) {
|
|
142
|
+
if ( ! m[i] ) return 0;
|
|
143
|
+
}
|
|
144
|
+
return 1;
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
/* Count of set bytes in [0, n). Under the {0,1} invariant, a plain accumulate of
|
|
148
|
+
uint8 lanes (no popcount needed); compilers lower this to a horizontal
|
|
149
|
+
byte-sum (psadbw / uaddlv). Consumer: ca_count_masked. */
|
|
150
|
+
static inline ca_size_t
|
|
151
|
+
ca_mask_word_count (const boolean8_t *m, ca_size_t n)
|
|
152
|
+
{
|
|
153
|
+
ca_size_t i, count = 0;
|
|
154
|
+
for (i = 0; i < n; i++) {
|
|
155
|
+
count += m[i];
|
|
156
|
+
}
|
|
157
|
+
return count;
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
/* ============================================================================
|
|
161
|
+
* (End of the word-bulk helpers. Smoke surface for these helpers is at
|
|
162
|
+
* the bottom of this file, just before Init_carray_mask.)
|
|
163
|
+
* ============================================================================ */
|
|
164
|
+
|
|
165
|
+
/* ----------------------------------------------------------------------------
|
|
166
|
+
* Virtual-mask block scan (non-materialise reduction)
|
|
167
|
+
*
|
|
168
|
+
* When a mask is a virtual view (e.g. CAStackMask composed by
|
|
169
|
+
* ca_stack_func_create_mask), ca_attach
|
|
170
|
+
* materialises the WHOLE composed mask before the word scan — which defeats
|
|
171
|
+
* the early-exit of any/all and forces peak O(n) memory. Instead, drive
|
|
172
|
+
* ca_xfer_stride over the mask in row-slab blocks pulled into one arena
|
|
173
|
+
* scratch (the same mechanism CAMonOp uses to consume a virtual source
|
|
174
|
+
* without materialising it). Peak memory = one block; ANY/ALL short-circuit
|
|
175
|
+
* on the first decisive block (a masked cell in block 0 of a K-deep CAStack
|
|
176
|
+
* returns after pulling 1/K of the data, never touching the rest).
|
|
177
|
+
*
|
|
178
|
+
* Only used for NON-alias masks (cheap/entity masks keep the O(1)-alias
|
|
179
|
+
* attach + direct word scan above). Blocking is along axis 0; for arrays
|
|
180
|
+
* with a very wide inner product (> CA_MASK_BLOCK_BYTES) the block degrades
|
|
181
|
+
* to a single row, which is still O(row) peak rather than O(n).
|
|
182
|
+
* ---------------------------------------------------------------------------- */
|
|
183
|
+
|
|
184
|
+
#define CA_MASK_BLOCK_BYTES (32 * 1024) /* L1-resident scratch budget */
|
|
185
|
+
|
|
186
|
+
enum { CA_MASK_SCAN_ANY, CA_MASK_SCAN_ALL, CA_MASK_SCAN_COUNT };
|
|
187
|
+
|
|
188
|
+
static ca_size_t
|
|
189
|
+
ca_mask_scan_virtual (CArray *mask, int op)
|
|
190
|
+
{
|
|
191
|
+
int8_t ndim = mask->ndim;
|
|
192
|
+
ca_size_t outer = mask->dim[0];
|
|
193
|
+
ca_size_t inner = 1;
|
|
194
|
+
ca_size_t native[CA_RANK_MAX];
|
|
195
|
+
ca_size_t rows, off, result = 0;
|
|
196
|
+
boolean8_t *scratch;
|
|
197
|
+
int8_t k;
|
|
198
|
+
|
|
199
|
+
for (k = 1; k < ndim; k++) {
|
|
200
|
+
inner *= mask->dim[k];
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
/* compact row-major byte strides (boolean = 1 byte/cell); native[0] == inner
|
|
204
|
+
so passing these as the destination layout writes each block contiguously
|
|
205
|
+
into scratch as [nrow * inner] cells. */
|
|
206
|
+
{
|
|
207
|
+
ca_size_t s = 1;
|
|
208
|
+
for (k = ndim - 1; k >= 0; k--) { native[k] = s; s *= mask->dim[k]; }
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
rows = (inner > 0) ? (CA_MASK_BLOCK_BYTES / inner) : CA_MASK_BLOCK_BYTES;
|
|
212
|
+
if ( rows < 1 ) rows = 1;
|
|
213
|
+
if ( rows > outer ) rows = outer;
|
|
214
|
+
|
|
215
|
+
if ( op == CA_MASK_SCAN_ALL ) {
|
|
216
|
+
result = 1; /* vacuously all-set until a non-set cell is found */
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
ca_lazy_arena_enter();
|
|
220
|
+
scratch = (boolean8_t *) ca_lazy_arena_acquire(rows * inner);
|
|
221
|
+
|
|
222
|
+
for (off = 0; off < outer; off += rows) {
|
|
223
|
+
ca_size_t nrow = (outer - off < rows) ? (outer - off) : rows;
|
|
224
|
+
ca_size_t n = nrow * inner;
|
|
225
|
+
ca_size_t starts[CA_RANK_MAX] = {0};
|
|
226
|
+
ca_size_t counts[CA_RANK_MAX];
|
|
227
|
+
|
|
228
|
+
starts[0] = off;
|
|
229
|
+
counts[0] = nrow;
|
|
230
|
+
for (k = 1; k < ndim; k++) {
|
|
231
|
+
counts[k] = mask->dim[k];
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
ca_xfer_stride(mask, starts, counts, native, scratch, CA_XFER_GET);
|
|
235
|
+
|
|
236
|
+
if ( op == CA_MASK_SCAN_ANY ) {
|
|
237
|
+
if ( ca_mask_word_any(scratch, n) ) { result = 1; break; }
|
|
238
|
+
}
|
|
239
|
+
else if ( op == CA_MASK_SCAN_ALL ) {
|
|
240
|
+
if ( ! ca_mask_word_all_set(scratch, n) ) { result = 0; break; }
|
|
241
|
+
}
|
|
242
|
+
else { /* CA_MASK_SCAN_COUNT — no early exit */
|
|
243
|
+
result += ca_mask_word_count(scratch, n);
|
|
244
|
+
}
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
ca_lazy_arena_release(scratch);
|
|
248
|
+
ca_lazy_arena_exit();
|
|
249
|
+
return result;
|
|
250
|
+
}
|
|
14
251
|
|
|
15
252
|
boolean8_t *
|
|
16
253
|
ca_mask_ptr (void *ap)
|
|
@@ -36,13 +273,32 @@ ca_has_mask (void *ap)
|
|
|
36
273
|
}
|
|
37
274
|
else if ( ca_is_caobject(ca) ) { /* CAObject */
|
|
38
275
|
CAObject *co = (CAObject *) ca;
|
|
276
|
+
if ( ca_is_face(ca) ) { /* Face: the mask follows the parent */
|
|
277
|
+
if ( co->parent && ca_has_mask(co->parent) ) {
|
|
278
|
+
ca_create_mask(ca);
|
|
279
|
+
return 1;
|
|
280
|
+
}
|
|
281
|
+
return 0;
|
|
282
|
+
}
|
|
39
283
|
if ( rb_obj_respond_to(co->self, rb_intern("mask_created?"), Qtrue) ) {
|
|
40
284
|
return RTEST(rb_funcall(co->self, rb_intern("mask_created?"), 0));
|
|
41
285
|
}
|
|
42
286
|
return ( ca->mask != NULL ) ? 1 : 0;
|
|
43
287
|
}
|
|
288
|
+
else if ( ca_test_flag(ca, CA_FLAG_MULTI_PARENTS) ) {
|
|
289
|
+
/* multi-parent view: masked iff ANY parent is (union of masked cells). */
|
|
290
|
+
CAMultiParent *mp = (CAMultiParent *) ca;
|
|
291
|
+
int32_t k;
|
|
292
|
+
for ( k = 0; k < mp->n_parents; k++ ) {
|
|
293
|
+
if ( ca_has_mask(mp->parents[k]) ) {
|
|
294
|
+
ca_create_mask(ca);
|
|
295
|
+
return 1;
|
|
296
|
+
}
|
|
297
|
+
}
|
|
298
|
+
return 0;
|
|
299
|
+
}
|
|
44
300
|
else {
|
|
45
|
-
|
|
301
|
+
CAView *cr = (CAView *) ca; /* view array, check parent array */
|
|
46
302
|
if ( ca_has_mask(cr->parent) ) {
|
|
47
303
|
ca_create_mask(ca);
|
|
48
304
|
return 1;
|
|
@@ -57,21 +313,26 @@ int
|
|
|
57
313
|
ca_is_any_masked (void *ap)
|
|
58
314
|
{
|
|
59
315
|
CArray *ca = (CArray *) ap;
|
|
60
|
-
boolean8_t *m;
|
|
61
|
-
ca_size_t i;
|
|
62
316
|
int flag = 0;
|
|
63
317
|
|
|
64
318
|
ca_update_mask(ca);
|
|
65
319
|
if ( ca->mask ) {
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
320
|
+
if ( ca_attach_is_alias(ca->mask) ) {
|
|
321
|
+
/* cheap (entity / contiguous-alias) mask: O(1) alias attach +
|
|
322
|
+
word-level any with early exit. A sparse mask returns on the first
|
|
323
|
+
non-zero word (the typical "all zero, no mask actually set" hot
|
|
324
|
+
pattern). An O(n) scan cache is not viable here: the mask is
|
|
325
|
+
dynamic and a view chain can introduce a mask mid-chain. */
|
|
326
|
+
ca_attach(ca->mask);
|
|
327
|
+
flag = ca_mask_word_any((boolean8_t *) ca->mask->ptr, ca->elements);
|
|
328
|
+
ca_detach(ca->mask);
|
|
329
|
+
}
|
|
330
|
+
else {
|
|
331
|
+
/* virtual mask (e.g. CAStackMask): block-loop the composed mask so it
|
|
332
|
+
is never materialised whole, and short-circuit on the first masked
|
|
333
|
+
block. See ca_mask_scan_virtual. */
|
|
334
|
+
flag = (int) ca_mask_scan_virtual(ca->mask, CA_MASK_SCAN_ANY);
|
|
73
335
|
}
|
|
74
|
-
ca_detach(ca->mask);
|
|
75
336
|
}
|
|
76
337
|
|
|
77
338
|
return flag;
|
|
@@ -81,26 +342,25 @@ int
|
|
|
81
342
|
ca_is_all_masked (void *ap)
|
|
82
343
|
{
|
|
83
344
|
CArray *ca = (CArray *) ap;
|
|
84
|
-
|
|
85
|
-
ca_size_t i;
|
|
86
|
-
int flag;
|
|
345
|
+
int flag = 0;
|
|
87
346
|
|
|
88
347
|
ca_update_mask(ca);
|
|
89
348
|
if ( ca->mask ) {
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
349
|
+
if ( ca_attach_is_alias(ca->mask) ) {
|
|
350
|
+
/* cheap mask: O(1) alias attach + word-level all_set with early exit.
|
|
351
|
+
The word check uses (w == 0x0101...01ULL) under the {0,1}
|
|
352
|
+
invariant; returns on the first non-fully-set word. */
|
|
353
|
+
ca_attach(ca->mask);
|
|
354
|
+
flag = ca_mask_word_all_set((boolean8_t *) ca->mask->ptr, ca->elements);
|
|
355
|
+
ca_detach(ca->mask);
|
|
356
|
+
}
|
|
357
|
+
else {
|
|
358
|
+
/* virtual mask: block-loop + short-circuit on first non-fully-set block. */
|
|
359
|
+
flag = (int) ca_mask_scan_virtual(ca->mask, CA_MASK_SCAN_ALL);
|
|
98
360
|
}
|
|
99
|
-
ca_detach(ca->mask);
|
|
100
|
-
return flag;
|
|
101
|
-
} else {
|
|
102
|
-
return 0;
|
|
103
361
|
}
|
|
362
|
+
|
|
363
|
+
return flag;
|
|
104
364
|
}
|
|
105
365
|
|
|
106
366
|
/* create mask array if array has mask but has not mask array */
|
|
@@ -132,11 +392,11 @@ ca_create_mask (void *ap)
|
|
|
132
392
|
if ( ! ca->mask ) {
|
|
133
393
|
ca_func[ca->obj_type].create_mask(ca);
|
|
134
394
|
ca_set_flag(ca->mask, CA_FLAG_MASK_ARRAY); /* set array as mask array */
|
|
135
|
-
if (
|
|
136
|
-
if (
|
|
395
|
+
if ( ca_is_view(ca) ) {
|
|
396
|
+
if ( CAVIEW(ca)->attach ) {
|
|
137
397
|
ca_attach(ca->mask);
|
|
138
|
-
if (
|
|
139
|
-
|
|
398
|
+
if ( ca_is_view(ca->mask) ) {
|
|
399
|
+
CAVIEW(ca->mask)->attach = CAVIEW(ca)->attach;
|
|
140
400
|
}
|
|
141
401
|
}
|
|
142
402
|
}
|
|
@@ -190,7 +450,6 @@ ca_copy_mask_overlay_n (void *ap, ca_size_t elements, int n, CArray **slist)
|
|
|
190
450
|
CArray *cs;
|
|
191
451
|
boolean8_t *ma, *ms;
|
|
192
452
|
int i, some_has_mask = 0;
|
|
193
|
-
ca_size_t j;
|
|
194
453
|
|
|
195
454
|
for (i=0; i<n; i++) {
|
|
196
455
|
if ( slist[i] && ca_has_mask(slist[i]) ) {
|
|
@@ -223,21 +482,17 @@ ca_copy_mask_overlay_n (void *ap, ca_size_t elements, int n, CArray **slist)
|
|
|
223
482
|
ca_attach(cs->mask);
|
|
224
483
|
ma = (boolean8_t *) ca->mask->ptr;
|
|
225
484
|
ms = (boolean8_t *) cs->mask->ptr;
|
|
485
|
+
/* scalar masked source -> broadcast to all (memset); array source
|
|
486
|
+
-> pure word OR (ca_mask_word_or). A defensive `if (*ms)` is
|
|
487
|
+
unnecessary: under the {0,1} invariant, `*ma |= *ms` equals
|
|
488
|
+
`if (*ms) *ma = 1`. Post-condition: the mask byte stays in {0,1}. */
|
|
226
489
|
if ( ca_is_scalar(cs) ) {
|
|
227
490
|
if ( *ms ) {
|
|
228
|
-
|
|
229
|
-
*ma = 1;
|
|
230
|
-
ma++;
|
|
231
|
-
}
|
|
491
|
+
memset(ma, 1, elements);
|
|
232
492
|
}
|
|
233
493
|
}
|
|
234
494
|
else {
|
|
235
|
-
|
|
236
|
-
if ( *ms ) {
|
|
237
|
-
*ma = 1;
|
|
238
|
-
}
|
|
239
|
-
ma++; ms++;
|
|
240
|
-
}
|
|
495
|
+
ca_mask_word_or(ma, ms, elements);
|
|
241
496
|
}
|
|
242
497
|
ca_detach(cs->mask);
|
|
243
498
|
}
|
|
@@ -254,7 +509,7 @@ ca_copy_mask_overlay (void *ap, ca_size_t elements, int n, ...)
|
|
|
254
509
|
va_list args;
|
|
255
510
|
int i;
|
|
256
511
|
|
|
257
|
-
slist =
|
|
512
|
+
slist = xmalloc(sizeof(CArray *)*n);
|
|
258
513
|
va_start(args, n);
|
|
259
514
|
for (i=0; i<n; i++) {
|
|
260
515
|
slist[i] = va_arg(args, CArray *);
|
|
@@ -263,7 +518,7 @@ ca_copy_mask_overlay (void *ap, ca_size_t elements, int n, ...)
|
|
|
263
518
|
|
|
264
519
|
ca_copy_mask_overlay_n(ca, elements, n, slist);
|
|
265
520
|
|
|
266
|
-
|
|
521
|
+
xfree(slist);
|
|
267
522
|
}
|
|
268
523
|
|
|
269
524
|
void
|
|
@@ -288,7 +543,7 @@ ca_copy_mask_overwrite (void *ap, ca_size_t elements, int n, ...)
|
|
|
288
543
|
va_list args;
|
|
289
544
|
int i;
|
|
290
545
|
|
|
291
|
-
slist =
|
|
546
|
+
slist = xmalloc(sizeof(CArray*)*n);
|
|
292
547
|
va_start(args, n);
|
|
293
548
|
for (i=0; i<n; i++) {
|
|
294
549
|
slist[i] = va_arg(args, CArray*);
|
|
@@ -303,7 +558,7 @@ ca_copy_mask_overwrite (void *ap, ca_size_t elements, int n, ...)
|
|
|
303
558
|
|
|
304
559
|
ca_copy_mask_overlay_n(ca, elements, n, slist);
|
|
305
560
|
|
|
306
|
-
|
|
561
|
+
xfree(slist);
|
|
307
562
|
}
|
|
308
563
|
|
|
309
564
|
void
|
|
@@ -320,19 +575,23 @@ ca_count_masked (void *ap)
|
|
|
320
575
|
{
|
|
321
576
|
CArray *ca = (CArray *) ap;
|
|
322
577
|
boolean8_t *m;
|
|
323
|
-
ca_size_t
|
|
578
|
+
ca_size_t count = 0;
|
|
324
579
|
|
|
325
580
|
ca_update_mask(ca);
|
|
326
581
|
|
|
327
582
|
if ( ca->mask ) {
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
583
|
+
if ( ca_attach_is_alias(ca->mask) ) {
|
|
584
|
+
/* cheap mask: O(1) alias attach + word-bulk count. */
|
|
585
|
+
ca_attach(ca->mask);
|
|
586
|
+
m = (boolean8_t *) ca->mask->ptr;
|
|
587
|
+
count = ca_mask_word_count(m, ca->elements);
|
|
588
|
+
ca_detach(ca->mask);
|
|
589
|
+
}
|
|
590
|
+
else {
|
|
591
|
+
/* virtual mask: block-loop the composed mask (peak O(block), no full
|
|
592
|
+
materialise). COUNT has no early exit but still avoids O(n) peak. */
|
|
593
|
+
count = ca_mask_scan_virtual(ca->mask, CA_MASK_SCAN_COUNT);
|
|
334
594
|
}
|
|
335
|
-
ca_detach(ca->mask);
|
|
336
595
|
}
|
|
337
596
|
|
|
338
597
|
return count;
|
|
@@ -345,31 +604,30 @@ ca_count_not_masked (void *ap)
|
|
|
345
604
|
return ca->elements - ca_count_masked(ca);
|
|
346
605
|
}
|
|
347
606
|
|
|
348
|
-
|
|
607
|
+
/* Scalar-fill every masked cell with fill_value, then clear its mask bit.
|
|
608
|
+
This depends only on the cell BYTE WIDTH, not the data type (the work is
|
|
609
|
+
a width-sized copy), so a single width switch covers every numeric /
|
|
610
|
+
complex / object dtype; the default arm handles fixlen and any other
|
|
611
|
+
width via memcpy. The typed arms let the compiler emit a plain store. */
|
|
612
|
+
#define proc_fill_bang_width(T) \
|
|
349
613
|
{ \
|
|
350
|
-
ca_size_t i;
|
|
351
|
-
|
|
352
|
-
|
|
614
|
+
ca_size_t i; \
|
|
615
|
+
T *p = (T *)ca->ptr; \
|
|
616
|
+
T v = *(T *)fill_value; \
|
|
353
617
|
boolean8_t *m = (boolean8_t *) ca->mask->ptr; \
|
|
354
618
|
for (i=0; i<ca->elements; i++) { \
|
|
355
|
-
if ( *m ) {
|
|
356
|
-
*p = v; \
|
|
357
|
-
*m = 0; \
|
|
358
|
-
} \
|
|
619
|
+
if ( *m ) { *p = v; *m = 0; } \
|
|
359
620
|
m++; p++; \
|
|
360
621
|
} \
|
|
361
622
|
}
|
|
362
623
|
|
|
363
|
-
#define
|
|
624
|
+
#define proc_fill_bang_bytes() \
|
|
364
625
|
{ \
|
|
365
|
-
ca_size_t i;
|
|
626
|
+
ca_size_t i; \
|
|
366
627
|
char *p = ca->ptr; \
|
|
367
628
|
boolean8_t *m = (boolean8_t *) ca->mask->ptr; \
|
|
368
629
|
for (i=0; i<ca->elements; i++) { \
|
|
369
|
-
if ( *m ) {
|
|
370
|
-
memcpy(p, fill_value, ca->bytes); \
|
|
371
|
-
*m = 0; \
|
|
372
|
-
} \
|
|
630
|
+
if ( *m ) { memcpy(p, fill_value, ca->bytes); *m = 0; } \
|
|
373
631
|
m++; p+=ca->bytes; \
|
|
374
632
|
} \
|
|
375
633
|
}
|
|
@@ -388,27 +646,12 @@ ca_unmask (void *ap, char *fill_value)
|
|
|
388
646
|
else {
|
|
389
647
|
ca_attach(ca);
|
|
390
648
|
|
|
391
|
-
switch ( ca->
|
|
392
|
-
case
|
|
393
|
-
case
|
|
394
|
-
case
|
|
395
|
-
case
|
|
396
|
-
|
|
397
|
-
case CA_UINT16: proc_fill_bang(int16_t); break;
|
|
398
|
-
case CA_INT32:
|
|
399
|
-
case CA_UINT32:
|
|
400
|
-
case CA_FLOAT32: proc_fill_bang(int32_t); break;
|
|
401
|
-
case CA_INT64:
|
|
402
|
-
case CA_UINT64:
|
|
403
|
-
case CA_FLOAT64: proc_fill_bang(float64_t); break;
|
|
404
|
-
case CA_FLOAT128: proc_fill_bang(float128_t); break;
|
|
405
|
-
#ifdef HAVE_COMPLEX_H
|
|
406
|
-
case CA_CMPLX64: proc_fill_bang(cmplx64_t); break;
|
|
407
|
-
case CA_CMPLX128: proc_fill_bang(cmplx128_t); break;
|
|
408
|
-
case CA_CMPLX256: proc_fill_bang(cmplx256_t); break;
|
|
409
|
-
#endif
|
|
410
|
-
case CA_OBJECT: proc_fill_bang(VALUE); break;
|
|
411
|
-
default: rb_bug("array has an unknown data type");
|
|
649
|
+
switch ( ca->bytes ) {
|
|
650
|
+
case 1: proc_fill_bang_width(uint8_t); break;
|
|
651
|
+
case 2: proc_fill_bang_width(uint16_t); break;
|
|
652
|
+
case 4: proc_fill_bang_width(uint32_t); break;
|
|
653
|
+
case 8: proc_fill_bang_width(uint64_t); break;
|
|
654
|
+
default: proc_fill_bang_bytes(); break; /* cmplx128 (16) + fixlen */
|
|
412
655
|
}
|
|
413
656
|
|
|
414
657
|
ca_sync(ca);
|
|
@@ -450,7 +693,6 @@ ca_invert_mask (void *ap)
|
|
|
450
693
|
{
|
|
451
694
|
CArray *ca = (CArray *) ap;
|
|
452
695
|
boolean8_t *m;
|
|
453
|
-
ca_size_t i;
|
|
454
696
|
|
|
455
697
|
ca_update_mask(ca);
|
|
456
698
|
|
|
@@ -460,10 +702,7 @@ ca_invert_mask (void *ap)
|
|
|
460
702
|
|
|
461
703
|
ca_attach(ca->mask);
|
|
462
704
|
m = (boolean8_t *) ca->mask->ptr;
|
|
463
|
-
|
|
464
|
-
*m ^= 1;
|
|
465
|
-
m++;
|
|
466
|
-
}
|
|
705
|
+
ca_mask_word_not(m, m, ca->elements); /* in-place 1 - m, LSB-XOR word loop */
|
|
467
706
|
ca_detach(ca->mask);
|
|
468
707
|
|
|
469
708
|
return;
|
|
@@ -500,7 +739,11 @@ ca_allocate_mask_iterator_n (int n, CArray **slist)
|
|
|
500
739
|
}
|
|
501
740
|
}
|
|
502
741
|
|
|
503
|
-
|
|
742
|
+
/* elements stays at its -1 sentinel only when no non-NULL array was given
|
|
743
|
+
(degenerate call); guard so the alloc/memset never see a negative size
|
|
744
|
+
(which would wrap to a huge bound). Also silences GCC's VRP warnings. */
|
|
745
|
+
if ( elements < 0 ) elements = 0;
|
|
746
|
+
m = xmalloc(sizeof(boolean8_t)*elements);
|
|
504
747
|
memset(m, 0, elements);
|
|
505
748
|
|
|
506
749
|
if ( ! some_has_mask ) {
|
|
@@ -528,10 +771,7 @@ ca_allocate_mask_iterator_n (int n, CArray **slist)
|
|
|
528
771
|
}
|
|
529
772
|
}
|
|
530
773
|
else {
|
|
531
|
-
|
|
532
|
-
*mp = ( *mp || *ms );
|
|
533
|
-
mp++; ms++;
|
|
534
|
-
}
|
|
774
|
+
ca_mask_word_or(mp, ms, elements); /* m[j] |= ms[j], word-bulk OR */
|
|
535
775
|
}
|
|
536
776
|
ca_detach(cs->mask);
|
|
537
777
|
}
|
|
@@ -546,7 +786,7 @@ ca_allocate_mask_iterator (int n, ...)
|
|
|
546
786
|
va_list args;
|
|
547
787
|
int i;
|
|
548
788
|
|
|
549
|
-
slist =
|
|
789
|
+
slist = xmalloc(sizeof(CArray *)*n);
|
|
550
790
|
va_start(args, n);
|
|
551
791
|
for (i=0; i<n; i++) {
|
|
552
792
|
slist[i] = va_arg(args, CArray *);
|
|
@@ -555,7 +795,7 @@ ca_allocate_mask_iterator (int n, ...)
|
|
|
555
795
|
|
|
556
796
|
m = ca_allocate_mask_iterator_n(n, slist);
|
|
557
797
|
|
|
558
|
-
|
|
798
|
+
xfree(slist);
|
|
559
799
|
|
|
560
800
|
return m;
|
|
561
801
|
}
|
|
@@ -656,8 +896,38 @@ rb_ca_value_array (VALUE self)
|
|
|
656
896
|
obj = rb_ca_refer_new(self, ca->data_type, ca->ndim, ca->dim, ca->bytes, 0);
|
|
657
897
|
TypedData_Get_Struct(obj, CArray, &carray_data_type, co);
|
|
658
898
|
|
|
899
|
+
/* Value arrays ignore the mask by definition. ca_stride_setup
|
|
900
|
+
auto-propagates the parent's mask into newly-built CAStride
|
|
901
|
+
subclasses; strip it here so ca_has_mask(value_array) stays
|
|
902
|
+
false. */
|
|
903
|
+
if (co->mask) {
|
|
904
|
+
ca_free(co->mask);
|
|
905
|
+
co->mask = NULL;
|
|
906
|
+
}
|
|
659
907
|
ca_set_flag(co, CA_FLAG_VALUE_ARRAY);
|
|
660
908
|
|
|
909
|
+
/* CAREFUL: when self is a Face, rb_ca_refer_new has already lifted its
|
|
910
|
+
result, so `co` above is the Face wrapper and the refer that actually
|
|
911
|
+
carries the data sits underneath it. Marking only the wrapper is not
|
|
912
|
+
enough: kernels strip Faces at entry (ca_strip_face) and would go on
|
|
913
|
+
to ask that refer for a mask, which -- having none of its own -- it
|
|
914
|
+
answers from its parent. The values would read back raw while a
|
|
915
|
+
reduction over them still skipped the masked cells.
|
|
916
|
+
|
|
917
|
+
Marking the storage level is enough for anything stacked in between,
|
|
918
|
+
since ca_is_value_array inherits from parent to child. */
|
|
919
|
+
if ( ca_is_face(co) ) {
|
|
920
|
+
CArray *storage = ca_strip_face(co);
|
|
921
|
+
if ( storage ) {
|
|
922
|
+
if ( storage->mask ) {
|
|
923
|
+
ca_free(storage->mask);
|
|
924
|
+
storage->mask = NULL;
|
|
925
|
+
}
|
|
926
|
+
ca_set_flag(storage, CA_FLAG_VALUE_ARRAY);
|
|
927
|
+
}
|
|
928
|
+
}
|
|
929
|
+
|
|
930
|
+
CA_FACE_LIFT_IF_FACE(obj, self, ca);
|
|
661
931
|
return obj;
|
|
662
932
|
}
|
|
663
933
|
|
|
@@ -671,7 +941,7 @@ The mask array can't be set mask.
|
|
|
671
941
|
VALUE
|
|
672
942
|
rb_ca_mask_array (VALUE self)
|
|
673
943
|
{
|
|
674
|
-
VALUE obj;
|
|
944
|
+
volatile VALUE obj;
|
|
675
945
|
CArray *ca;
|
|
676
946
|
|
|
677
947
|
TypedData_Get_Struct(self, CArray, &carray_data_type, ca);
|
|
@@ -830,36 +1100,6 @@ rb_ca_is_not_masked (VALUE self)
|
|
|
830
1100
|
return ca_wrap_struct(co);
|
|
831
1101
|
}
|
|
832
1102
|
|
|
833
|
-
/* @overload count_masked
|
|
834
|
-
|
|
835
|
-
(Masking, Statistics)
|
|
836
|
-
Returns the number of masked elements.
|
|
837
|
-
*/
|
|
838
|
-
|
|
839
|
-
VALUE
|
|
840
|
-
rb_ca_count_masked (VALUE self)
|
|
841
|
-
{
|
|
842
|
-
CArray *ca;
|
|
843
|
-
printf("hello\n");
|
|
844
|
-
TypedData_Get_Struct(self, CArray, &carray_data_type, ca);
|
|
845
|
-
printf("world\n");
|
|
846
|
-
return SIZE2NUM(ca_count_masked(ca));
|
|
847
|
-
}
|
|
848
|
-
|
|
849
|
-
/* @overload count_not_masked
|
|
850
|
-
|
|
851
|
-
(Masking, Statistics)
|
|
852
|
-
Returns the number of not-masked elements.
|
|
853
|
-
*/
|
|
854
|
-
|
|
855
|
-
VALUE
|
|
856
|
-
rb_ca_count_not_masked (VALUE self)
|
|
857
|
-
{
|
|
858
|
-
CArray *ca;
|
|
859
|
-
TypedData_Get_Struct(self, CArray, &carray_data_type, ca);
|
|
860
|
-
return SIZE2NUM(ca_count_not_masked(ca));
|
|
861
|
-
}
|
|
862
|
-
|
|
863
1103
|
/* @overload unmask (fill_value = nil)
|
|
864
1104
|
|
|
865
1105
|
(Masking, Destructive)
|
|
@@ -886,7 +1126,14 @@ rb_ca_unmask_method (int argc, VALUE *argv, VALUE self)
|
|
|
886
1126
|
TypedData_Get_Struct(self, CArray, &carray_data_type, ca);
|
|
887
1127
|
|
|
888
1128
|
if ( rfval != CA_NIL ) {
|
|
889
|
-
|
|
1129
|
+
/* Face has surface != storage; the fill value is cast in the storage data_type. */
|
|
1130
|
+
int8_t conv_type = ca->data_type;
|
|
1131
|
+
if ( ca_is_face(ca) ) {
|
|
1132
|
+
CArray *root = ca;
|
|
1133
|
+
while (root && ca_is_face(root)) root = ((CAView *) root)->parent;
|
|
1134
|
+
if (root) conv_type = root->data_type;
|
|
1135
|
+
}
|
|
1136
|
+
rcs = rb_cscalar_new_with_value(conv_type, ca->bytes, rfval);
|
|
890
1137
|
TypedData_Get_Struct(rcs, CScalar, &cscalar_data_type, cv);
|
|
891
1138
|
fval = cv->ptr;
|
|
892
1139
|
}
|
|
@@ -936,14 +1183,21 @@ rb_ca_unmask_copy_method (int argc, VALUE *argv, VALUE self)
|
|
|
936
1183
|
TypedData_Get_Struct(self, CArray, &carray_data_type, ca);
|
|
937
1184
|
|
|
938
1185
|
if ( rfval != CA_NIL ) {
|
|
939
|
-
|
|
1186
|
+
/* Face has surface != storage; the fill value is cast in the storage data_type. */
|
|
1187
|
+
int8_t conv_type = ca->data_type;
|
|
1188
|
+
if ( ca_is_face(ca) ) {
|
|
1189
|
+
CArray *root = ca;
|
|
1190
|
+
while (root && ca_is_face(root)) root = ((CAView *) root)->parent;
|
|
1191
|
+
if (root) conv_type = root->data_type;
|
|
1192
|
+
}
|
|
1193
|
+
rcs = rb_cscalar_new_with_value(conv_type, ca->bytes, rfval);
|
|
940
1194
|
TypedData_Get_Struct(rcs, CScalar, &cscalar_data_type, cv);
|
|
941
1195
|
fval = cv->ptr;
|
|
942
1196
|
}
|
|
943
1197
|
|
|
944
1198
|
co = ca_unmask_copy(ca, fval);
|
|
945
1199
|
obj = ca_wrap_struct(co);
|
|
946
|
-
|
|
1200
|
+
CA_FACE_LIFT_IF_FACE(obj, self, ca);
|
|
947
1201
|
return obj;
|
|
948
1202
|
}
|
|
949
1203
|
|
|
@@ -978,6 +1232,71 @@ rb_ca_invert_mask (VALUE self)
|
|
|
978
1232
|
return self;
|
|
979
1233
|
}
|
|
980
1234
|
|
|
1235
|
+
/* guard_undef(*values, fill_value: UNDEF, &block) — class method.
|
|
1236
|
+
Returns fill_value if any of values is UNDEF, otherwise yields values
|
|
1237
|
+
to the block and returns its result.
|
|
1238
|
+
See lib/carray/mask.rb (removed in CIFY). */
|
|
1239
|
+
static VALUE
|
|
1240
|
+
rb_ca_s_guard_undef(int argc, VALUE *argv, VALUE klass)
|
|
1241
|
+
{
|
|
1242
|
+
VALUE values, opts;
|
|
1243
|
+
VALUE fill_value = CA_UNDEF;
|
|
1244
|
+
long i, n;
|
|
1245
|
+
|
|
1246
|
+
rb_scan_args(argc, argv, "*:", &values, &opts);
|
|
1247
|
+
rb_scan_options(opts, "fill_value", &fill_value);
|
|
1248
|
+
|
|
1249
|
+
n = RARRAY_LEN(values);
|
|
1250
|
+
for (i = 0; i < n; i++) {
|
|
1251
|
+
if (RARRAY_AREF(values, i) == CA_UNDEF) {
|
|
1252
|
+
return fill_value;
|
|
1253
|
+
}
|
|
1254
|
+
}
|
|
1255
|
+
|
|
1256
|
+
return rb_yield_splat(values);
|
|
1257
|
+
}
|
|
1258
|
+
|
|
1259
|
+
/* mask_eq(v) — return form of "mask cells equal to v".
|
|
1260
|
+
Equivalent to: obj = copy; obj[:eq, v] = UNDEF; obj */
|
|
1261
|
+
static VALUE
|
|
1262
|
+
rb_ca_mask_eq (VALUE self, VALUE v)
|
|
1263
|
+
{
|
|
1264
|
+
volatile VALUE obj = rb_ca_copy(self);
|
|
1265
|
+
rb_funcall(obj, rb_intern("[]="), 3,
|
|
1266
|
+
ID2SYM(rb_intern("eq")), v, CA_UNDEF);
|
|
1267
|
+
return obj;
|
|
1268
|
+
}
|
|
1269
|
+
|
|
1270
|
+
/* mask_invalid — return form of "mask NaN/Inf cells".
|
|
1271
|
+
Equivalent to: obj = copy; obj[:is_invalid] = UNDEF; obj */
|
|
1272
|
+
static VALUE
|
|
1273
|
+
rb_ca_mask_invalid (VALUE self)
|
|
1274
|
+
{
|
|
1275
|
+
volatile VALUE obj = rb_ca_copy(self);
|
|
1276
|
+
rb_funcall(obj, rb_intern("[]="), 2,
|
|
1277
|
+
ID2SYM(rb_intern("is_invalid")), CA_UNDEF);
|
|
1278
|
+
return obj;
|
|
1279
|
+
}
|
|
1280
|
+
|
|
1281
|
+
/* mask_where(*args) — generic return form predicate masking.
|
|
1282
|
+
Equivalent to: obj = copy; obj[*args] = UNDEF; obj
|
|
1283
|
+
Requires at least 1 argument. */
|
|
1284
|
+
static VALUE
|
|
1285
|
+
rb_ca_mask_where (int argc, VALUE *argv, VALUE self)
|
|
1286
|
+
{
|
|
1287
|
+
volatile VALUE obj;
|
|
1288
|
+
VALUE *all_args;
|
|
1289
|
+
if (argc == 0) {
|
|
1290
|
+
rb_raise(rb_eArgError, "mask_where requires at least 1 argument");
|
|
1291
|
+
}
|
|
1292
|
+
obj = rb_ca_copy(self);
|
|
1293
|
+
all_args = ALLOCA_N(VALUE, argc + 1);
|
|
1294
|
+
memcpy(all_args, argv, argc * sizeof(VALUE));
|
|
1295
|
+
all_args[argc] = CA_UNDEF;
|
|
1296
|
+
rb_funcallv(obj, rb_intern("[]="), argc + 1, all_args);
|
|
1297
|
+
return obj;
|
|
1298
|
+
}
|
|
1299
|
+
|
|
981
1300
|
/* @overload inherit_mask (*others):
|
|
982
1301
|
|
|
983
1302
|
(Masking, Destructive)
|
|
@@ -996,7 +1315,7 @@ rb_ca_inherit_mask_method (int argc, VALUE *argv, VALUE self)
|
|
|
996
1315
|
|
|
997
1316
|
TypedData_Get_Struct(self, CArray, &carray_data_type, ca);
|
|
998
1317
|
|
|
999
|
-
slist =
|
|
1318
|
+
slist = xmalloc(sizeof(CArray *)*argc);
|
|
1000
1319
|
for (i=0; i<argc; i++) {
|
|
1001
1320
|
if ( rb_obj_is_carray(argv[i]) ) {
|
|
1002
1321
|
TypedData_Get_Struct(argv[i], CArray, &carray_data_type, cs);
|
|
@@ -1008,7 +1327,7 @@ rb_ca_inherit_mask_method (int argc, VALUE *argv, VALUE self)
|
|
|
1008
1327
|
}
|
|
1009
1328
|
ca_copy_mask_overlay_n(ca, ca->elements, argc, slist);
|
|
1010
1329
|
|
|
1011
|
-
|
|
1330
|
+
xfree(slist);
|
|
1012
1331
|
|
|
1013
1332
|
return self;
|
|
1014
1333
|
}
|
|
@@ -1037,7 +1356,7 @@ rb_ca_inherit_mask (VALUE self, int n, ...)
|
|
|
1037
1356
|
TypedData_Get_Struct(self, CArray, &carray_data_type, ca);
|
|
1038
1357
|
|
|
1039
1358
|
va_start(rothers, n);
|
|
1040
|
-
slist =
|
|
1359
|
+
slist = xmalloc(sizeof(CArray *)*n);
|
|
1041
1360
|
for (i=0; i<n; i++) {
|
|
1042
1361
|
other = va_arg(rothers, VALUE);
|
|
1043
1362
|
if ( rb_obj_is_carray(other) ) {
|
|
@@ -1052,7 +1371,7 @@ rb_ca_inherit_mask (VALUE self, int n, ...)
|
|
|
1052
1371
|
|
|
1053
1372
|
ca_copy_mask_overlay_n(ca, ca->elements, n, slist);
|
|
1054
1373
|
|
|
1055
|
-
|
|
1374
|
+
xfree(slist);
|
|
1056
1375
|
|
|
1057
1376
|
return self;
|
|
1058
1377
|
}
|
|
@@ -1075,7 +1394,7 @@ rb_ca_inherit_mask_replace_method (int argc, VALUE *argv, VALUE self)
|
|
|
1075
1394
|
|
|
1076
1395
|
TypedData_Get_Struct(self, CArray, &carray_data_type, ca);
|
|
1077
1396
|
|
|
1078
|
-
slist =
|
|
1397
|
+
slist = xmalloc(sizeof(CArray *)*argc);
|
|
1079
1398
|
for (i=0; i<argc; i++) {
|
|
1080
1399
|
if ( rb_obj_is_carray(argv[i]) ) {
|
|
1081
1400
|
TypedData_Get_Struct(argv[i], CArray, &carray_data_type, cs);
|
|
@@ -1087,7 +1406,7 @@ rb_ca_inherit_mask_replace_method (int argc, VALUE *argv, VALUE self)
|
|
|
1087
1406
|
}
|
|
1088
1407
|
ca_copy_mask_overwrite_n(ca, ca->elements, argc, slist);
|
|
1089
1408
|
|
|
1090
|
-
|
|
1409
|
+
xfree(slist);
|
|
1091
1410
|
|
|
1092
1411
|
return self;
|
|
1093
1412
|
}
|
|
@@ -1116,7 +1435,7 @@ rb_ca_inherit_mask_replace (VALUE self, int n, ...)
|
|
|
1116
1435
|
TypedData_Get_Struct(self, CArray, &carray_data_type, ca);
|
|
1117
1436
|
|
|
1118
1437
|
va_start(rothers, n);
|
|
1119
|
-
slist =
|
|
1438
|
+
slist = xmalloc(sizeof(CArray *)*n);
|
|
1120
1439
|
for (i=0; i<n; i++) {
|
|
1121
1440
|
other = va_arg(rothers, VALUE);
|
|
1122
1441
|
if ( rb_obj_is_carray(other) ) {
|
|
@@ -1131,13 +1450,375 @@ rb_ca_inherit_mask_replace (VALUE self, int n, ...)
|
|
|
1131
1450
|
|
|
1132
1451
|
ca_copy_mask_overwrite_n(ca, ca->elements, n, slist);
|
|
1133
1452
|
|
|
1134
|
-
|
|
1453
|
+
xfree(slist);
|
|
1135
1454
|
|
|
1136
1455
|
return self;
|
|
1137
1456
|
}
|
|
1138
1457
|
|
|
1458
|
+
/* Helper: build a fill-valued int64 CArray whose shape is ca's shape
|
|
1459
|
+
with the listed axes removed. Used by count_masked/count_not_masked
|
|
1460
|
+
when there is no mask. */
|
|
1461
|
+
static VALUE
|
|
1462
|
+
ca_make_reduced_int64 (CArray *ca, int argc, VALUE *argv, ca_size_t fill_val)
|
|
1463
|
+
{
|
|
1464
|
+
ca_size_t dim[CA_RANK_MAX];
|
|
1465
|
+
int8_t ndim = 0;
|
|
1466
|
+
int i, j;
|
|
1467
|
+
int64_t v = (int64_t) fill_val;
|
|
1468
|
+
VALUE result;
|
|
1469
|
+
CArray *co;
|
|
1470
|
+
|
|
1471
|
+
for (i = 0; i < ca->ndim; i++) {
|
|
1472
|
+
int is_axis = 0;
|
|
1473
|
+
for (j = 0; j < argc; j++) {
|
|
1474
|
+
int ax = NUM2INT(argv[j]);
|
|
1475
|
+
if (ax < 0) ax += ca->ndim;
|
|
1476
|
+
if (ax == i) { is_axis = 1; break; }
|
|
1477
|
+
}
|
|
1478
|
+
if (!is_axis) dim[ndim++] = ca->dim[i];
|
|
1479
|
+
}
|
|
1480
|
+
|
|
1481
|
+
result = rb_carray_new(CA_INT64, ndim, dim, 0, NULL);
|
|
1482
|
+
TypedData_Get_Struct(result, CArray, &carray_data_type, co);
|
|
1483
|
+
ca_fill(co, &v);
|
|
1484
|
+
return result;
|
|
1485
|
+
}
|
|
1486
|
+
|
|
1487
|
+
/* count_true_ki / count_false_ki -- mkkernel-generated count kernels
|
|
1488
|
+
(carray_kernels.c, bind_ruby: false; output CA_INT64). Used to reduce
|
|
1489
|
+
the boolean mask along axes in C for count_masked / count_not_masked,
|
|
1490
|
+
so the axis path needs no Ruby accumulate trampoline. */
|
|
1491
|
+
extern VALUE rb_ca_count_true_ki (int argc, VALUE *argv, VALUE self);
|
|
1492
|
+
extern VALUE rb_ca_count_false_ki (int argc, VALUE *argv, VALUE self);
|
|
1493
|
+
|
|
1494
|
+
/* count_masked(*axis) -- number of masked elements, optionally reduced
|
|
1495
|
+
along axes (no args -> Integer; axis args -> int64 CArray with those
|
|
1496
|
+
axes collapsed).
|
|
1497
|
+
|
|
1498
|
+
Public (non-static) linkage so rb_ca_count in carray_count.c can forward
|
|
1499
|
+
count(UNDEF) here directly without going through Ruby method dispatch. */
|
|
1500
|
+
VALUE
|
|
1501
|
+
rb_ca_count_masked (int argc, VALUE *argv, VALUE self)
|
|
1502
|
+
{
|
|
1503
|
+
CArray *ca;
|
|
1504
|
+
TypedData_Get_Struct(self, CArray, &carray_data_type, ca);
|
|
1505
|
+
|
|
1506
|
+
/* Accepts the axis: kwarg only (positional axis is not accepted),
|
|
1507
|
+
matching the aggregate _ki family. */
|
|
1508
|
+
VALUE kw_hash, axis_val = Qnil;
|
|
1509
|
+
rb_scan_args(argc, argv, "0:", &kw_hash);
|
|
1510
|
+
rb_scan_options(kw_hash, "axis", &axis_val);
|
|
1511
|
+
|
|
1512
|
+
/* No mask -> nothing is masked. Short-circuit without materialising a
|
|
1513
|
+
zero mask (axis form returns a zero-filled reduced int64 array). */
|
|
1514
|
+
if (!ca_has_mask(ca)) {
|
|
1515
|
+
if (NIL_P(axis_val)) {
|
|
1516
|
+
return SIZE2NUM(0);
|
|
1517
|
+
}
|
|
1518
|
+
VALUE axis_ary = (TYPE(axis_val) == T_ARRAY) ? axis_val : rb_ary_new3(1, axis_val);
|
|
1519
|
+
return ca_make_reduced_int64(ca, (int) RARRAY_LEN(axis_ary),
|
|
1520
|
+
(VALUE *) RARRAY_CONST_PTR(axis_ary), 0);
|
|
1521
|
+
}
|
|
1522
|
+
|
|
1523
|
+
/* Masked count = count of `true` in the mask, routed through the
|
|
1524
|
+
count_true_ki kernel for both the flat (no axis -> scalar Integer) and
|
|
1525
|
+
per-axis (-> reduced CArray) forms. The scalar C primitive
|
|
1526
|
+
ca_count_masked stays separately for internal C callers (grid /
|
|
1527
|
+
median / order) that need a raw ca_size_t without Ruby allocation. */
|
|
1528
|
+
VALUE mask_obj = rb_ca_mask_array(self);
|
|
1529
|
+
if (NIL_P(axis_val)) {
|
|
1530
|
+
return rb_ca_count_true_ki(0, NULL, mask_obj);
|
|
1531
|
+
}
|
|
1532
|
+
VALUE kw = rb_hash_new();
|
|
1533
|
+
rb_hash_aset(kw, ID2SYM(rb_intern("axis")), axis_val);
|
|
1534
|
+
VALUE kargv[1] = { kw };
|
|
1535
|
+
return rb_ca_count_true_ki(1, kargv, mask_obj);
|
|
1536
|
+
}
|
|
1537
|
+
|
|
1538
|
+
/* @overload count_not_masked(*axis)
|
|
1539
|
+
|
|
1540
|
+
(Masking, Statistics)
|
|
1541
|
+
Returns the number of not-masked elements, optionally reduced along axes.
|
|
1542
|
+
With no arguments returns an Integer. With axis arguments returns a
|
|
1543
|
+
CArray of int64 with the specified axes collapsed.
|
|
1544
|
+
*/
|
|
1545
|
+
/* C-callable entry: skip rb_scan_args (call-frame state dependency).
|
|
1546
|
+
* axis_val = Qnil -> scalar Integer count over whole array
|
|
1547
|
+
* axis_val = Integer/Array -> reduced CArray int64
|
|
1548
|
+
* For ext authors who need to call count_not_masked from C. */
|
|
1549
|
+
VALUE
|
|
1550
|
+
rb_ca_count_not_masked_c (VALUE self, VALUE axis_val)
|
|
1551
|
+
{
|
|
1552
|
+
CArray *ca;
|
|
1553
|
+
TypedData_Get_Struct(self, CArray, &carray_data_type, ca);
|
|
1554
|
+
|
|
1555
|
+
/* No mask -> every element is not-masked. Short-circuit without
|
|
1556
|
+
materialising a mask (no axis -> elements; axis form -> reduced int64
|
|
1557
|
+
filled with the product of the reduced-axis extents). */
|
|
1558
|
+
if (!ca_has_mask(ca)) {
|
|
1559
|
+
if (NIL_P(axis_val)) {
|
|
1560
|
+
return SIZE2NUM(ca->elements);
|
|
1561
|
+
}
|
|
1562
|
+
VALUE axis_ary = (TYPE(axis_val) == T_ARRAY) ? axis_val : rb_ary_new3(1, axis_val);
|
|
1563
|
+
int n_axes = (int) RARRAY_LEN(axis_ary);
|
|
1564
|
+
VALUE *axes_argv = (VALUE *) RARRAY_CONST_PTR(axis_ary);
|
|
1565
|
+
ca_size_t axis_vol = 1;
|
|
1566
|
+
int j;
|
|
1567
|
+
for (j = 0; j < n_axes; j++) {
|
|
1568
|
+
int ax = NUM2INT(axes_argv[j]);
|
|
1569
|
+
if (ax < 0) ax += ca->ndim;
|
|
1570
|
+
axis_vol *= ca->dim[ax];
|
|
1571
|
+
}
|
|
1572
|
+
return ca_make_reduced_int64(ca, n_axes, axes_argv, axis_vol);
|
|
1573
|
+
}
|
|
1574
|
+
|
|
1575
|
+
/* Not-masked count = count of `false` in the mask, routed through the
|
|
1576
|
+
count_false_ki kernel for both the flat (no axis -> scalar Integer) and
|
|
1577
|
+
per-axis (-> reduced CArray) forms. The scalar C primitive
|
|
1578
|
+
ca_count_not_masked (= elements - ca_count_masked) stays separately for
|
|
1579
|
+
internal C callers (median / order) that need a raw ca_size_t. */
|
|
1580
|
+
VALUE mask_obj = rb_ca_mask_array(self);
|
|
1581
|
+
if (NIL_P(axis_val)) {
|
|
1582
|
+
return rb_ca_count_false_ki(0, NULL, mask_obj);
|
|
1583
|
+
}
|
|
1584
|
+
VALUE kw = rb_hash_new();
|
|
1585
|
+
rb_hash_aset(kw, ID2SYM(rb_intern("axis")), axis_val);
|
|
1586
|
+
VALUE kargv[1] = { kw };
|
|
1587
|
+
return rb_ca_count_false_ki(1, kargv, mask_obj);
|
|
1588
|
+
}
|
|
1589
|
+
|
|
1590
|
+
/* Ruby binding entry: parses (axis: kwarg) -> forwards to _c. */
|
|
1591
|
+
VALUE
|
|
1592
|
+
rb_ca_count_not_masked (int argc, VALUE *argv, VALUE self)
|
|
1593
|
+
{
|
|
1594
|
+
/* Accepts the axis: kwarg only. */
|
|
1595
|
+
VALUE kw_hash, axis_val = Qnil;
|
|
1596
|
+
rb_scan_args(argc, argv, "0:", &kw_hash);
|
|
1597
|
+
rb_scan_options(kw_hash, "axis", &axis_val);
|
|
1598
|
+
return rb_ca_count_not_masked_c(self, axis_val);
|
|
1599
|
+
}
|
|
1600
|
+
|
|
1601
|
+
/* ============================================================================
|
|
1602
|
+
* boolean8_t bit-op fast paths (table slot patching)
|
|
1603
|
+
*
|
|
1604
|
+
* Strategy: ext/carray_math.c is GENERATED (= .gitignored, regen'd from
|
|
1605
|
+
* ext/carray_math.rb). Hand-editing the generated functions would be
|
|
1606
|
+
* overwritten by `ruby carray_math.rb`. Instead we:
|
|
1607
|
+
*
|
|
1608
|
+
* 1. Define standalone boolean8_t-only fast functions here in
|
|
1609
|
+
* carray_mask.c (= hand-written, regen-safe).
|
|
1610
|
+
* 2. Patch the generated dispatch tables at Init time:
|
|
1611
|
+
* ca_binop_bit_and_i[CA_BOOLEAN] = ca_binop_bit_and_i_boolean8_fast;
|
|
1612
|
+
* (and similarly for _or_i, _xor_i, and ca_monop_bit_neg)
|
|
1613
|
+
* 3. Init_carray_mask_fast_paths() is called AFTER Init_carray_math()
|
|
1614
|
+
* from ruby_carray.c so the patch sticks.
|
|
1615
|
+
*
|
|
1616
|
+
* Each fast function handles BOTH paths internally:
|
|
1617
|
+
* fast: m == NULL && i1 == i2 == i3 == 1 (contig + no mask)
|
|
1618
|
+
* → word loop on uint64_t + tail byte fallback
|
|
1619
|
+
* slow: anything else (mask present, or non-contig view, etc.)
|
|
1620
|
+
* → duplicate of the original generated byte loop
|
|
1621
|
+
*
|
|
1622
|
+
* The slow path is bit-identical to the generated function's body so
|
|
1623
|
+
* hooking is transparent — no semantic change, only fast-path addition.
|
|
1624
|
+
* ============================================================================ */
|
|
1625
|
+
|
|
1626
|
+
/* Generated tables to be patched (defined non-static in carray_math.c). */
|
|
1627
|
+
extern ca_binop_func_t ca_binop_bit_and_i[];
|
|
1628
|
+
extern ca_binop_func_t ca_binop_bit_or_i[];
|
|
1629
|
+
extern ca_binop_func_t ca_binop_bit_xor_i[];
|
|
1630
|
+
extern ca_monop_func_t ca_monop_bit_neg[];
|
|
1631
|
+
/* Note: Ruby `~` is registered as an alias of `bit_neg` (carray_math.c:
|
|
1632
|
+
rb_define_alias(rb_cCArray, "~", "bit_neg")); the table is
|
|
1633
|
+
ca_monop_bit_neg. */
|
|
1634
|
+
|
|
1635
|
+
/* ----- bit_and_i (boolean8_t fast) ----- */
|
|
1636
|
+
static void
|
|
1637
|
+
ca_binop_bit_and_i_boolean8_fast (ca_size_t n, boolean8_t *m,
|
|
1638
|
+
char *ptr1, ca_size_t i1,
|
|
1639
|
+
char *ptr2, ca_size_t i2,
|
|
1640
|
+
char *ptr3, ca_size_t i3)
|
|
1641
|
+
{
|
|
1642
|
+
boolean8_t *q1 = (boolean8_t *) ptr1;
|
|
1643
|
+
boolean8_t *q2 = (boolean8_t *) ptr2;
|
|
1644
|
+
boolean8_t *q3 = (boolean8_t *) ptr3;
|
|
1645
|
+
boolean8_t *p1, *p2, *p3;
|
|
1646
|
+
ca_size_t k;
|
|
1647
|
+
|
|
1648
|
+
if ( m ) {
|
|
1649
|
+
/* mask path: existing byte loop (skip masked positions) */
|
|
1650
|
+
boolean8_t *pm;
|
|
1651
|
+
for (k = 0; k < n; k++) {
|
|
1652
|
+
pm = m + k;
|
|
1653
|
+
if ( ! *pm ) {
|
|
1654
|
+
p1 = q1 + k*i1;
|
|
1655
|
+
p2 = q2 + k*i2;
|
|
1656
|
+
p3 = q3 + k*i3;
|
|
1657
|
+
(*p3) = (*p1) & (*p2);
|
|
1658
|
+
}
|
|
1659
|
+
}
|
|
1660
|
+
}
|
|
1661
|
+
else if ( i1 == 1 && i2 == 1 && i3 == 1 ) {
|
|
1662
|
+
/* fast path: word-bulk AND */
|
|
1663
|
+
ca_mask_word_and(q3, q1, q2, n);
|
|
1664
|
+
}
|
|
1665
|
+
else {
|
|
1666
|
+
/* non-contig view: existing byte loop */
|
|
1667
|
+
for (k = 0; k < n; k++) {
|
|
1668
|
+
p1 = q1 + k*i1;
|
|
1669
|
+
p2 = q2 + k*i2;
|
|
1670
|
+
p3 = q3 + k*i3;
|
|
1671
|
+
(*p3) = (*p1) & (*p2);
|
|
1672
|
+
}
|
|
1673
|
+
}
|
|
1674
|
+
}
|
|
1675
|
+
|
|
1676
|
+
/* ----- bit_or_i (boolean8_t fast) ----- */
|
|
1677
|
+
static void
|
|
1678
|
+
ca_binop_bit_or_i_boolean8_fast (ca_size_t n, boolean8_t *m,
|
|
1679
|
+
char *ptr1, ca_size_t i1,
|
|
1680
|
+
char *ptr2, ca_size_t i2,
|
|
1681
|
+
char *ptr3, ca_size_t i3)
|
|
1682
|
+
{
|
|
1683
|
+
boolean8_t *q1 = (boolean8_t *) ptr1;
|
|
1684
|
+
boolean8_t *q2 = (boolean8_t *) ptr2;
|
|
1685
|
+
boolean8_t *q3 = (boolean8_t *) ptr3;
|
|
1686
|
+
boolean8_t *p1, *p2, *p3;
|
|
1687
|
+
ca_size_t k;
|
|
1688
|
+
|
|
1689
|
+
if ( m ) {
|
|
1690
|
+
boolean8_t *pm;
|
|
1691
|
+
for (k = 0; k < n; k++) {
|
|
1692
|
+
pm = m + k;
|
|
1693
|
+
if ( ! *pm ) {
|
|
1694
|
+
p1 = q1 + k*i1;
|
|
1695
|
+
p2 = q2 + k*i2;
|
|
1696
|
+
p3 = q3 + k*i3;
|
|
1697
|
+
(*p3) = (*p1) | (*p2);
|
|
1698
|
+
}
|
|
1699
|
+
}
|
|
1700
|
+
}
|
|
1701
|
+
else if ( i1 == 1 && i2 == 1 && i3 == 1 ) {
|
|
1702
|
+
/* fast path: inline 3-way word OR (ca_mask_word_or is 2-arg
|
|
1703
|
+
accumulating; here we need 3-arg). */
|
|
1704
|
+
ca_size_t nw = n / CA_MASK_WORD_BYTES;
|
|
1705
|
+
ca_size_t j;
|
|
1706
|
+
uint64_t *wd = (uint64_t *) q3;
|
|
1707
|
+
const uint64_t *wa = (const uint64_t *) q1;
|
|
1708
|
+
const uint64_t *wb = (const uint64_t *) q2;
|
|
1709
|
+
for (j = 0; j < nw; j++) {
|
|
1710
|
+
wd[j] = wa[j] | wb[j];
|
|
1711
|
+
}
|
|
1712
|
+
for (j = nw * CA_MASK_WORD_BYTES; j < n; j++) {
|
|
1713
|
+
q3[j] = q1[j] | q2[j];
|
|
1714
|
+
}
|
|
1715
|
+
}
|
|
1716
|
+
else {
|
|
1717
|
+
for (k = 0; k < n; k++) {
|
|
1718
|
+
p1 = q1 + k*i1;
|
|
1719
|
+
p2 = q2 + k*i2;
|
|
1720
|
+
p3 = q3 + k*i3;
|
|
1721
|
+
(*p3) = (*p1) | (*p2);
|
|
1722
|
+
}
|
|
1723
|
+
}
|
|
1724
|
+
}
|
|
1725
|
+
|
|
1726
|
+
/* ----- bit_xor_i (boolean8_t fast) -----
|
|
1727
|
+
Original generated form for boolean8_t uses `(*p3) = ((*p1) != (*p2)) ? 1 : 0`
|
|
1728
|
+
(logical XOR for {0,1}). Word XOR is equivalent under invariant {0,1}:
|
|
1729
|
+
bytewise XOR of {0,1} stays in {0,1}. */
|
|
1730
|
+
static void
|
|
1731
|
+
ca_binop_bit_xor_i_boolean8_fast (ca_size_t n, boolean8_t *m,
|
|
1732
|
+
char *ptr1, ca_size_t i1,
|
|
1733
|
+
char *ptr2, ca_size_t i2,
|
|
1734
|
+
char *ptr3, ca_size_t i3)
|
|
1735
|
+
{
|
|
1736
|
+
boolean8_t *q1 = (boolean8_t *) ptr1;
|
|
1737
|
+
boolean8_t *q2 = (boolean8_t *) ptr2;
|
|
1738
|
+
boolean8_t *q3 = (boolean8_t *) ptr3;
|
|
1739
|
+
boolean8_t *p1, *p2, *p3;
|
|
1740
|
+
ca_size_t k;
|
|
1741
|
+
|
|
1742
|
+
if ( m ) {
|
|
1743
|
+
boolean8_t *pm;
|
|
1744
|
+
for (k = 0; k < n; k++) {
|
|
1745
|
+
pm = m + k;
|
|
1746
|
+
if ( ! *pm ) {
|
|
1747
|
+
p1 = q1 + k*i1;
|
|
1748
|
+
p2 = q2 + k*i2;
|
|
1749
|
+
p3 = q3 + k*i3;
|
|
1750
|
+
(*p3) = ((*p1) != (*p2)) ? 1 : 0;
|
|
1751
|
+
}
|
|
1752
|
+
}
|
|
1753
|
+
}
|
|
1754
|
+
else if ( i1 == 1 && i2 == 1 && i3 == 1 ) {
|
|
1755
|
+
/* fast path: word XOR */
|
|
1756
|
+
ca_mask_word_xor(q3, q1, q2, n);
|
|
1757
|
+
}
|
|
1758
|
+
else {
|
|
1759
|
+
for (k = 0; k < n; k++) {
|
|
1760
|
+
p1 = q1 + k*i1;
|
|
1761
|
+
p2 = q2 + k*i2;
|
|
1762
|
+
p3 = q3 + k*i3;
|
|
1763
|
+
(*p3) = ((*p1) != (*p2)) ? 1 : 0;
|
|
1764
|
+
}
|
|
1765
|
+
}
|
|
1766
|
+
}
|
|
1767
|
+
|
|
1768
|
+
/* ----- bit_neg (= Ruby `~`, monop) (boolean8_t fast) -----
|
|
1769
|
+
The generated boolean8_t variant uses `(*p2) = (*p1) ? 0 : 1` (logical
|
|
1770
|
+
inversion via ternary), NOT bitwise `~`. No caller expects byte `~`
|
|
1771
|
+
semantics for a boolean array, and the LSB XOR pattern
|
|
1772
|
+
(= ca_mask_word_not) is fully equivalent while preserving {0,1}. */
|
|
1773
|
+
static void
|
|
1774
|
+
ca_monop_bit_neg_boolean8_fast (ca_size_t n, boolean8_t *m,
|
|
1775
|
+
char *ptr1, ca_size_t i1,
|
|
1776
|
+
char *ptr2, ca_size_t i2)
|
|
1777
|
+
{
|
|
1778
|
+
boolean8_t *q1 = (boolean8_t *) ptr1;
|
|
1779
|
+
boolean8_t *q2 = (boolean8_t *) ptr2;
|
|
1780
|
+
boolean8_t *p1, *p2;
|
|
1781
|
+
ca_size_t k;
|
|
1782
|
+
|
|
1783
|
+
if ( m ) {
|
|
1784
|
+
boolean8_t *pm;
|
|
1785
|
+
for (k = 0; k < n; k++) {
|
|
1786
|
+
pm = m + k;
|
|
1787
|
+
if ( ! *pm ) {
|
|
1788
|
+
p1 = q1 + k*i1;
|
|
1789
|
+
p2 = q2 + k*i2;
|
|
1790
|
+
(*p2) = (*p1) ? 0 : 1;
|
|
1791
|
+
}
|
|
1792
|
+
}
|
|
1793
|
+
}
|
|
1794
|
+
else if ( i1 == 1 && i2 == 1 ) {
|
|
1795
|
+
/* fast path: word NOT (LSB XOR) */
|
|
1796
|
+
ca_mask_word_not(q2, q1, n);
|
|
1797
|
+
}
|
|
1798
|
+
else {
|
|
1799
|
+
for (k = 0; k < n; k++) {
|
|
1800
|
+
p1 = q1 + k*i1;
|
|
1801
|
+
p2 = q2 + k*i2;
|
|
1802
|
+
(*p2) = (*p1) ? 0 : 1;
|
|
1803
|
+
}
|
|
1804
|
+
}
|
|
1805
|
+
}
|
|
1806
|
+
|
|
1807
|
+
/* Init_carray_mask_fast_paths — patch generated dispatch tables.
|
|
1808
|
+
Must be called AFTER Init_carray_math (table is set up there).
|
|
1809
|
+
Wired in ruby_carray.c. */
|
|
1139
1810
|
void
|
|
1140
|
-
|
|
1811
|
+
Init_carray_mask_fast_paths (void)
|
|
1812
|
+
{
|
|
1813
|
+
ca_binop_bit_and_i[CA_BOOLEAN] = ca_binop_bit_and_i_boolean8_fast;
|
|
1814
|
+
ca_binop_bit_or_i [CA_BOOLEAN] = ca_binop_bit_or_i_boolean8_fast;
|
|
1815
|
+
ca_binop_bit_xor_i[CA_BOOLEAN] = ca_binop_bit_xor_i_boolean8_fast;
|
|
1816
|
+
ca_monop_bit_neg [CA_BOOLEAN] = ca_monop_bit_neg_boolean8_fast;
|
|
1817
|
+
}
|
|
1818
|
+
|
|
1819
|
+
|
|
1820
|
+
void
|
|
1821
|
+
Init_carray_mask (void)
|
|
1141
1822
|
{
|
|
1142
1823
|
rb_define_private_method(rb_cCArray, "__create_mask__", rb_ca_create_mask, 0);
|
|
1143
1824
|
/*
|
|
@@ -1154,16 +1835,29 @@ Init_carray_mask ()
|
|
|
1154
1835
|
rb_define_method(rb_cCArray, "is_masked", rb_ca_is_masked, 0);
|
|
1155
1836
|
rb_define_method(rb_cCArray, "is_not_masked", rb_ca_is_not_masked, 0);
|
|
1156
1837
|
rb_define_method(rb_cCArray, "unmask", rb_ca_unmask_method, -1);
|
|
1157
|
-
|
|
1838
|
+
|
|
1839
|
+
/* unmask_copy is not offered (removed in 3.0), no alias. Migration:
|
|
1840
|
+
ca.unmask_copy() -> ca.value.to_ca (mask structure dissociated)
|
|
1841
|
+
ca.unmask_copy(fill) -> ca.strip_mask(fill)
|
|
1842
|
+
The internal C function ca_unmask_copy / rb_ca_mask_fill_copy stays
|
|
1843
|
+
since strip_mask wraps it. */
|
|
1844
|
+
|
|
1845
|
+
/* strip_mask(fill) is the return form of "produce a mask-free copy
|
|
1846
|
+
filled with fill at previously-masked positions". Fill is mandatory
|
|
1847
|
+
(arity 1, no default), matching the "all arguments mandatory"
|
|
1848
|
+
regularity of the mask SET family. */
|
|
1849
|
+
rb_define_method(rb_cCArray, "strip_mask", rb_ca_mask_fill_copy, 1);
|
|
1158
1850
|
rb_define_method(rb_cCArray, "invert_mask", rb_ca_invert_mask, 0);
|
|
1851
|
+
rb_define_singleton_method(rb_cCArray, "guard_undef", rb_ca_s_guard_undef, -1);
|
|
1852
|
+
rb_define_method(rb_cCArray, "mask_eq", rb_ca_mask_eq, 1);
|
|
1853
|
+
rb_define_method(rb_cCArray, "mask_invalid", rb_ca_mask_invalid, 0);
|
|
1854
|
+
rb_define_method(rb_cCArray, "mask_where", rb_ca_mask_where, -1);
|
|
1159
1855
|
|
|
1160
1856
|
rb_define_method(rb_cCArray, "inherit_mask", rb_ca_inherit_mask_method, -1);
|
|
1161
|
-
rb_define_method(rb_cCArray, "inherit_mask_replace",
|
|
1857
|
+
rb_define_method(rb_cCArray, "inherit_mask_replace",
|
|
1162
1858
|
rb_ca_inherit_mask_replace_method, -1);
|
|
1163
1859
|
|
|
1164
|
-
|
|
1165
|
-
|
|
1166
|
-
/* def count_not_masked (*axis); end */
|
|
1167
|
-
|
|
1860
|
+
rb_define_method(rb_cCArray, "count_masked", rb_ca_count_masked, -1);
|
|
1861
|
+
rb_define_method(rb_cCArray, "count_not_masked", rb_ca_count_not_masked, -1);
|
|
1168
1862
|
}
|
|
1169
1863
|
|