carray 2.0.1 → 3.0.1
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 +6 -25
- data/CHANGELOG.md +338 -0
- data/{NEWS.md → CHANGELOG.v1.md} +3 -0
- data/LICENSE +1 -1
- data/README.md +120 -36
- data/carray.gemspec +32 -30
- 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 +1244 -0
- data/ext/ca_bincmp_dispatch.c +76 -0
- data/ext/ca_bincmp_dispatch.h +85 -0
- data/ext/ca_binop_dispatch.c +124 -0
- data/ext/ca_binop_dispatch.h +152 -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 +239 -0
- data/ext/ca_group_iter.c +304 -0
- data/ext/ca_iter_substrate.h +325 -0
- data/ext/ca_kernel_iterator.c +4367 -0
- data/ext/ca_kernel_iterator.h +2596 -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 +839 -0
- data/ext/ca_obj_binop.c +948 -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 +967 -0
- data/ext/ca_obj_face.c +750 -0
- data/ext/ca_obj_face.h +279 -0
- data/ext/ca_obj_fake.c +239 -100
- data/ext/ca_obj_farray.c +54 -441
- data/ext/ca_obj_field.c +82 -529
- data/ext/ca_obj_fixlen_string.c +308 -0
- data/ext/ca_obj_grid.c +866 -440
- data/ext/ca_obj_meld.c +1039 -0
- data/ext/ca_obj_moncmp.c +588 -0
- data/ext/ca_obj_monop.c +1123 -0
- data/ext/ca_obj_object.c +866 -296
- data/ext/ca_obj_record.c +470 -0
- data/ext/ca_obj_reduce.c +97 -82
- data/ext/ca_obj_refer.c +593 -459
- data/ext/ca_obj_remap.c +475 -0
- data/ext/ca_obj_repeat.c +92 -477
- data/ext/ca_obj_roll.c +624 -0
- data/ext/ca_obj_select.c +344 -296
- data/ext/ca_obj_select_axis.c +1306 -0
- data/ext/ca_obj_shift.c +231 -793
- data/ext/ca_obj_source.c +78 -0
- data/ext/ca_obj_stack.c +1173 -0
- data/ext/ca_obj_stride.c +2584 -0
- data/ext/ca_obj_string.c +270 -0
- data/ext/ca_obj_tile.c +622 -0
- data/ext/ca_obj_time.c +548 -0
- data/ext/ca_obj_timedelta.c +437 -0
- data/ext/ca_obj_transpose.c +62 -516
- data/ext/ca_obj_triop.c +785 -0
- data/ext/ca_obj_window.c +1202 -565
- data/ext/ca_op_byte_swap.c +175 -0
- data/ext/ca_op_cmplx64.h +123 -0
- data/ext/ca_op_ipower.c +316 -0
- data/ext/ca_op_powi.h +88 -0
- data/ext/ca_sort_kernels.h +132 -0
- data/ext/ca_sweep_engine.c +473 -0
- data/ext/ca_sweep_engine.h +166 -0
- data/ext/ca_transform_common.c +235 -0
- data/ext/ca_triop_dispatch.c +55 -0
- data/ext/ca_triop_dispatch.h +62 -0
- data/ext/carray.h +810 -420
- data/ext/carray_access.c +873 -731
- data/ext/carray_attribute.c +98 -329
- data/ext/carray_bincount.c +255 -0
- data/ext/carray_broadcast.c +376 -0
- data/ext/carray_build_flags.h +3 -0
- data/ext/carray_call_cfunc.c +2897 -874
- data/ext/carray_call_cfunc.h +313 -0
- data/ext/carray_cast.c +1264 -315
- 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 +168 -270
- data/ext/carray_core.c +1396 -206
- 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 +1021 -0
- data/ext/carray_index_classifier.h +27 -0
- data/ext/carray_internal.h +136 -0
- data/ext/carray_kernels_bincmp.c +4446 -0
- data/ext/carray_kernels_binop.c +11001 -0
- data/ext/carray_kernels_init.c +1131 -0
- data/ext/carray_kernels_map.c +3467 -0
- data/ext/carray_kernels_moncmp.c +2097 -0
- data/ext/carray_kernels_monop.c +18313 -0
- data/ext/carray_kernels_reduce_aggregate.c +25837 -0
- data/ext/carray_kernels_reduce_boolean.c +330 -0
- data/ext/carray_kernels_reduce_cumulative.c +14593 -0
- data/ext/carray_kernels_reduce_extreme.c +16948 -0
- data/ext/carray_kernels_reduce_variance.c +3910 -0
- data/ext/carray_kernels_scan.c +3693 -0
- data/ext/carray_kernels_search.c +32138 -0
- data/ext/carray_kernels_sort.c +10626 -0
- data/ext/carray_kernels_triop.c +1392 -0
- data/ext/carray_lazy.c +737 -0
- data/ext/carray_loop.c +88 -200
- data/ext/carray_mask.c +853 -158
- 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 +1650 -0
- data/ext/carray_operator.c +1525 -320
- 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 +234 -55
- data/ext/mk_call_cfunc.rb +671 -0
- data/ext/mkkernel.rb +9096 -0
- data/ext/ruby_carray.c +211 -108
- 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 +239 -0
- data/lib/carray/autoload_method_extension.rb +45 -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 +614 -0
- data/lib/carray/boolean_reduce.rb +109 -0
- data/lib/carray/categorical.rb +561 -0
- data/lib/carray/categorical_iterator.rb +1084 -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 +160 -328
- data/lib/carray/core_extensions.rb +297 -0
- data/lib/carray/data_type_extension.rb +250 -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 +642 -0
- data/lib/carray/frame/group.rb +186 -0
- data/lib/carray/frame/io.rb +321 -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 +316 -0
- data/lib/carray/frame.rb +16 -0
- data/lib/carray/fuse_source.rb +123 -0
- data/lib/carray/fusion.rb +218 -0
- data/lib/carray/histogram.rb +512 -0
- data/lib/carray/inspect.rb +37 -26
- data/lib/carray/iterator.rb +58 -349
- data/lib/carray/lazy.rb +941 -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 +52 -0
- data/lib/carray/methods/mask_duplicates.rb +41 -0
- data/lib/carray/methods/meshgrid.rb +90 -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 +161 -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 +89 -0
- data/lib/carray/serialize.rb +478 -167
- data/lib/carray/slab_iterator.rb +305 -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 +2654 -38
- data/lib/carray/window_iterator.rb +927 -0
- data/lib/carray.rb +55 -57
- data/yard-stubs/ca_obj_array.rb +385 -0
- data/yard-stubs/ca_obj_bitarray.rb +38 -0
- data/yard-stubs/ca_obj_bitfield.rb +43 -0
- data/yard-stubs/ca_obj_block.rb +73 -0
- data/yard-stubs/ca_obj_byte_swap.rb +56 -0
- data/yard-stubs/ca_obj_fake.rb +31 -0
- data/yard-stubs/ca_obj_farray.rb +32 -0
- data/yard-stubs/ca_obj_field.rb +45 -0
- data/yard-stubs/ca_obj_grid.rb +35 -0
- data/yard-stubs/ca_obj_refer.rb +72 -0
- data/yard-stubs/ca_obj_roll.rb +45 -0
- data/yard-stubs/ca_obj_shift.rb +43 -0
- data/yard-stubs/ca_obj_stride.rb +181 -0
- data/yard-stubs/ca_obj_tile.rb +29 -0
- data/yard-stubs/ca_obj_transpose.rb +40 -0
- data/yard-stubs/ca_obj_window.rb +49 -0
- data/yard-stubs/carray_access.rb +131 -0
- data/yard-stubs/carray_attribute.rb +246 -0
- data/yard-stubs/carray_broadcast.rb +37 -0
- data/yard-stubs/carray_cast.rb +489 -0
- data/yard-stubs/carray_class.rb +65 -0
- data/yard-stubs/carray_conversion.rb +76 -0
- data/yard-stubs/carray_copy.rb +79 -0
- data/yard-stubs/carray_core.rb +114 -0
- data/yard-stubs/carray_count.rb +79 -0
- data/yard-stubs/carray_element.rb +108 -0
- data/yard-stubs/carray_generate.rb +66 -0
- data/yard-stubs/carray_lazy.rb +23 -0
- data/yard-stubs/carray_loop.rb +140 -0
- data/yard-stubs/carray_mask.rb +259 -0
- data/yard-stubs/carray_math.rb +132 -0
- data/yard-stubs/carray_mathfunc.rb +45 -0
- data/yard-stubs/carray_median_percentile.rb +89 -0
- data/yard-stubs/carray_memory_view.rb +163 -0
- data/yard-stubs/carray_order.rb +312 -0
- data/yard-stubs/carray_random.rb +89 -0
- data/yard-stubs/carray_scatter.rb +106 -0
- data/yard-stubs/carray_slab.rb +57 -0
- data/yard-stubs/carray_sort.rb +163 -0
- data/yard-stubs/carray_test.rb +85 -0
- data/yard-stubs/carray_undef.rb +64 -0
- data/yard-stubs/carray_utils.rb +97 -0
- data/yard-stubs/ruby_carray.rb +193 -0
- metadata +220 -138
- 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/ca_obj_unbound_repeat.c +0 -529
- 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/mailmap +0 -1
- 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/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_operator.c
CHANGED
|
@@ -1,16 +1,34 @@
|
|
|
1
1
|
/* ---------------------------------------------------------------------------
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
Copyright (C) 2005-2025 Hiroki Motoyoshi
|
|
3
|
+
Operator dispatch: the drivers behind CArray's arithmetic / comparison
|
|
4
|
+
operators (rb_ca_call_monop / _binop / _triop / _moncmp / _bincmp),
|
|
5
|
+
plus the chunked-gather and safe-mask-overlay helpers that let the eager
|
|
6
|
+
slow path materialise operands without attaching attach-hostile views.
|
|
8
7
|
|
|
9
8
|
---------------------------------------------------------------------------- */
|
|
10
9
|
|
|
11
10
|
#include <math.h>
|
|
11
|
+
#include <stdarg.h>
|
|
12
12
|
|
|
13
13
|
#include "carray.h"
|
|
14
|
+
#include "carray_internal.h" /* ca_lazy_arena_* */
|
|
15
|
+
|
|
16
|
+
/* Operand scratch for a chunked / gathered kernel run.
|
|
17
|
+
|
|
18
|
+
For CA_OBJECT the scratch holds VALUEs, and an object-lane kernel
|
|
19
|
+
calls rb_funcall per cell -- a collection in the middle would free
|
|
20
|
+
whatever the gather pulled in from a lazy operand, since those cells
|
|
21
|
+
exist nowhere else. The object form of the arena acquire keeps the
|
|
22
|
+
slot marked until it is released. */
|
|
23
|
+
static void *
|
|
24
|
+
ca_op_acquire_operand_scratch (CArray *ca, ca_size_t n_elements)
|
|
25
|
+
{
|
|
26
|
+
return ( ca->data_type == CA_OBJECT )
|
|
27
|
+
? ca_lazy_arena_acquire_object(n_elements)
|
|
28
|
+
: ca_lazy_arena_acquire(n_elements * ca->bytes);
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
#include "ca_obj_face.h" /* ca_face_reconcile_comparison (comparison Face gate) */
|
|
14
32
|
|
|
15
33
|
VALUE rb_mCAMath;
|
|
16
34
|
|
|
@@ -18,14 +36,183 @@ extern ca_binop_func_t ca_binop_mul[CA_NTYPE];
|
|
|
18
36
|
extern ca_binop_func_t ca_binop_add[CA_NTYPE];
|
|
19
37
|
|
|
20
38
|
void
|
|
21
|
-
ca_zerodiv ()
|
|
39
|
+
ca_zerodiv (void)
|
|
22
40
|
{
|
|
23
|
-
#ifdef _OPENMP
|
|
24
|
-
#pragma omp master
|
|
25
|
-
#endif
|
|
26
41
|
rb_raise(rb_eZeroDivError, "divided by 0");
|
|
27
42
|
}
|
|
28
43
|
|
|
44
|
+
/* Chunked gather helpers for the eager slow path. Instead of an ALLOCV
|
|
45
|
+
full-size materialise, gather per-region via xfer_stride into arena
|
|
46
|
+
scratch, dropping the memory peak from O(operand_size) to O(chunk).
|
|
47
|
+
|
|
48
|
+
Policy:
|
|
49
|
+
- target chunk = 4096 elements (~32KB at f64), L1d-friendly
|
|
50
|
+
- N-D shape: outer-axis chunking, row-aligned
|
|
51
|
+
- mask handling: via the ca_copy_mask_overlay path
|
|
52
|
+
|
|
53
|
+
These three helpers (ca_chunk_inner_size / ca_chunk_compute_n /
|
|
54
|
+
ca_chunked_gather) are extern so ca_sweep_engine.c can reuse the same
|
|
55
|
+
chunk policy + region-gather mechanism for the sweep ELEMENT macro
|
|
56
|
+
family without duplicating the implementation. */
|
|
57
|
+
|
|
58
|
+
#define CA_CHUNK_TARGET_ELEMENTS 4096
|
|
59
|
+
|
|
60
|
+
ca_size_t
|
|
61
|
+
ca_chunk_inner_size (CArray *ca)
|
|
62
|
+
{
|
|
63
|
+
/* product of all dims except outermost; 1 for ndim <= 1 / scalar */
|
|
64
|
+
ca_size_t inner = 1;
|
|
65
|
+
int8_t k;
|
|
66
|
+
if (ca->ndim <= 1) return 1;
|
|
67
|
+
for (k = ca->ndim - 1; k >= 1; k--) inner *= ca->dim[k];
|
|
68
|
+
return inner;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
ca_size_t
|
|
72
|
+
ca_chunk_compute_n (ca_size_t total, ca_size_t inner, ca_size_t bytes_per_cell)
|
|
73
|
+
{
|
|
74
|
+
/* Compute chunk_n in elements aligned to outer axis: a multiple of
|
|
75
|
+
`inner` (or = inner if target < inner = 1 row/chunk fallback).
|
|
76
|
+
Result is clamped to `total`. */
|
|
77
|
+
ca_size_t target_bytes = CA_CHUNK_TARGET_ELEMENTS * 8; /* ~32KB at f64 */
|
|
78
|
+
ca_size_t target_n = target_bytes / (bytes_per_cell > 0 ? bytes_per_cell : 1);
|
|
79
|
+
ca_size_t chunk_n;
|
|
80
|
+
if (target_n < 1) target_n = 1;
|
|
81
|
+
if (inner <= 0) inner = 1;
|
|
82
|
+
if (target_n >= inner) {
|
|
83
|
+
chunk_n = (target_n / inner) * inner; /* round down to inner multiple */
|
|
84
|
+
} else {
|
|
85
|
+
chunk_n = inner; /* 1 row/chunk fallback */
|
|
86
|
+
}
|
|
87
|
+
if (chunk_n > total) chunk_n = total;
|
|
88
|
+
if (chunk_n < 1) chunk_n = 1;
|
|
89
|
+
return chunk_n;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
/* Gather `n` elements starting at flat-offset `off` from `ca` into `dest`
|
|
93
|
+
(contig native byte layout of the chunked region). Requires:
|
|
94
|
+
- `off` and `n` are multiples of inner = Π_{k>=1} ca->dim[k]
|
|
95
|
+
- `n` represents `outer_rows * inner` consecutive flat-addr cells
|
|
96
|
+
For ndim <= 1: simple linear region. */
|
|
97
|
+
void
|
|
98
|
+
ca_chunked_gather (CArray *ca, ca_size_t off, ca_size_t n, void *dest)
|
|
99
|
+
{
|
|
100
|
+
ca_size_t starts[CA_RANK_MAX];
|
|
101
|
+
ca_size_t counts[CA_RANK_MAX];
|
|
102
|
+
ca_size_t strides[CA_RANK_MAX];
|
|
103
|
+
int8_t k;
|
|
104
|
+
ca_size_t inner, bytes, s;
|
|
105
|
+
|
|
106
|
+
bytes = ca->bytes;
|
|
107
|
+
|
|
108
|
+
if (ca->ndim <= 1) {
|
|
109
|
+
/* 1-D (or scalar reified to 1-D via elements): single axis chunk */
|
|
110
|
+
starts[0] = (ca->ndim == 0) ? 0 : off;
|
|
111
|
+
counts[0] = (ca->ndim == 0) ? 1 : n;
|
|
112
|
+
strides[0] = bytes;
|
|
113
|
+
ca_xfer_stride(ca, starts, counts, strides, dest, CA_XFER_GET);
|
|
114
|
+
return;
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
inner = 1;
|
|
118
|
+
for (k = ca->ndim - 1; k >= 1; k--) inner *= ca->dim[k];
|
|
119
|
+
|
|
120
|
+
starts[0] = (inner > 0) ? off / inner : 0;
|
|
121
|
+
counts[0] = (inner > 0) ? n / inner : 0;
|
|
122
|
+
for (k = 1; k < ca->ndim; k++) {
|
|
123
|
+
starts[k] = 0;
|
|
124
|
+
counts[k] = ca->dim[k];
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
/* native contig strides for the chunked shape */
|
|
128
|
+
s = bytes;
|
|
129
|
+
for (k = ca->ndim - 1; k >= 0; k--) {
|
|
130
|
+
strides[k] = s;
|
|
131
|
+
s *= counts[k];
|
|
132
|
+
}
|
|
133
|
+
ca_xfer_stride(ca, starts, counts, strides, dest, CA_XFER_GET);
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
/* Operand mask overlay without calling ca_attach on the operand masks.
|
|
137
|
+
|
|
138
|
+
`ca_copy_mask_overlay` (carray_mask.c) attaches each operand mask via
|
|
139
|
+
ca_attach(cs->mask) before OR-folding into the output mask. For
|
|
140
|
+
attach-hostile operand masks (= mock fixture, CAStack expansion,
|
|
141
|
+
per-region xfer-only views) that raises.
|
|
142
|
+
|
|
143
|
+
This helper instead materialises each operand mask via ca_xfer_all into
|
|
144
|
+
a transient arena scratch and OR-folds byte-wise into ca_out->mask.
|
|
145
|
+
Memory peak: one mask scratch at a time (= elements bytes, 1 B/cell),
|
|
146
|
+
released between operands.
|
|
147
|
+
|
|
148
|
+
CAREFUL: `ca_out` must be a freshly templated entity (driver allocates
|
|
149
|
+
via ca_template_safe), so ca_out->mask->ptr is directly writable
|
|
150
|
+
without attach.
|
|
151
|
+
|
|
152
|
+
This gathers the full mask once; a per-chunk mask gather inside the
|
|
153
|
+
chunked branch is a possible future optimisation. */
|
|
154
|
+
void
|
|
155
|
+
ca_mask_overlay_safe (CArray *ca_out, int n, ...)
|
|
156
|
+
{
|
|
157
|
+
va_list ap;
|
|
158
|
+
CArray *slist[8];
|
|
159
|
+
int i, any_mask = 0;
|
|
160
|
+
|
|
161
|
+
if ( n < 0 || n > 8 ) {
|
|
162
|
+
rb_raise(rb_eRuntimeError, "ca_mask_overlay_safe: n out of range");
|
|
163
|
+
}
|
|
164
|
+
va_start(ap, n);
|
|
165
|
+
for ( i = 0; i < n; i++ ) {
|
|
166
|
+
slist[i] = va_arg(ap, CArray *);
|
|
167
|
+
if ( slist[i] && ca_has_mask(slist[i]) ) any_mask = 1;
|
|
168
|
+
}
|
|
169
|
+
va_end(ap);
|
|
170
|
+
|
|
171
|
+
if ( ! any_mask ) return;
|
|
172
|
+
|
|
173
|
+
ca_update_mask(ca_out);
|
|
174
|
+
{
|
|
175
|
+
int created_new = 0;
|
|
176
|
+
if ( ! ca_out->mask ) {
|
|
177
|
+
ca_create_mask(ca_out);
|
|
178
|
+
created_new = 1;
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
boolean8_t *ma = (boolean8_t *) ca_out->mask->ptr;
|
|
182
|
+
ca_size_t elements = ca_out->elements;
|
|
183
|
+
ca_size_t j;
|
|
184
|
+
|
|
185
|
+
/* Fresh mask → zero-init. Existing mask (= bang variant where
|
|
186
|
+
ca_out IS one of the operands) → preserve as initial accumulator
|
|
187
|
+
(= ca_out's own contribution is already in `ma`, OR in others).
|
|
188
|
+
This is structurally equivalent to ca_copy_mask_overlay_n's
|
|
189
|
+
behavior which OR'd into existing mask without clearing. */
|
|
190
|
+
if ( created_new ) memset(ma, 0, elements);
|
|
191
|
+
|
|
192
|
+
for ( i = 0; i < n; i++ ) {
|
|
193
|
+
CArray *cs = slist[i];
|
|
194
|
+
if ( ! cs ) continue;
|
|
195
|
+
ca_update_mask(cs);
|
|
196
|
+
if ( ! cs->mask ) continue;
|
|
197
|
+
|
|
198
|
+
if ( ca_is_scalar(cs) ) {
|
|
199
|
+
boolean8_t bit = 0;
|
|
200
|
+
ca_xfer_all(cs->mask, &bit, CA_XFER_GET);
|
|
201
|
+
if ( bit ) memset(ma, 1, elements);
|
|
202
|
+
} else {
|
|
203
|
+
void *scratch = ca_lazy_arena_acquire(elements);
|
|
204
|
+
boolean8_t *ms = (boolean8_t *) scratch;
|
|
205
|
+
ca_xfer_all(cs->mask, scratch, CA_XFER_GET);
|
|
206
|
+
for ( j = 0; j < elements; j++ ) ma[j] |= ms[j];
|
|
207
|
+
ca_lazy_arena_release(scratch);
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
/* Monop driver. ca1 is input-only (the driver does not attach it); ca2
|
|
214
|
+
is the output (a new entity, attach legit). fast = ca_attach_is_alias(ca1)
|
|
215
|
+
→ 1-shot; slow = ALLOCV + ca_xfer_all without ca_func[X].attach. */
|
|
29
216
|
VALUE
|
|
30
217
|
rb_ca_call_monop (VALUE self, ca_monop_func_t func[])
|
|
31
218
|
{
|
|
@@ -34,28 +221,123 @@ rb_ca_call_monop (VALUE self, ca_monop_func_t func[])
|
|
|
34
221
|
|
|
35
222
|
TypedData_Get_Struct(self, CArray, &carray_data_type, ca1);
|
|
36
223
|
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
224
|
+
/* Boolean-as-numeric promotion: an arithmetic monop (-@ etc.) has no
|
|
225
|
+
boolean kernel (func[CA_BOOLEAN] == ca_monop_not_implement), so coerce
|
|
226
|
+
a bool input to CA_INT64 -- `-b` yields [-1, 0, ...] as its 0/1 numeric
|
|
227
|
+
storage. Monops that DO define a boolean kernel are left as bool. */
|
|
228
|
+
if ( ca1->data_type == CA_BOOLEAN && func[CA_BOOLEAN] == ca_monop_not_implement ) {
|
|
229
|
+
self = rb_ca_wrap_readonly(self, INT2NUM(CA_INT64));
|
|
230
|
+
TypedData_Get_Struct(self, CArray, &carray_data_type, ca1);
|
|
42
231
|
}
|
|
43
232
|
|
|
44
|
-
ca2 = ca_template(ca1);
|
|
233
|
+
ca2 = ca_has_mask(ca1) ? ca_template_safe(ca1) : ca_template(ca1);
|
|
45
234
|
out = ca_wrap_struct(ca2);
|
|
46
235
|
|
|
47
|
-
|
|
48
|
-
ca_copy_mask_overlay(ca2, ca2->elements, 1, ca1);
|
|
49
|
-
func[ca1->data_type](ca1->elements,
|
|
50
|
-
( ca2->mask ) ? (boolean8_t *)ca2->mask->ptr : NULL,
|
|
51
|
-
ca1->ptr, 1,
|
|
52
|
-
ca2->ptr, 1);
|
|
53
|
-
ca_detach(ca1);
|
|
236
|
+
ca_mask_overlay_safe(ca2, 1, ca1);
|
|
54
237
|
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
238
|
+
if ( ca_attach_is_alias(ca1) ) {
|
|
239
|
+
ca_attach(ca1);
|
|
240
|
+
func[ca1->data_type](ca1->elements,
|
|
241
|
+
( ca2->mask ) ? (boolean8_t *)ca2->mask->ptr : NULL,
|
|
242
|
+
ca1->ptr, 1,
|
|
243
|
+
ca2->ptr, 1);
|
|
244
|
+
ca_detach(ca1);
|
|
245
|
+
}
|
|
246
|
+
else {
|
|
247
|
+
/* Strided-walk fast path: when ca1 is a non-alias CAStride-family view
|
|
248
|
+
that composes to a ptr-bearing root, walk strides directly into the
|
|
249
|
+
kernel instead of staging via ca_xfer_all + a contig-kernel pass.
|
|
250
|
+
Saves 2x bandwidth (no gather buffer write+read). Output ca2 is a
|
|
251
|
+
contig entity; per-row output offset = row_idx * inner_count *
|
|
252
|
+
ca2->bytes. */
|
|
253
|
+
int strided_path_done = 0;
|
|
254
|
+
{
|
|
255
|
+
extern ca_operation_function_t ca_stride_func;
|
|
256
|
+
if ( ca_func[ca1->obj_type].attach == ca_stride_func.attach ) {
|
|
257
|
+
CAStride *cs1 = (CAStride *) ca1;
|
|
258
|
+
CArray *root1;
|
|
259
|
+
ca_size_t strides1[CA_RANK_MAX];
|
|
260
|
+
ca_size_t base1;
|
|
261
|
+
int8_t ndim = cs1->ndim;
|
|
262
|
+
int8_t k;
|
|
263
|
+
int ok = 1;
|
|
264
|
+
|
|
265
|
+
ca_stride_compose_to_root(cs1, &root1, strides1, &base1);
|
|
266
|
+
|
|
267
|
+
if ( !root1->ptr ) ok = 0;
|
|
268
|
+
|
|
269
|
+
/* element-stride conversion: strides must be byte-multiples of bytes */
|
|
270
|
+
if ( ok ) {
|
|
271
|
+
for ( k = 0; k < ndim; k++ ) {
|
|
272
|
+
if ( strides1[k] % ca1->bytes != 0 ) { ok = 0; break; }
|
|
273
|
+
}
|
|
274
|
+
}
|
|
275
|
+
if ( ok && (base1 % ca1->bytes != 0) ) ok = 0;
|
|
276
|
+
|
|
277
|
+
if ( ok ) {
|
|
278
|
+
int8_t inner_axis = ndim - 1;
|
|
279
|
+
ca_size_t inner_n = (ndim >= 1) ? cs1->dim[inner_axis] : ca1->elements;
|
|
280
|
+
ca_size_t s1_inner = (ndim >= 1) ? strides1[inner_axis] / ca1->bytes : 1;
|
|
281
|
+
ca_size_t idx[CA_RANK_MAX];
|
|
282
|
+
ca_size_t out_off = 0;
|
|
283
|
+
ca_size_t e1_strides[CA_RANK_MAX];
|
|
284
|
+
ca_size_t e1_base;
|
|
285
|
+
char *p1_root;
|
|
286
|
+
|
|
287
|
+
for ( k = 0; k < ndim; k++ ) {
|
|
288
|
+
e1_strides[k] = strides1[k] / ca1->bytes;
|
|
289
|
+
}
|
|
290
|
+
e1_base = base1 / ca1->bytes;
|
|
291
|
+
p1_root = (char *) root1->ptr;
|
|
292
|
+
|
|
293
|
+
for ( k = 0; k < ndim; k++ ) idx[k] = 0;
|
|
294
|
+
|
|
295
|
+
if ( ndim == 0 || ndim == 1 ) {
|
|
296
|
+
ca_size_t n_call = (ndim == 0) ? 1 : inner_n;
|
|
297
|
+
func[ca1->data_type](n_call,
|
|
298
|
+
( ca2->mask ) ? (boolean8_t *) ca2->mask->ptr : NULL,
|
|
299
|
+
p1_root + e1_base * ca1->bytes,
|
|
300
|
+
(ndim == 0) ? 0 : s1_inner,
|
|
301
|
+
ca2->ptr, 1);
|
|
302
|
+
}
|
|
303
|
+
else {
|
|
304
|
+
while ( 1 ) {
|
|
305
|
+
ca_size_t off1 = e1_base;
|
|
306
|
+
for ( k = 0; k < ndim - 1; k++ ) {
|
|
307
|
+
off1 += idx[k] * e1_strides[k];
|
|
308
|
+
}
|
|
309
|
+
func[ca1->data_type](inner_n,
|
|
310
|
+
ca2->mask
|
|
311
|
+
? ((boolean8_t *) ca2->mask->ptr) + out_off
|
|
312
|
+
: NULL,
|
|
313
|
+
p1_root + off1 * ca1->bytes, s1_inner,
|
|
314
|
+
(char *) ca2->ptr + out_off * ca2->bytes, 1);
|
|
315
|
+
out_off += inner_n;
|
|
316
|
+
k = ndim - 2;
|
|
317
|
+
while ( k >= 0 ) {
|
|
318
|
+
if ( ++idx[k] < cs1->dim[k] ) break;
|
|
319
|
+
idx[k] = 0; k--;
|
|
320
|
+
}
|
|
321
|
+
if ( k < 0 ) break;
|
|
322
|
+
}
|
|
323
|
+
}
|
|
324
|
+
strided_path_done = 1;
|
|
325
|
+
}
|
|
326
|
+
}
|
|
327
|
+
}
|
|
328
|
+
|
|
329
|
+
if ( !strided_path_done ) {
|
|
330
|
+
volatile VALUE h1 = Qnil;
|
|
331
|
+
char *p1;
|
|
332
|
+
(void) h1;
|
|
333
|
+
p1 = ALLOCV_N(char, h1, ca1->elements * ca1->bytes);
|
|
334
|
+
ca_xfer_all(ca1, p1, CA_XFER_GET);
|
|
335
|
+
func[ca1->data_type](ca1->elements,
|
|
336
|
+
( ca2->mask ) ? (boolean8_t *)ca2->mask->ptr : NULL,
|
|
337
|
+
p1, 1,
|
|
338
|
+
ca2->ptr, 1);
|
|
339
|
+
ALLOCV_END(h1);
|
|
340
|
+
}
|
|
59
341
|
}
|
|
60
342
|
|
|
61
343
|
return out;
|
|
@@ -81,6 +363,139 @@ rb_ca_call_monop_bang (VALUE self, ca_monop_func_t func[])
|
|
|
81
363
|
return self;
|
|
82
364
|
}
|
|
83
365
|
|
|
366
|
+
/* Dtype-changing monop dispatch. Allocates output of data_type
|
|
367
|
+
out_data_types[in_data_type]. When out_data_type == in_data_type this is identical
|
|
368
|
+
to rb_ca_call_monop; when they differ (e.g. abs on cmplx128 -> f64),
|
|
369
|
+
the output array has different cell size from input. No bang form
|
|
370
|
+
(= data_type change in-place is ill-defined; bang must preserve data_type). */
|
|
371
|
+
VALUE
|
|
372
|
+
rb_ca_call_monop_typed (VALUE self, ca_monop_func_t func[],
|
|
373
|
+
int8_t out_data_types[])
|
|
374
|
+
{
|
|
375
|
+
volatile VALUE out;
|
|
376
|
+
CArray *ca1, *ca2;
|
|
377
|
+
int8_t out_dt;
|
|
378
|
+
|
|
379
|
+
TypedData_Get_Struct(self, CArray, &carray_data_type, ca1);
|
|
380
|
+
|
|
381
|
+
out_dt = out_data_types[ca1->data_type];
|
|
382
|
+
if ( out_dt < 0 ) {
|
|
383
|
+
rb_raise(rb_eCADataTypeError,
|
|
384
|
+
"data_type-changing monop not implemented for input data_type %d",
|
|
385
|
+
ca1->data_type);
|
|
386
|
+
}
|
|
387
|
+
|
|
388
|
+
/* Allocate output with the per-op output data_type. Same shape as input. */
|
|
389
|
+
ca2 = carray_new(out_dt, ca1->ndim, ca1->dim, 0, NULL);
|
|
390
|
+
out = ca_wrap_struct(ca2);
|
|
391
|
+
|
|
392
|
+
/* Same fast/slow pattern as rb_ca_call_monop. ca1 input-only, ca2 output. */
|
|
393
|
+
ca_mask_overlay_safe(ca2, 1, ca1);
|
|
394
|
+
|
|
395
|
+
if ( ca_attach_is_alias(ca1) ) {
|
|
396
|
+
ca_attach(ca1);
|
|
397
|
+
func[ca1->data_type](ca1->elements,
|
|
398
|
+
( ca2->mask ) ? (boolean8_t *)ca2->mask->ptr : NULL,
|
|
399
|
+
ca1->ptr, 1,
|
|
400
|
+
ca2->ptr, 1);
|
|
401
|
+
ca_detach(ca1);
|
|
402
|
+
}
|
|
403
|
+
else {
|
|
404
|
+
/* Same strided-walk fast path as rb_ca_call_monop, but the output ca2
|
|
405
|
+
may have a different cell size (ca2->bytes may differ from
|
|
406
|
+
ca1->bytes, e.g. abs cmplx128 -> f64). */
|
|
407
|
+
int strided_path_done = 0;
|
|
408
|
+
{
|
|
409
|
+
extern ca_operation_function_t ca_stride_func;
|
|
410
|
+
if ( ca_func[ca1->obj_type].attach == ca_stride_func.attach ) {
|
|
411
|
+
CAStride *cs1 = (CAStride *) ca1;
|
|
412
|
+
CArray *root1;
|
|
413
|
+
ca_size_t strides1[CA_RANK_MAX];
|
|
414
|
+
ca_size_t base1;
|
|
415
|
+
int8_t ndim = cs1->ndim;
|
|
416
|
+
int8_t k;
|
|
417
|
+
int ok = 1;
|
|
418
|
+
|
|
419
|
+
ca_stride_compose_to_root(cs1, &root1, strides1, &base1);
|
|
420
|
+
|
|
421
|
+
if ( !root1->ptr ) ok = 0;
|
|
422
|
+
|
|
423
|
+
if ( ok ) {
|
|
424
|
+
for ( k = 0; k < ndim; k++ ) {
|
|
425
|
+
if ( strides1[k] % ca1->bytes != 0 ) { ok = 0; break; }
|
|
426
|
+
}
|
|
427
|
+
}
|
|
428
|
+
if ( ok && (base1 % ca1->bytes != 0) ) ok = 0;
|
|
429
|
+
|
|
430
|
+
if ( ok ) {
|
|
431
|
+
int8_t inner_axis = ndim - 1;
|
|
432
|
+
ca_size_t inner_n = (ndim >= 1) ? cs1->dim[inner_axis] : ca1->elements;
|
|
433
|
+
ca_size_t s1_inner = (ndim >= 1) ? strides1[inner_axis] / ca1->bytes : 1;
|
|
434
|
+
ca_size_t idx[CA_RANK_MAX];
|
|
435
|
+
ca_size_t out_off = 0;
|
|
436
|
+
ca_size_t e1_strides[CA_RANK_MAX];
|
|
437
|
+
ca_size_t e1_base;
|
|
438
|
+
char *p1_root;
|
|
439
|
+
|
|
440
|
+
for ( k = 0; k < ndim; k++ ) {
|
|
441
|
+
e1_strides[k] = strides1[k] / ca1->bytes;
|
|
442
|
+
}
|
|
443
|
+
e1_base = base1 / ca1->bytes;
|
|
444
|
+
p1_root = (char *) root1->ptr;
|
|
445
|
+
|
|
446
|
+
for ( k = 0; k < ndim; k++ ) idx[k] = 0;
|
|
447
|
+
|
|
448
|
+
if ( ndim == 0 || ndim == 1 ) {
|
|
449
|
+
ca_size_t n_call = (ndim == 0) ? 1 : inner_n;
|
|
450
|
+
func[ca1->data_type](n_call,
|
|
451
|
+
( ca2->mask ) ? (boolean8_t *) ca2->mask->ptr : NULL,
|
|
452
|
+
p1_root + e1_base * ca1->bytes,
|
|
453
|
+
(ndim == 0) ? 0 : s1_inner,
|
|
454
|
+
ca2->ptr, 1);
|
|
455
|
+
}
|
|
456
|
+
else {
|
|
457
|
+
while ( 1 ) {
|
|
458
|
+
ca_size_t off1 = e1_base;
|
|
459
|
+
for ( k = 0; k < ndim - 1; k++ ) {
|
|
460
|
+
off1 += idx[k] * e1_strides[k];
|
|
461
|
+
}
|
|
462
|
+
func[ca1->data_type](inner_n,
|
|
463
|
+
ca2->mask
|
|
464
|
+
? ((boolean8_t *) ca2->mask->ptr) + out_off
|
|
465
|
+
: NULL,
|
|
466
|
+
p1_root + off1 * ca1->bytes, s1_inner,
|
|
467
|
+
(char *) ca2->ptr + out_off * ca2->bytes, 1);
|
|
468
|
+
out_off += inner_n;
|
|
469
|
+
k = ndim - 2;
|
|
470
|
+
while ( k >= 0 ) {
|
|
471
|
+
if ( ++idx[k] < cs1->dim[k] ) break;
|
|
472
|
+
idx[k] = 0; k--;
|
|
473
|
+
}
|
|
474
|
+
if ( k < 0 ) break;
|
|
475
|
+
}
|
|
476
|
+
}
|
|
477
|
+
strided_path_done = 1;
|
|
478
|
+
}
|
|
479
|
+
}
|
|
480
|
+
}
|
|
481
|
+
|
|
482
|
+
if ( !strided_path_done ) {
|
|
483
|
+
volatile VALUE h1 = Qnil;
|
|
484
|
+
char *p1;
|
|
485
|
+
(void) h1;
|
|
486
|
+
p1 = ALLOCV_N(char, h1, ca1->elements * ca1->bytes);
|
|
487
|
+
ca_xfer_all(ca1, p1, CA_XFER_GET);
|
|
488
|
+
func[ca1->data_type](ca1->elements,
|
|
489
|
+
( ca2->mask ) ? (boolean8_t *)ca2->mask->ptr : NULL,
|
|
490
|
+
p1, 1,
|
|
491
|
+
ca2->ptr, 1);
|
|
492
|
+
ALLOCV_END(h1);
|
|
493
|
+
}
|
|
494
|
+
}
|
|
495
|
+
|
|
496
|
+
return out;
|
|
497
|
+
}
|
|
498
|
+
|
|
84
499
|
int
|
|
85
500
|
rb_ca_test_castable (VALUE other)
|
|
86
501
|
{
|
|
@@ -104,12 +519,119 @@ rb_ca_binop_pass_to_other (VALUE self, VALUE other, ID method)
|
|
|
104
519
|
return rb_funcall(self, method, 1, other);
|
|
105
520
|
}
|
|
106
521
|
|
|
522
|
+
/* Gather a boolean operand's value + mask bytes into vbuf/mbuf, broadcasting
|
|
523
|
+
a scalar operand across n cells. Uses ca_copy_data (materialise, no
|
|
524
|
+
attach) so views / lazy sources are handled transparently. */
|
|
525
|
+
static void
|
|
526
|
+
kleene_gather_bool (CArray *ca, boolean8_t *vbuf, boolean8_t *mbuf, ca_size_t n)
|
|
527
|
+
{
|
|
528
|
+
if ( ca->elements == n ) {
|
|
529
|
+
ca_copy_data(ca, (char *) vbuf);
|
|
530
|
+
ca_update_mask(ca);
|
|
531
|
+
if ( ca->mask ) {
|
|
532
|
+
ca_copy_data(ca->mask, (char *) mbuf);
|
|
533
|
+
}
|
|
534
|
+
else {
|
|
535
|
+
memset(mbuf, 0, n);
|
|
536
|
+
}
|
|
537
|
+
}
|
|
538
|
+
else { /* scalar operand: gather one, broadcast */
|
|
539
|
+
boolean8_t v = 0, m = 0;
|
|
540
|
+
ca_copy_data(ca, (char *) &v);
|
|
541
|
+
ca_update_mask(ca);
|
|
542
|
+
if ( ca->mask ) {
|
|
543
|
+
ca_copy_data(ca->mask, (char *) &m);
|
|
544
|
+
}
|
|
545
|
+
memset(vbuf, v, n);
|
|
546
|
+
memset(mbuf, m, n);
|
|
547
|
+
}
|
|
548
|
+
}
|
|
549
|
+
|
|
550
|
+
/* Kleene three-valued mask fixup for boolean AND (is_or=0) / OR (is_or=1).
|
|
551
|
+
|
|
552
|
+
After the value-blind binop, a masked output cell can still be resolved by
|
|
553
|
+
the *known* side: `unknown | true = true`, `unknown & false = false`. This
|
|
554
|
+
pass forces those cells to the known result and unmasks them; genuinely
|
|
555
|
+
undetermined cells (U|U, U&U, U|F, U&T) keep the blind mask. All other
|
|
556
|
+
cells were already correct from the value kernel.
|
|
557
|
+
|
|
558
|
+
Gate: boolean data type + output has a mask (else no-op -- the hot path is
|
|
559
|
+
untouched, integer bitwise stays blind). `out` is a fresh entity, so
|
|
560
|
+
out->ptr / out->mask->ptr are writable without attach. */
|
|
561
|
+
VALUE
|
|
562
|
+
ca_kleene_bool_fixup (VALUE vout, VALUE vself, VALUE vother, int is_or)
|
|
563
|
+
{
|
|
564
|
+
CArray *out, *a, *b;
|
|
565
|
+
volatile VALUE va, vb;
|
|
566
|
+
boolean8_t *ov, *om, *av, *am, *bv, *bm;
|
|
567
|
+
ca_size_t n, i;
|
|
568
|
+
|
|
569
|
+
TypedData_Get_Struct(vout, CArray, &carray_data_type, out);
|
|
570
|
+
if ( out->data_type != CA_BOOLEAN ) {
|
|
571
|
+
return vout; /* integer bitwise: blind, unchanged */
|
|
572
|
+
}
|
|
573
|
+
ca_update_mask(out);
|
|
574
|
+
if ( ! out->mask ) {
|
|
575
|
+
return vout; /* no undetermined cells to resolve */
|
|
576
|
+
}
|
|
577
|
+
|
|
578
|
+
/* Re-normalise the operands the same way rb_ca_call_binop did, so scalar /
|
|
579
|
+
CScalar / view operands all present as boolean CArrays. */
|
|
580
|
+
va = vself; vb = vother;
|
|
581
|
+
rb_ca_cast_self_or_other(&va, &vb);
|
|
582
|
+
TypedData_Get_Struct(va, CArray, &carray_data_type, a);
|
|
583
|
+
TypedData_Get_Struct(vb, CArray, &carray_data_type, b);
|
|
584
|
+
|
|
585
|
+
n = out->elements;
|
|
586
|
+
av = ALLOC_N(boolean8_t, n); am = ALLOC_N(boolean8_t, n);
|
|
587
|
+
bv = ALLOC_N(boolean8_t, n); bm = ALLOC_N(boolean8_t, n);
|
|
588
|
+
kleene_gather_bool(a, av, am, n);
|
|
589
|
+
kleene_gather_bool(b, bv, bm, n);
|
|
590
|
+
|
|
591
|
+
ov = (boolean8_t *) out->ptr;
|
|
592
|
+
om = (boolean8_t *) out->mask->ptr;
|
|
593
|
+
for (i = 0; i < n; i++) {
|
|
594
|
+
if ( ! om[i] ) {
|
|
595
|
+
continue; /* cell already determined */
|
|
596
|
+
}
|
|
597
|
+
if ( is_or ) {
|
|
598
|
+
if ( ( ! am[i] && av[i] ) || ( ! bm[i] && bv[i] ) ) { /* known TRUE */
|
|
599
|
+
ov[i] = 1;
|
|
600
|
+
om[i] = 0;
|
|
601
|
+
}
|
|
602
|
+
}
|
|
603
|
+
else {
|
|
604
|
+
if ( ( ! am[i] && ! av[i] ) || ( ! bm[i] && ! bv[i] ) ) { /* known FALSE */
|
|
605
|
+
ov[i] = 0;
|
|
606
|
+
om[i] = 0;
|
|
607
|
+
}
|
|
608
|
+
}
|
|
609
|
+
}
|
|
610
|
+
|
|
611
|
+
xfree(av); xfree(am); xfree(bv); xfree(bm);
|
|
612
|
+
return vout;
|
|
613
|
+
}
|
|
614
|
+
|
|
615
|
+
/* Binop driver, split into fast / slow path.
|
|
616
|
+
- fast: both operands alias-attachable (= entity / cscalar / contig
|
|
617
|
+
CAStride) → 1-shot ca_attach_n + kernel call.
|
|
618
|
+
- slow: at least one operand needs materialise → use ca_xfer_all to
|
|
619
|
+
gather into ALLOCV scratch, never calling ca_func[X].attach
|
|
620
|
+
on the operand. Honors the core invariant "the driver does
|
|
621
|
+
not attach an input-only operand", which keeps CAStack /
|
|
622
|
+
CATile expansion, attach-hostile roots, and unattachable test
|
|
623
|
+
fixtures working.
|
|
624
|
+
Output `ca3` is always a new entity (write target, attach legitimate). */
|
|
107
625
|
VALUE
|
|
108
626
|
rb_ca_call_binop (volatile VALUE self, volatile VALUE other,
|
|
109
627
|
ca_binop_func_t func[])
|
|
110
628
|
{
|
|
111
629
|
volatile VALUE out;
|
|
112
630
|
CArray *ca1, *ca2, *ca3; /* ca3 = ca1.op(ca2) */
|
|
631
|
+
int self_is_scalar, other_is_scalar;
|
|
632
|
+
ca_size_t n_kernel;
|
|
633
|
+
ca_size_t i1, i2, i3;
|
|
634
|
+
int fast_path;
|
|
113
635
|
|
|
114
636
|
/* do implicit casting and resolving unbound repeat array */
|
|
115
637
|
rb_ca_cast_self_or_other(&self, &other);
|
|
@@ -117,103 +639,394 @@ rb_ca_call_binop (volatile VALUE self, volatile VALUE other,
|
|
|
117
639
|
TypedData_Get_Struct(self, CArray, &carray_data_type, ca1);
|
|
118
640
|
TypedData_Get_Struct(other, CArray, &carray_data_type, ca2);
|
|
119
641
|
|
|
120
|
-
|
|
642
|
+
/* Boolean-as-numeric promotion: an arithmetic op (+ - * / % ** ...) has
|
|
643
|
+
no boolean kernel, so its func[CA_BOOLEAN] slot is ca_binop_not_implement.
|
|
644
|
+
When both operands promoted to CA_BOOLEAN (= bool op bool), coerce them
|
|
645
|
+
to CA_INT64 so `b1 + b2` behaves as its 0/1 numeric storage. Signed
|
|
646
|
+
(not the u64 used by bool reductions) because `b1 - b2` must reach -1.
|
|
647
|
+
Logical ops (& | ^) DO have a boolean kernel, so this leaves them as
|
|
648
|
+
bool. A bool op numeric already promoted away from CA_BOOLEAN above. */
|
|
649
|
+
if ( ca1->data_type == CA_BOOLEAN && func[CA_BOOLEAN] == ca_binop_not_implement ) {
|
|
650
|
+
self = rb_ca_wrap_readonly(self, INT2NUM(CA_INT64));
|
|
651
|
+
other = rb_ca_wrap_readonly(other, INT2NUM(CA_INT64));
|
|
652
|
+
TypedData_Get_Struct(self, CArray, &carray_data_type, ca1);
|
|
653
|
+
TypedData_Get_Struct(other, CArray, &carray_data_type, ca2);
|
|
654
|
+
}
|
|
655
|
+
|
|
656
|
+
self_is_scalar = RTEST(rb_obj_is_cscalar(self));
|
|
657
|
+
other_is_scalar = RTEST(rb_obj_is_cscalar(other));
|
|
121
658
|
|
|
122
|
-
/*
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
659
|
+
/* output template + kernel n + strides (= preserve existing dispatch
|
|
660
|
+
matrix: scalar×scalar / scalar×array / array×scalar / array×array) */
|
|
661
|
+
if ( self_is_scalar && other_is_scalar ) {
|
|
662
|
+
ca3 = ( ca_has_mask(ca1) || ca_has_mask(ca2) ) ? ca_template_safe(ca1)
|
|
663
|
+
: ca_template(ca1);
|
|
664
|
+
n_kernel = ca1->elements;
|
|
665
|
+
i1 = 0; i2 = 0; i3 = 0;
|
|
666
|
+
}
|
|
667
|
+
else if ( self_is_scalar /* && !other_is_scalar */ ) {
|
|
668
|
+
ca3 = ( ca_has_mask(ca1) || ca_has_mask(ca2) ) ? ca_template_safe(ca2)
|
|
669
|
+
: ca_template(ca2);
|
|
670
|
+
n_kernel = ca2->elements;
|
|
671
|
+
i1 = 0; i2 = 1; i3 = 1;
|
|
672
|
+
}
|
|
673
|
+
else if ( other_is_scalar /* && !self_is_scalar */ ) {
|
|
674
|
+
ca3 = ( ca_has_mask(ca1) || ca_has_mask(ca2) ) ? ca_template_safe(ca1)
|
|
675
|
+
: ca_template(ca1);
|
|
676
|
+
n_kernel = ca1->elements;
|
|
677
|
+
i1 = 1; i2 = 0; i3 = 1;
|
|
678
|
+
}
|
|
679
|
+
else { /* array vs array */
|
|
680
|
+
if ( ca1->elements != ca2->elements ) {
|
|
681
|
+
rb_raise(rb_eRuntimeError, "elements mismatch (%" PRId64 " <-> %" PRId64 ")",
|
|
682
|
+
(ca_size_t) ca1->elements,
|
|
683
|
+
(ca_size_t) ca2->elements);
|
|
684
|
+
}
|
|
685
|
+
ca3 = ( ca_has_mask(ca1) || ca_has_mask(ca2) ) ? ca_template_safe(ca1)
|
|
686
|
+
: ca_template(ca1);
|
|
687
|
+
n_kernel = ca1->elements;
|
|
688
|
+
i1 = 1; i2 = 1; i3 = 1;
|
|
689
|
+
}
|
|
690
|
+
out = ca_wrap_struct(ca3);
|
|
691
|
+
|
|
692
|
+
/* Mask overlay via the safe variant: materialises operand masks with
|
|
693
|
+
ca_xfer_all instead of attaching them, so a mask on an attach-hostile
|
|
694
|
+
input-only operand does not raise. */
|
|
695
|
+
ca_mask_overlay_safe(ca3, 2, ca1, ca2);
|
|
696
|
+
|
|
697
|
+
/* path decision: alias-attachable both → fast path */
|
|
698
|
+
fast_path = ca_attach_is_alias(ca1) && ca_attach_is_alias(ca2);
|
|
699
|
+
|
|
700
|
+
if ( fast_path ) {
|
|
701
|
+
/* FAST PATH: zero behavioral change for the hot case. */
|
|
702
|
+
ca_attach_n(2, ca1, ca2);
|
|
703
|
+
func[ca1->data_type](n_kernel,
|
|
704
|
+
( ca3->mask ) ? (boolean8_t *) ca3->mask->ptr : NULL,
|
|
705
|
+
ca1->ptr, i1,
|
|
706
|
+
ca2->ptr, i2,
|
|
707
|
+
ca3->ptr, i3);
|
|
708
|
+
ca_detach_n(2, ca1, ca2);
|
|
709
|
+
}
|
|
710
|
+
else if ( ca1 == ca2 ) {
|
|
711
|
+
/* SAME-OPERAND SHARING. When the same view appears on both sides
|
|
712
|
+
(`mt + mt`, `(view) < (view)` etc.), materialise once and share the
|
|
713
|
+
scratch buffer between both kernel inputs. When !fast_path &&
|
|
714
|
+
ca1 == ca2, ca1 must be non-alias (if it were alias, fast_path would
|
|
715
|
+
be true), so a single ALLOCV + ca_xfer_all suffices. */
|
|
716
|
+
volatile VALUE h_shared = Qnil;
|
|
717
|
+
char *p_shared;
|
|
718
|
+
(void) h_shared;
|
|
719
|
+
|
|
720
|
+
p_shared = ALLOCV_N(char, h_shared, ca1->elements * ca1->bytes);
|
|
721
|
+
ca_xfer_all(ca1, p_shared, CA_XFER_GET);
|
|
722
|
+
func[ca1->data_type](n_kernel,
|
|
723
|
+
( ca3->mask ) ? (boolean8_t *) ca3->mask->ptr : NULL,
|
|
724
|
+
p_shared, i1,
|
|
725
|
+
p_shared, i2,
|
|
726
|
+
ca3->ptr, i3);
|
|
727
|
+
ALLOCV_END(h_shared);
|
|
728
|
+
}
|
|
729
|
+
else {
|
|
730
|
+
/* SLOW PATH (distinct operands): CHUNKED materialise via arena.
|
|
731
|
+
Memory peak: O(operand_size) → O(chunk). Per-operand decision:
|
|
732
|
+
- i==0 scalar → gather 1 element once (fixed, stride 0 in kernel)
|
|
733
|
+
- alias array → use ca->ptr + off*bytes (no copy, contig alias)
|
|
734
|
+
- non-alias array → per-chunk gather into arena scratch
|
|
735
|
+
|
|
736
|
+
Threshold dispatch: count the non-alias array operands. If only 1
|
|
737
|
+
needs per-chunk gather, chunking gives no memory-peak win (the other
|
|
738
|
+
operand is already alias = no scratch) but pays per-iter dispatch
|
|
739
|
+
overhead, so fall back to a 1-shot ALLOCV for that operand. Only
|
|
740
|
+
when both operands need gather (a structural 2x peak reduction) do
|
|
741
|
+
we keep the chunked path.
|
|
742
|
+
|
|
743
|
+
Decision matrix:
|
|
744
|
+
- both array & both non-alias (= mt + mt2) → CHUNKED (peak 2x win)
|
|
745
|
+
- one non-alias array + one alias/scalar (= mt + 3.14, mt + entity)
|
|
746
|
+
→ 1-shot ALLOCV */
|
|
747
|
+
char *p1_src = NULL, *p2_src = NULL;
|
|
748
|
+
void *s1_arena = NULL, *s2_arena = NULL;
|
|
749
|
+
int gather_per_chunk1 = 0, gather_per_chunk2 = 0;
|
|
750
|
+
int attached1 = 0, attached2 = 0;
|
|
751
|
+
int8_t dt = ca1->data_type;
|
|
752
|
+
ca_size_t chunk_n;
|
|
753
|
+
ca_size_t off;
|
|
754
|
+
int nonalias_arrays;
|
|
755
|
+
int use_chunked;
|
|
756
|
+
|
|
757
|
+
nonalias_arrays = 0;
|
|
758
|
+
if ( i1 == 1 && !ca_attach_is_alias(ca1) ) nonalias_arrays++;
|
|
759
|
+
if ( i2 == 1 && !ca_attach_is_alias(ca2) ) nonalias_arrays++;
|
|
760
|
+
use_chunked = (nonalias_arrays >= 2);
|
|
761
|
+
|
|
762
|
+
/* Unified strided-walk path. Eligibility: both operands are array
|
|
763
|
+
(i==1) and at least one needs gather. Each operand must compose to
|
|
764
|
+
a ptr-bearing root,
|
|
765
|
+
either as CAStride family (via ca_stride_compose_to_root) or as an
|
|
766
|
+
entity (CA_OBJ_ARRAY / CA_OBJ_ARRAY_WRAP) treated as a view
|
|
767
|
+
CAStride with row-major byte strides + base 0. When eligible, walk
|
|
768
|
+
per-row directly into the kernel and skip the chunked-gather / ALLOCV
|
|
769
|
+
materialise pass — saves 2x bandwidth (no gather buffer write+read).
|
|
770
|
+
Output ca3 is contig entity; per-row output offset = row_idx *
|
|
771
|
+
inner_count. The use_chunked / ALLOCV blocks below remain as a
|
|
772
|
+
fallback (dead on the success path) but fire for views outside the
|
|
773
|
+
CAStride+entity family (CASelect / CAMapping / CAReduce
|
|
774
|
+
etc.). */
|
|
775
|
+
if ( i1 == 1 && i2 == 1 && nonalias_arrays >= 1 ) {
|
|
776
|
+
extern ca_operation_function_t ca_stride_func;
|
|
777
|
+
CArray *root1 = NULL, *root2 = NULL;
|
|
778
|
+
ca_size_t strides1[CA_RANK_MAX], strides2[CA_RANK_MAX];
|
|
779
|
+
ca_size_t base1 = 0, base2 = 0;
|
|
780
|
+
int8_t ndim = ca1->ndim;
|
|
781
|
+
int8_t k;
|
|
782
|
+
int ok = 1;
|
|
783
|
+
|
|
784
|
+
/* operand 1: CAStride family -> compose, entity -> synthesize */
|
|
785
|
+
if ( ca_func[ca1->obj_type].attach == ca_stride_func.attach ) {
|
|
786
|
+
ca_stride_compose_to_root((CAStride *) ca1, &root1, strides1, &base1);
|
|
127
787
|
}
|
|
128
|
-
else
|
|
129
|
-
|
|
788
|
+
else if ( ca1->obj_type == CA_OBJ_ARRAY ||
|
|
789
|
+
ca1->obj_type == CA_OBJ_ARRAY_WRAP ) {
|
|
790
|
+
ca_size_t stride_bytes = ca1->bytes;
|
|
791
|
+
root1 = ca1;
|
|
792
|
+
base1 = 0;
|
|
793
|
+
for ( k = ca1->ndim - 1; k >= 0; k-- ) {
|
|
794
|
+
strides1[k] = stride_bytes;
|
|
795
|
+
stride_bytes *= ca1->dim[k];
|
|
796
|
+
}
|
|
797
|
+
}
|
|
798
|
+
else ok = 0;
|
|
799
|
+
|
|
800
|
+
if ( ok ) {
|
|
801
|
+
if ( ca_func[ca2->obj_type].attach == ca_stride_func.attach ) {
|
|
802
|
+
ca_stride_compose_to_root((CAStride *) ca2, &root2, strides2, &base2);
|
|
803
|
+
}
|
|
804
|
+
else if ( ca2->obj_type == CA_OBJ_ARRAY ||
|
|
805
|
+
ca2->obj_type == CA_OBJ_ARRAY_WRAP ) {
|
|
806
|
+
ca_size_t stride_bytes = ca2->bytes;
|
|
807
|
+
root2 = ca2;
|
|
808
|
+
base2 = 0;
|
|
809
|
+
for ( k = ca2->ndim - 1; k >= 0; k-- ) {
|
|
810
|
+
strides2[k] = stride_bytes;
|
|
811
|
+
stride_bytes *= ca2->dim[k];
|
|
812
|
+
}
|
|
813
|
+
}
|
|
814
|
+
else ok = 0;
|
|
130
815
|
}
|
|
131
|
-
out = ca_wrap_struct(ca3);
|
|
132
816
|
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
817
|
+
if ( ok ) {
|
|
818
|
+
if ( !root1->ptr || !root2->ptr ) ok = 0;
|
|
819
|
+
if ( ca2->ndim != ndim ) ok = 0;
|
|
820
|
+
|
|
821
|
+
/* element-stride conversion: strides must be byte-multiples of bytes */
|
|
822
|
+
if ( ok ) {
|
|
823
|
+
for ( k = 0; k < ndim; k++ ) {
|
|
824
|
+
if ( strides1[k] % ca1->bytes != 0 ) { ok = 0; break; }
|
|
825
|
+
if ( strides2[k] % ca2->bytes != 0 ) { ok = 0; break; }
|
|
826
|
+
}
|
|
827
|
+
}
|
|
828
|
+
if ( ok && (base1 % ca1->bytes != 0 || base2 % ca2->bytes != 0) ) ok = 0;
|
|
829
|
+
}
|
|
830
|
+
|
|
831
|
+
if ( ok ) {
|
|
832
|
+
int8_t inner_axis = ndim - 1;
|
|
833
|
+
ca_size_t inner_n = (ndim >= 1) ? ca1->dim[inner_axis] : ca1->elements;
|
|
834
|
+
ca_size_t s1_inner = (ndim >= 1) ? strides1[inner_axis] / ca1->bytes : 1;
|
|
835
|
+
ca_size_t s2_inner = (ndim >= 1) ? strides2[inner_axis] / ca2->bytes : 1;
|
|
836
|
+
ca_size_t idx[CA_RANK_MAX];
|
|
837
|
+
ca_size_t out_off = 0;
|
|
838
|
+
ca_size_t e1_strides[CA_RANK_MAX], e2_strides[CA_RANK_MAX];
|
|
839
|
+
ca_size_t e1_base, e2_base;
|
|
840
|
+
char *p1_root, *p2_root;
|
|
841
|
+
|
|
842
|
+
for ( k = 0; k < ndim; k++ ) {
|
|
843
|
+
e1_strides[k] = strides1[k] / ca1->bytes;
|
|
844
|
+
e2_strides[k] = strides2[k] / ca2->bytes;
|
|
845
|
+
}
|
|
846
|
+
e1_base = base1 / ca1->bytes;
|
|
847
|
+
e2_base = base2 / ca2->bytes;
|
|
848
|
+
p1_root = (char *) root1->ptr;
|
|
849
|
+
p2_root = (char *) root2->ptr;
|
|
850
|
+
|
|
851
|
+
for ( k = 0; k < ndim; k++ ) idx[k] = 0;
|
|
852
|
+
|
|
853
|
+
if ( ndim == 0 || ndim == 1 ) {
|
|
854
|
+
ca_size_t n_call = (ndim == 0) ? 1 : inner_n;
|
|
855
|
+
func[dt](n_call,
|
|
856
|
+
( ca3->mask ) ? (boolean8_t *) ca3->mask->ptr : NULL,
|
|
857
|
+
p1_root + e1_base * ca1->bytes, (ndim == 0) ? 0 : s1_inner,
|
|
858
|
+
p2_root + e2_base * ca2->bytes, (ndim == 0) ? 0 : s2_inner,
|
|
859
|
+
ca3->ptr, i3);
|
|
860
|
+
}
|
|
861
|
+
else {
|
|
862
|
+
while ( 1 ) {
|
|
863
|
+
ca_size_t off1 = e1_base, off2 = e2_base;
|
|
864
|
+
for ( k = 0; k < ndim - 1; k++ ) {
|
|
865
|
+
off1 += idx[k] * e1_strides[k];
|
|
866
|
+
off2 += idx[k] * e2_strides[k];
|
|
867
|
+
}
|
|
868
|
+
func[dt](inner_n,
|
|
869
|
+
ca3->mask
|
|
870
|
+
? ((boolean8_t *) ca3->mask->ptr) + out_off
|
|
871
|
+
: NULL,
|
|
872
|
+
p1_root + off1 * ca1->bytes, s1_inner,
|
|
873
|
+
p2_root + off2 * ca2->bytes, s2_inner,
|
|
874
|
+
(char *) ca3->ptr + out_off * ca3->bytes, i3);
|
|
875
|
+
out_off += inner_n;
|
|
876
|
+
k = ndim - 2;
|
|
877
|
+
while ( k >= 0 ) {
|
|
878
|
+
if ( ++idx[k] < ca1->dim[k] ) break;
|
|
879
|
+
idx[k] = 0; k--;
|
|
880
|
+
}
|
|
881
|
+
if ( k < 0 ) break;
|
|
882
|
+
}
|
|
883
|
+
}
|
|
884
|
+
|
|
885
|
+
return out;
|
|
886
|
+
}
|
|
887
|
+
/* else fall through to the use_chunked / ALLOCV path */
|
|
139
888
|
}
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
889
|
+
|
|
890
|
+
if ( !use_chunked ) {
|
|
891
|
+
/* 1-shot ALLOCV path. At most 1 operand needs ALLOCV; the other is
|
|
892
|
+
alias-direct. */
|
|
893
|
+
volatile VALUE h1 = Qnil, h2 = Qnil;
|
|
894
|
+
char *p1, *p2;
|
|
895
|
+
int a1 = 0, a2 = 0;
|
|
896
|
+
(void) h1; (void) h2;
|
|
897
|
+
|
|
898
|
+
if ( ca_attach_is_alias(ca1) ) {
|
|
899
|
+
ca_attach(ca1); p1 = (char *) ca1->ptr; a1 = 1;
|
|
900
|
+
} else {
|
|
901
|
+
p1 = ALLOCV_N(char, h1, ca1->elements * ca1->bytes);
|
|
902
|
+
ca_xfer_all(ca1, p1, CA_XFER_GET);
|
|
143
903
|
}
|
|
144
|
-
|
|
145
|
-
|
|
904
|
+
if ( ca_attach_is_alias(ca2) ) {
|
|
905
|
+
ca_attach(ca2); p2 = (char *) ca2->ptr; a2 = 1;
|
|
906
|
+
} else {
|
|
907
|
+
p2 = ALLOCV_N(char, h2, ca2->elements * ca2->bytes);
|
|
908
|
+
ca_xfer_all(ca2, p2, CA_XFER_GET);
|
|
146
909
|
}
|
|
147
|
-
out = ca_wrap_struct(ca3);
|
|
148
910
|
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
911
|
+
func[dt](n_kernel,
|
|
912
|
+
( ca3->mask ) ? (boolean8_t *) ca3->mask->ptr : NULL,
|
|
913
|
+
p1, i1,
|
|
914
|
+
p2, i2,
|
|
915
|
+
ca3->ptr, i3);
|
|
916
|
+
|
|
917
|
+
if ( a2 ) { ca_detach(ca2); } else { ALLOCV_END(h2); }
|
|
918
|
+
if ( a1 ) { ca_detach(ca1); } else { ALLOCV_END(h1); }
|
|
919
|
+
|
|
920
|
+
return out;
|
|
155
921
|
}
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
922
|
+
|
|
923
|
+
/* Compute chunk_n: target ~32KB worth of cells, row-aligned to the
|
|
924
|
+
larger-inner operand's outer axis. */
|
|
925
|
+
{
|
|
926
|
+
ca_size_t inner = 1;
|
|
927
|
+
ca_size_t maxb = ca1->bytes > ca2->bytes ? ca1->bytes : ca2->bytes;
|
|
928
|
+
if (ca3->bytes > maxb) maxb = ca3->bytes;
|
|
929
|
+
if (i1 == 1) inner = ca_chunk_inner_size(ca1);
|
|
930
|
+
if (i2 == 1) {
|
|
931
|
+
ca_size_t inner2 = ca_chunk_inner_size(ca2);
|
|
932
|
+
if (inner2 > inner) inner = inner2;
|
|
164
933
|
}
|
|
165
|
-
|
|
934
|
+
chunk_n = ca_chunk_compute_n(n_kernel, inner, maxb);
|
|
935
|
+
}
|
|
166
936
|
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
937
|
+
ca_lazy_arena_enter();
|
|
938
|
+
|
|
939
|
+
/* ca1 acquire */
|
|
940
|
+
if ( i1 == 0 ) {
|
|
941
|
+
/* scalar: gather 1 element once, kernel re-reads with stride 0 */
|
|
942
|
+
if ( ca_attach_is_alias(ca1) ) {
|
|
943
|
+
ca_attach(ca1);
|
|
944
|
+
p1_src = (char *) ca1->ptr;
|
|
945
|
+
attached1 = 1;
|
|
946
|
+
} else {
|
|
947
|
+
s1_arena = ca_op_acquire_operand_scratch(ca1, 1);
|
|
948
|
+
ca_xfer_all(ca1, s1_arena, CA_XFER_GET);
|
|
949
|
+
p1_src = (char *) s1_arena;
|
|
950
|
+
}
|
|
951
|
+
} else if ( ca_attach_is_alias(ca1) ) {
|
|
952
|
+
ca_attach(ca1);
|
|
953
|
+
p1_src = (char *) ca1->ptr;
|
|
954
|
+
attached1 = 1;
|
|
955
|
+
} else {
|
|
956
|
+
s1_arena = ca_op_acquire_operand_scratch(ca1, chunk_n);
|
|
957
|
+
p1_src = (char *) s1_arena;
|
|
958
|
+
gather_per_chunk1 = 1;
|
|
173
959
|
}
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
960
|
+
|
|
961
|
+
/* ca2 acquire (mirror) */
|
|
962
|
+
if ( i2 == 0 ) {
|
|
963
|
+
if ( ca_attach_is_alias(ca2) ) {
|
|
964
|
+
ca_attach(ca2);
|
|
965
|
+
p2_src = (char *) ca2->ptr;
|
|
966
|
+
attached2 = 1;
|
|
967
|
+
} else {
|
|
968
|
+
s2_arena = ca_op_acquire_operand_scratch(ca2, 1);
|
|
969
|
+
ca_xfer_all(ca2, s2_arena, CA_XFER_GET);
|
|
970
|
+
p2_src = (char *) s2_arena;
|
|
178
971
|
}
|
|
179
|
-
|
|
180
|
-
|
|
972
|
+
} else if ( ca_attach_is_alias(ca2) ) {
|
|
973
|
+
ca_attach(ca2);
|
|
974
|
+
p2_src = (char *) ca2->ptr;
|
|
975
|
+
attached2 = 1;
|
|
976
|
+
} else {
|
|
977
|
+
s2_arena = ca_op_acquire_operand_scratch(ca2, chunk_n);
|
|
978
|
+
p2_src = (char *) s2_arena;
|
|
979
|
+
gather_per_chunk2 = 1;
|
|
980
|
+
}
|
|
981
|
+
|
|
982
|
+
/* chunk loop: walk n_kernel cells in chunk_n strides */
|
|
983
|
+
for ( off = 0; off < n_kernel; off += chunk_n ) {
|
|
984
|
+
ca_size_t n_done = (off + chunk_n > n_kernel) ? n_kernel - off : chunk_n;
|
|
985
|
+
char *p1, *p2;
|
|
986
|
+
|
|
987
|
+
if ( gather_per_chunk1 ) {
|
|
988
|
+
ca_chunked_gather(ca1, off, n_done, s1_arena);
|
|
989
|
+
p1 = (char *) s1_arena;
|
|
990
|
+
} else {
|
|
991
|
+
p1 = p1_src + (i1 ? off * ca1->bytes : 0);
|
|
181
992
|
}
|
|
182
|
-
|
|
183
|
-
|
|
993
|
+
if ( gather_per_chunk2 ) {
|
|
994
|
+
ca_chunked_gather(ca2, off, n_done, s2_arena);
|
|
995
|
+
p2 = (char *) s2_arena;
|
|
996
|
+
} else {
|
|
997
|
+
p2 = p2_src + (i2 ? off * ca2->bytes : 0);
|
|
184
998
|
}
|
|
185
|
-
out = ca_wrap_struct(ca3);
|
|
186
999
|
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
1000
|
+
func[dt](n_done,
|
|
1001
|
+
ca3->mask ? ((boolean8_t *) ca3->mask->ptr) + (i3 ? off : 0)
|
|
1002
|
+
: NULL,
|
|
1003
|
+
p1, i1,
|
|
1004
|
+
p2, i2,
|
|
1005
|
+
(char *) ca3->ptr + (i3 ? off * ca3->bytes : 0), i3);
|
|
193
1006
|
}
|
|
194
|
-
}
|
|
195
1007
|
|
|
196
|
-
|
|
1008
|
+
if ( s2_arena ) ca_lazy_arena_release(s2_arena);
|
|
1009
|
+
if ( s1_arena ) ca_lazy_arena_release(s1_arena);
|
|
1010
|
+
if ( attached2 ) ca_detach(ca2);
|
|
1011
|
+
if ( attached1 ) ca_detach(ca1);
|
|
197
1012
|
|
|
198
|
-
|
|
199
|
-
if ( ca1->obj_type == CA_OBJ_UNBOUND_REPEAT ) {
|
|
200
|
-
CAUnboundRepeat *cx = (CAUnboundRepeat *) ca1;
|
|
201
|
-
out = rb_ca_ubrep_new(rb_ca_ubrep_shave(self, out), cx->rep_ndim, cx->rep_dim);
|
|
202
|
-
}
|
|
203
|
-
|
|
204
|
-
/* unresolved unbound repeat array generates unbound repeat array again */
|
|
205
|
-
if ( ca2->obj_type == CA_OBJ_UNBOUND_REPEAT ) {
|
|
206
|
-
CAUnboundRepeat *cx = (CAUnboundRepeat *) ca2;
|
|
207
|
-
out = rb_ca_ubrep_new(rb_ca_ubrep_shave(other, out), cx->rep_ndim, cx->rep_dim);
|
|
1013
|
+
ca_lazy_arena_exit();
|
|
208
1014
|
}
|
|
209
1015
|
|
|
210
1016
|
return out;
|
|
211
1017
|
}
|
|
212
1018
|
|
|
1019
|
+
/* Bang (in-place) variant. Invariant: the input-only operand (= other)
|
|
1020
|
+
must not be attached; self IS the output (write target, attach
|
|
1021
|
+
legitimate). Same fast/slow pattern as rb_ca_call_binop, applied to
|
|
1022
|
+
`other` only.
|
|
1023
|
+
self always goes through ca_attach + ca_sync (= write-back to root). */
|
|
213
1024
|
VALUE
|
|
214
1025
|
rb_ca_call_binop_bang (VALUE self, VALUE other, ca_binop_func_t func[])
|
|
215
1026
|
{
|
|
216
1027
|
CArray *ca1, *ca2; /* ca1.op!(ca2) */
|
|
1028
|
+
int self_is_scalar, other_is_scalar;
|
|
1029
|
+
ca_size_t i1, i2;
|
|
217
1030
|
|
|
218
1031
|
rb_ca_modify(self);
|
|
219
1032
|
|
|
@@ -223,63 +1036,465 @@ rb_ca_call_binop_bang (VALUE self, VALUE other, ca_binop_func_t func[])
|
|
|
223
1036
|
TypedData_Get_Struct(self, CArray, &carray_data_type, ca1);
|
|
224
1037
|
TypedData_Get_Struct(other, CArray, &carray_data_type, ca2);
|
|
225
1038
|
|
|
226
|
-
|
|
1039
|
+
self_is_scalar = RTEST(rb_obj_is_cscalar(self));
|
|
1040
|
+
other_is_scalar = RTEST(rb_obj_is_cscalar(other));
|
|
227
1041
|
|
|
228
|
-
/*
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
1042
|
+
/* self is the write target, so its shape is the result's by definition
|
|
1043
|
+
and the destination rule applies (the same one assignment uses), not
|
|
1044
|
+
the symmetric one a binary operation is held to. */
|
|
1045
|
+
if ( !self_is_scalar && !other_is_scalar ) {
|
|
1046
|
+
ca_broadcast_to_destination(self, &other);
|
|
1047
|
+
TypedData_Get_Struct(other, CArray, &carray_data_type, ca2);
|
|
1048
|
+
other_is_scalar = RTEST(rb_obj_is_cscalar(other));
|
|
1049
|
+
}
|
|
1050
|
+
if ( self_is_scalar && !other_is_scalar &&
|
|
1051
|
+
ca1->elements != ca2->elements ) {
|
|
1052
|
+
rb_raise(rb_eRuntimeError, "elements mismatch (%" PRId64 " <-> %" PRId64 ")",
|
|
1053
|
+
(ca_size_t) ca1->elements,
|
|
1054
|
+
(ca_size_t) ca2->elements);
|
|
1055
|
+
}
|
|
1056
|
+
|
|
1057
|
+
/* kernel strides: cscalar → 0 (broadcast). ca1 is both src1 (input)
|
|
1058
|
+
and dst (output), same stride. ca2 is input only. */
|
|
1059
|
+
i1 = self_is_scalar ? 0 : 1;
|
|
1060
|
+
i2 = other_is_scalar ? 0 : 1;
|
|
1061
|
+
|
|
1062
|
+
/* self IS the output (= write target). Always attach + ca_sync
|
|
1063
|
+
(= legitimate per refined invariant; self can be view e.g.
|
|
1064
|
+
`arr[i,nil].add!(b)`, sync writes back to root). */
|
|
1065
|
+
ca_attach(ca1);
|
|
1066
|
+
ca_mask_overlay_safe(ca1, 2, ca1, ca2);
|
|
1067
|
+
|
|
1068
|
+
/* other is input only: fast path if alias-cheap, else materialise
|
|
1069
|
+
via ca_xfer_all without ca_func[X].attach. */
|
|
1070
|
+
if ( ca_attach_is_alias(ca2) ) {
|
|
1071
|
+
ca_attach(ca2);
|
|
1072
|
+
func[ca1->data_type](ca1->elements,
|
|
1073
|
+
( ca1->mask ) ? (boolean8_t *) ca1->mask->ptr : NULL,
|
|
1074
|
+
ca1->ptr, i1,
|
|
1075
|
+
ca2->ptr, i2,
|
|
1076
|
+
ca1->ptr, i1);
|
|
1077
|
+
ca_detach(ca2);
|
|
1078
|
+
}
|
|
1079
|
+
else {
|
|
1080
|
+
volatile VALUE h2 = Qnil;
|
|
1081
|
+
char *p2;
|
|
1082
|
+
(void) h2;
|
|
1083
|
+
p2 = ALLOCV_N(char, h2, ca2->elements * ca2->bytes);
|
|
1084
|
+
ca_xfer_all(ca2, p2, CA_XFER_GET);
|
|
1085
|
+
func[ca1->data_type](ca1->elements,
|
|
1086
|
+
( ca1->mask ) ? (boolean8_t *) ca1->mask->ptr : NULL,
|
|
1087
|
+
ca1->ptr, i1,
|
|
1088
|
+
p2, i2,
|
|
1089
|
+
ca1->ptr, i1);
|
|
1090
|
+
ALLOCV_END(h2);
|
|
1091
|
+
}
|
|
1092
|
+
|
|
1093
|
+
ca_sync(ca1);
|
|
1094
|
+
ca_detach(ca1);
|
|
1095
|
+
|
|
1096
|
+
return self;
|
|
1097
|
+
}
|
|
1098
|
+
|
|
1099
|
+
/* ------------------------------------------------------------------- */
|
|
1100
|
+
/* Triop driver — 3 inputs, 1 output, eager-only. */
|
|
1101
|
+
/* */
|
|
1102
|
+
/* Approach: instead of enumerating the 2^3 = 8 scalar/array combos in */
|
|
1103
|
+
/* line as `rb_ca_call_binop` does, we reuse `ca_set_iterator(3, ...)` */
|
|
1104
|
+
/* from carray_call_cfunc.c which collapses any operand with */
|
|
1105
|
+
/* `is_scalar == true` to a stride-0 walker. Same uniform inner loop */
|
|
1106
|
+
/* for all 8 cases. Output is templated from the first non-scalar */
|
|
1107
|
+
/* operand (or self if all are scalar). */
|
|
1108
|
+
/* ------------------------------------------------------------------- */
|
|
1109
|
+
|
|
1110
|
+
static VALUE
|
|
1111
|
+
rb_ca_triop_select_template (VALUE self, VALUE other2, VALUE other3,
|
|
1112
|
+
CArray *ca1, CArray *ca2, CArray *ca3)
|
|
1113
|
+
{
|
|
1114
|
+
/* Template from the first non-scalar operand; fall back to self if
|
|
1115
|
+
all three are scalars. Mask is allocated when any operand has
|
|
1116
|
+
a mask. */
|
|
1117
|
+
int has_mask = ca_has_mask(ca1) || ca_has_mask(ca2) || ca_has_mask(ca3);
|
|
1118
|
+
CArray *src;
|
|
1119
|
+
if ( ! rb_obj_is_cscalar(self) ) src = ca1;
|
|
1120
|
+
else if ( ! rb_obj_is_cscalar(other2) ) src = ca2;
|
|
1121
|
+
else if ( ! rb_obj_is_cscalar(other3) ) src = ca3;
|
|
1122
|
+
else src = ca1;
|
|
1123
|
+
return has_mask ? ca_wrap_struct(ca_template_safe(src))
|
|
1124
|
+
: ca_wrap_struct(ca_template(src));
|
|
1125
|
+
}
|
|
1126
|
+
|
|
1127
|
+
/* Per-operand acquire/release macros for input-only operands. Used by
|
|
1128
|
+
the triop / bincmp drivers where 3 inputs make inline branching
|
|
1129
|
+
unwieldy.
|
|
1130
|
+
|
|
1131
|
+
- alias-cheap operand → ca_attach (= O(1)) + use ca->ptr directly
|
|
1132
|
+
- else → ALLOCV scratch + ca_xfer_all without ca_func[X].attach
|
|
1133
|
+
|
|
1134
|
+
Pair ACQUIRE / RELEASE; `h` must be a volatile VALUE declared by
|
|
1135
|
+
caller (= ALLOCV_END requires it even on alias-cheap branch where
|
|
1136
|
+
ALLOCV_N wasn't actually called, since holder stays Qnil = no-op). */
|
|
1137
|
+
#define EAGER_ACQUIRE_INPUT(ca_, p_, h_, attached_) do { \
|
|
1138
|
+
if ( ca_attach_is_alias(ca_) ) { \
|
|
1139
|
+
ca_attach(ca_); \
|
|
1140
|
+
(p_) = (char *)(ca_)->ptr; \
|
|
1141
|
+
(attached_) = 1; \
|
|
1142
|
+
} \
|
|
1143
|
+
else { \
|
|
1144
|
+
(p_) = ALLOCV_N(char, (h_), (ca_)->elements * (ca_)->bytes); \
|
|
1145
|
+
ca_xfer_all((ca_), (p_), CA_XFER_GET); \
|
|
1146
|
+
(attached_) = 0; \
|
|
1147
|
+
} \
|
|
1148
|
+
} while (0)
|
|
1149
|
+
|
|
1150
|
+
#define EAGER_RELEASE_INPUT(ca_, h_, attached_) do { \
|
|
1151
|
+
if ( (attached_) ) { ca_detach(ca_); } \
|
|
1152
|
+
else { ALLOCV_END(h_); } \
|
|
1153
|
+
} while (0)
|
|
1154
|
+
|
|
1155
|
+
/* triop driver. 3 inputs are input-only (the driver does not attach
|
|
1156
|
+
them); cao = new entity output (attach is legit). Each input
|
|
1157
|
+
independently uses fast (= alias) or slow (= ALLOCV + ca_xfer_all)
|
|
1158
|
+
path. */
|
|
1159
|
+
VALUE
|
|
1160
|
+
rb_ca_call_triop (VALUE self, VALUE other2, VALUE other3,
|
|
1161
|
+
ca_triop_func_t func[])
|
|
1162
|
+
{
|
|
1163
|
+
volatile VALUE out;
|
|
1164
|
+
CArray *ca1, *ca2, *ca3, *cao;
|
|
1165
|
+
|
|
1166
|
+
/* Pairwise data_type promotion: ((self, other2) -> common), then
|
|
1167
|
+
((self', other3) -> common). Mirrors how the binop driver normalises
|
|
1168
|
+
two operands; for triop we apply it twice. After this, all three
|
|
1169
|
+
CArrays share the same data_type (and unbound-repeats are resolved). */
|
|
1170
|
+
rb_ca_cast_self_or_other(&self, &other2);
|
|
1171
|
+
rb_ca_cast_self_or_other(&self, &other3);
|
|
1172
|
+
rb_ca_cast_self_or_other(&other2, &other3);
|
|
1173
|
+
/* one more pass to re-normalise self vs other2 in case the
|
|
1174
|
+
other2/other3 cast widened other2 above self's data_type */
|
|
1175
|
+
rb_ca_cast_self_or_other(&self, &other2);
|
|
1176
|
+
|
|
1177
|
+
TypedData_Get_Struct(self, CArray, &carray_data_type, ca1);
|
|
1178
|
+
TypedData_Get_Struct(other2, CArray, &carray_data_type, ca2);
|
|
1179
|
+
TypedData_Get_Struct(other3, CArray, &carray_data_type, ca3);
|
|
1180
|
+
|
|
1181
|
+
/* Boolean-as-numeric promotion (same rule as rb_ca_call_binop): an
|
|
1182
|
+
arithmetic triop (fma / fms) has no boolean kernel, so its
|
|
1183
|
+
func[CA_BOOLEAN] slot is ca_triop_not_implement. When all three
|
|
1184
|
+
operands promoted to CA_BOOLEAN (= every operand boolean), coerce
|
|
1185
|
+
them to CA_INT64 so `a * b + c` behaves as their 0/1 numeric storage
|
|
1186
|
+
(signed, so a product/sum can reach negative in fms). A boolean
|
|
1187
|
+
mixed with a numeric already promoted away from CA_BOOLEAN via the
|
|
1188
|
+
pairwise casts above. */
|
|
1189
|
+
if ( ca1->data_type == CA_BOOLEAN && func[CA_BOOLEAN] == ca_triop_not_implement ) {
|
|
1190
|
+
self = rb_ca_wrap_readonly(self, INT2NUM(CA_INT64));
|
|
1191
|
+
other2 = rb_ca_wrap_readonly(other2, INT2NUM(CA_INT64));
|
|
1192
|
+
other3 = rb_ca_wrap_readonly(other3, INT2NUM(CA_INT64));
|
|
1193
|
+
TypedData_Get_Struct(self, CArray, &carray_data_type, ca1);
|
|
1194
|
+
TypedData_Get_Struct(other2, CArray, &carray_data_type, ca2);
|
|
1195
|
+
TypedData_Get_Struct(other3, CArray, &carray_data_type, ca3);
|
|
1196
|
+
}
|
|
1197
|
+
|
|
1198
|
+
/* Element-count check: all non-scalar operands must agree. */
|
|
1199
|
+
{
|
|
1200
|
+
ca_size_t n = 1;
|
|
1201
|
+
if ( ! rb_obj_is_cscalar(self) ) n = ca1->elements;
|
|
1202
|
+
if ( ! rb_obj_is_cscalar(other2) ) {
|
|
1203
|
+
if ( n == 1 ) n = ca2->elements;
|
|
1204
|
+
else if ( ca2->elements != n ) {
|
|
1205
|
+
rb_raise(rb_eRuntimeError, "elements mismatch in triop (op2: %" PRId64 " != %" PRId64 ")",
|
|
1206
|
+
(ca_size_t) ca2->elements, n);
|
|
1207
|
+
}
|
|
237
1208
|
}
|
|
238
|
-
|
|
239
|
-
if (
|
|
240
|
-
|
|
241
|
-
|
|
1209
|
+
if ( ! rb_obj_is_cscalar(other3) ) {
|
|
1210
|
+
if ( n == 1 ) n = ca3->elements;
|
|
1211
|
+
else if ( ca3->elements != n ) {
|
|
1212
|
+
rb_raise(rb_eRuntimeError, "elements mismatch in triop (op3: %" PRId64 " != %" PRId64 ")",
|
|
1213
|
+
(ca_size_t) ca3->elements, n);
|
|
242
1214
|
}
|
|
243
|
-
|
|
244
|
-
ca_copy_mask_overlay(ca1, ca1->elements, 2, ca1, ca2);
|
|
245
|
-
func[ca1->data_type](ca1->elements,
|
|
246
|
-
( ca1->mask ) ? (boolean8_t *) ca1->mask->ptr : NULL,
|
|
247
|
-
ca1->ptr, 0,
|
|
248
|
-
ca2->ptr, 0,
|
|
249
|
-
ca1->ptr, 0);
|
|
250
1215
|
}
|
|
251
1216
|
}
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
1217
|
+
|
|
1218
|
+
out = rb_ca_triop_select_template(self, other2, other3, ca1, ca2, ca3);
|
|
1219
|
+
TypedData_Get_Struct(out, CArray, &carray_data_type, cao);
|
|
1220
|
+
|
|
1221
|
+
ca_mask_overlay_safe(cao, 3, ca1, ca2, ca3);
|
|
1222
|
+
|
|
1223
|
+
{
|
|
1224
|
+
ca_size_t s1 = rb_obj_is_cscalar(self) ? 0 : 1;
|
|
1225
|
+
ca_size_t s2 = rb_obj_is_cscalar(other2) ? 0 : 1;
|
|
1226
|
+
ca_size_t s3 = rb_obj_is_cscalar(other3) ? 0 : 1;
|
|
1227
|
+
|
|
1228
|
+
/* Binop-style threshold dispatch: count non-alias array operands;
|
|
1229
|
+
>= 2 → CHUNKED (memory peak amortizes), else 1-shot ALLOCV. 3-way
|
|
1230
|
+
same-operand sharing is not done (a rare pattern like `fma(a, a, b)`;
|
|
1231
|
+
for now a non-alias `a` is gathered twice — wasteful but correct). */
|
|
1232
|
+
int nonalias_arrays = 0;
|
|
1233
|
+
int use_chunked;
|
|
1234
|
+
if ( s1 == 1 && !ca_attach_is_alias(ca1) ) nonalias_arrays++;
|
|
1235
|
+
if ( s2 == 1 && !ca_attach_is_alias(ca2) ) nonalias_arrays++;
|
|
1236
|
+
if ( s3 == 1 && !ca_attach_is_alias(ca3) ) nonalias_arrays++;
|
|
1237
|
+
use_chunked = (nonalias_arrays >= 2);
|
|
1238
|
+
|
|
1239
|
+
if ( !use_chunked ) {
|
|
1240
|
+
volatile VALUE h1 = Qnil, h2 = Qnil, h3 = Qnil;
|
|
1241
|
+
char *p1, *p2, *p3;
|
|
1242
|
+
int attached1, attached2, attached3;
|
|
1243
|
+
(void) h1; (void) h2; (void) h3;
|
|
1244
|
+
|
|
1245
|
+
EAGER_ACQUIRE_INPUT(ca1, p1, h1, attached1);
|
|
1246
|
+
EAGER_ACQUIRE_INPUT(ca2, p2, h2, attached2);
|
|
1247
|
+
EAGER_ACQUIRE_INPUT(ca3, p3, h3, attached3);
|
|
1248
|
+
|
|
1249
|
+
func[ca1->data_type](cao->elements,
|
|
1250
|
+
( cao->mask ) ? (boolean8_t *) cao->mask->ptr : NULL,
|
|
1251
|
+
p1, s1,
|
|
1252
|
+
p2, s2,
|
|
1253
|
+
p3, s3,
|
|
1254
|
+
cao->ptr, 1);
|
|
1255
|
+
|
|
1256
|
+
EAGER_RELEASE_INPUT(ca3, h3, attached3);
|
|
1257
|
+
EAGER_RELEASE_INPUT(ca2, h2, attached2);
|
|
1258
|
+
EAGER_RELEASE_INPUT(ca1, h1, attached1);
|
|
260
1259
|
}
|
|
261
|
-
else {
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
1260
|
+
else {
|
|
1261
|
+
/* CHUNKED PATH: per-operand decision matrix (the binop pattern
|
|
1262
|
+
extended to 3 inputs). scalar/alias same as before; non-alias
|
|
1263
|
+
array goes through per-chunk gather. */
|
|
1264
|
+
char *p1_src = NULL, *p2_src = NULL, *p3_src = NULL;
|
|
1265
|
+
void *s1_arena = NULL, *s2_arena = NULL, *s3_arena = NULL;
|
|
1266
|
+
int gpc1 = 0, gpc2 = 0, gpc3 = 0; /* gather-per-chunk flags */
|
|
1267
|
+
int att1 = 0, att2 = 0, att3 = 0;
|
|
1268
|
+
int8_t dt = ca1->data_type;
|
|
1269
|
+
ca_size_t chunk_n;
|
|
1270
|
+
ca_size_t off;
|
|
1271
|
+
ca_size_t n_total = cao->elements;
|
|
1272
|
+
|
|
1273
|
+
{
|
|
1274
|
+
ca_size_t inner = 1;
|
|
1275
|
+
ca_size_t maxb = ca1->bytes;
|
|
1276
|
+
if ( ca2->bytes > maxb ) maxb = ca2->bytes;
|
|
1277
|
+
if ( ca3->bytes > maxb ) maxb = ca3->bytes;
|
|
1278
|
+
if ( cao->bytes > maxb ) maxb = cao->bytes;
|
|
1279
|
+
if ( s1 == 1 ) inner = ca_chunk_inner_size(ca1);
|
|
1280
|
+
if ( s2 == 1 ) {
|
|
1281
|
+
ca_size_t inn = ca_chunk_inner_size(ca2);
|
|
1282
|
+
if ( inn > inner ) inner = inn;
|
|
1283
|
+
}
|
|
1284
|
+
if ( s3 == 1 ) {
|
|
1285
|
+
ca_size_t inn = ca_chunk_inner_size(ca3);
|
|
1286
|
+
if ( inn > inner ) inner = inn;
|
|
1287
|
+
}
|
|
1288
|
+
chunk_n = ca_chunk_compute_n(n_total, inner, maxb);
|
|
265
1289
|
}
|
|
266
1290
|
|
|
267
|
-
|
|
268
|
-
|
|
1291
|
+
ca_lazy_arena_enter();
|
|
1292
|
+
|
|
1293
|
+
/* ca1 acquire */
|
|
1294
|
+
if ( s1 == 0 ) {
|
|
1295
|
+
if ( ca_attach_is_alias(ca1) ) {
|
|
1296
|
+
ca_attach(ca1); p1_src = (char *) ca1->ptr; att1 = 1;
|
|
1297
|
+
} else {
|
|
1298
|
+
s1_arena = ca_op_acquire_operand_scratch(ca1, 1);
|
|
1299
|
+
ca_xfer_all(ca1, s1_arena, CA_XFER_GET);
|
|
1300
|
+
p1_src = (char *) s1_arena;
|
|
1301
|
+
}
|
|
1302
|
+
} else if ( ca_attach_is_alias(ca1) ) {
|
|
1303
|
+
ca_attach(ca1); p1_src = (char *) ca1->ptr; att1 = 1;
|
|
1304
|
+
} else {
|
|
1305
|
+
s1_arena = ca_op_acquire_operand_scratch(ca1, chunk_n);
|
|
1306
|
+
p1_src = (char *) s1_arena; gpc1 = 1;
|
|
1307
|
+
}
|
|
1308
|
+
/* ca2 acquire (mirror) */
|
|
1309
|
+
if ( s2 == 0 ) {
|
|
1310
|
+
if ( ca_attach_is_alias(ca2) ) {
|
|
1311
|
+
ca_attach(ca2); p2_src = (char *) ca2->ptr; att2 = 1;
|
|
1312
|
+
} else {
|
|
1313
|
+
s2_arena = ca_op_acquire_operand_scratch(ca2, 1);
|
|
1314
|
+
ca_xfer_all(ca2, s2_arena, CA_XFER_GET);
|
|
1315
|
+
p2_src = (char *) s2_arena;
|
|
1316
|
+
}
|
|
1317
|
+
} else if ( ca_attach_is_alias(ca2) ) {
|
|
1318
|
+
ca_attach(ca2); p2_src = (char *) ca2->ptr; att2 = 1;
|
|
1319
|
+
} else {
|
|
1320
|
+
s2_arena = ca_op_acquire_operand_scratch(ca2, chunk_n);
|
|
1321
|
+
p2_src = (char *) s2_arena; gpc2 = 1;
|
|
1322
|
+
}
|
|
1323
|
+
/* ca3 acquire (mirror) */
|
|
1324
|
+
if ( s3 == 0 ) {
|
|
1325
|
+
if ( ca_attach_is_alias(ca3) ) {
|
|
1326
|
+
ca_attach(ca3); p3_src = (char *) ca3->ptr; att3 = 1;
|
|
1327
|
+
} else {
|
|
1328
|
+
s3_arena = ca_op_acquire_operand_scratch(ca3, 1);
|
|
1329
|
+
ca_xfer_all(ca3, s3_arena, CA_XFER_GET);
|
|
1330
|
+
p3_src = (char *) s3_arena;
|
|
1331
|
+
}
|
|
1332
|
+
} else if ( ca_attach_is_alias(ca3) ) {
|
|
1333
|
+
ca_attach(ca3); p3_src = (char *) ca3->ptr; att3 = 1;
|
|
1334
|
+
} else {
|
|
1335
|
+
s3_arena = ca_op_acquire_operand_scratch(ca3, chunk_n);
|
|
1336
|
+
p3_src = (char *) s3_arena; gpc3 = 1;
|
|
1337
|
+
}
|
|
1338
|
+
|
|
1339
|
+
for ( off = 0; off < n_total; off += chunk_n ) {
|
|
1340
|
+
ca_size_t n_done = (off + chunk_n > n_total) ? n_total - off
|
|
1341
|
+
: chunk_n;
|
|
1342
|
+
char *p1, *p2, *p3;
|
|
1343
|
+
|
|
1344
|
+
if ( gpc1 ) { ca_chunked_gather(ca1, off, n_done, s1_arena);
|
|
1345
|
+
p1 = (char *) s1_arena; }
|
|
1346
|
+
else { p1 = p1_src + (s1 ? off * ca1->bytes : 0); }
|
|
1347
|
+
if ( gpc2 ) { ca_chunked_gather(ca2, off, n_done, s2_arena);
|
|
1348
|
+
p2 = (char *) s2_arena; }
|
|
1349
|
+
else { p2 = p2_src + (s2 ? off * ca2->bytes : 0); }
|
|
1350
|
+
if ( gpc3 ) { ca_chunked_gather(ca3, off, n_done, s3_arena);
|
|
1351
|
+
p3 = (char *) s3_arena; }
|
|
1352
|
+
else { p3 = p3_src + (s3 ? off * ca3->bytes : 0); }
|
|
1353
|
+
|
|
1354
|
+
func[dt](n_done,
|
|
1355
|
+
cao->mask ? ((boolean8_t *) cao->mask->ptr) + off
|
|
1356
|
+
: NULL,
|
|
1357
|
+
p1, s1,
|
|
1358
|
+
p2, s2,
|
|
1359
|
+
p3, s3,
|
|
1360
|
+
(char *) cao->ptr + off * cao->bytes, 1);
|
|
1361
|
+
}
|
|
1362
|
+
|
|
1363
|
+
if ( s3_arena ) ca_lazy_arena_release(s3_arena);
|
|
1364
|
+
if ( s2_arena ) ca_lazy_arena_release(s2_arena);
|
|
1365
|
+
if ( s1_arena ) ca_lazy_arena_release(s1_arena);
|
|
1366
|
+
if ( att3 ) ca_detach(ca3);
|
|
1367
|
+
if ( att2 ) ca_detach(ca2);
|
|
1368
|
+
if ( att1 ) ca_detach(ca1);
|
|
1369
|
+
|
|
1370
|
+
ca_lazy_arena_exit();
|
|
1371
|
+
}
|
|
1372
|
+
}
|
|
1373
|
+
|
|
1374
|
+
return out;
|
|
1375
|
+
}
|
|
1376
|
+
|
|
1377
|
+
/* triop_bang (in-place) driver. ca1 = self = output (write target,
|
|
1378
|
+
attach legit; keep ca_attach + ca_sync); ca2/ca3 = input only (fast/slow
|
|
1379
|
+
dispatch via EAGER_ACQUIRE/RELEASE). */
|
|
1380
|
+
VALUE
|
|
1381
|
+
rb_ca_call_triop_bang (VALUE self, VALUE other2, VALUE other3,
|
|
1382
|
+
ca_triop_func_t func[])
|
|
1383
|
+
{
|
|
1384
|
+
CArray *ca1, *ca2, *ca3;
|
|
1385
|
+
|
|
1386
|
+
rb_ca_modify(self);
|
|
1387
|
+
|
|
1388
|
+
rb_ca_cast_other(&self, &other2);
|
|
1389
|
+
rb_ca_cast_other(&self, &other3);
|
|
1390
|
+
|
|
1391
|
+
TypedData_Get_Struct(self, CArray, &carray_data_type, ca1);
|
|
1392
|
+
TypedData_Get_Struct(other2, CArray, &carray_data_type, ca2);
|
|
1393
|
+
TypedData_Get_Struct(other3, CArray, &carray_data_type, ca3);
|
|
1394
|
+
|
|
1395
|
+
/* self is the write target, so the destination rule applies to each
|
|
1396
|
+
input operand in turn (the same one assignment uses). */
|
|
1397
|
+
if ( ! rb_obj_is_cscalar(other2) ) {
|
|
1398
|
+
ca_broadcast_to_destination(self, &other2);
|
|
1399
|
+
TypedData_Get_Struct(other2, CArray, &carray_data_type, ca2);
|
|
1400
|
+
}
|
|
1401
|
+
if ( ! rb_obj_is_cscalar(other3) ) {
|
|
1402
|
+
ca_broadcast_to_destination(self, &other3);
|
|
1403
|
+
TypedData_Get_Struct(other3, CArray, &carray_data_type, ca3);
|
|
1404
|
+
}
|
|
1405
|
+
|
|
1406
|
+
/* self IS the output (= write target; attach legit per refined invariant) */
|
|
1407
|
+
ca_attach(ca1);
|
|
1408
|
+
ca_mask_overlay_safe(ca1, 3, ca1, ca2, ca3);
|
|
1409
|
+
|
|
1410
|
+
{
|
|
1411
|
+
ca_size_t s2 = rb_obj_is_cscalar(other2) ? 0 : 1;
|
|
1412
|
+
ca_size_t s3 = rb_obj_is_cscalar(other3) ? 0 : 1;
|
|
1413
|
+
|
|
1414
|
+
/* Threshold dispatch on the input-only operands (ca2 / ca3). Self
|
|
1415
|
+
(ca1) IS the output (attached + ca_sync as usual). >= 2 non-alias
|
|
1416
|
+
input arrays → chunked, else 1-shot ALLOCV. */
|
|
1417
|
+
int nonalias_arrays = 0;
|
|
1418
|
+
int use_chunked;
|
|
1419
|
+
if ( s2 == 1 && !ca_attach_is_alias(ca2) ) nonalias_arrays++;
|
|
1420
|
+
if ( s3 == 1 && !ca_attach_is_alias(ca3) ) nonalias_arrays++;
|
|
1421
|
+
use_chunked = (nonalias_arrays >= 2);
|
|
1422
|
+
|
|
1423
|
+
if ( !use_chunked ) {
|
|
1424
|
+
volatile VALUE h2 = Qnil, h3 = Qnil;
|
|
1425
|
+
char *p2, *p3;
|
|
1426
|
+
int attached2, attached3;
|
|
1427
|
+
(void) h2; (void) h3;
|
|
1428
|
+
|
|
1429
|
+
EAGER_ACQUIRE_INPUT(ca2, p2, h2, attached2);
|
|
1430
|
+
EAGER_ACQUIRE_INPUT(ca3, p3, h3, attached3);
|
|
1431
|
+
|
|
1432
|
+
func[ca1->data_type](ca1->elements,
|
|
269
1433
|
( ca1->mask ) ? (boolean8_t *) ca1->mask->ptr : NULL,
|
|
270
1434
|
ca1->ptr, 1,
|
|
271
|
-
|
|
1435
|
+
p2, s2,
|
|
1436
|
+
p3, s3,
|
|
272
1437
|
ca1->ptr, 1);
|
|
1438
|
+
|
|
1439
|
+
EAGER_RELEASE_INPUT(ca3, h3, attached3);
|
|
1440
|
+
EAGER_RELEASE_INPUT(ca2, h2, attached2);
|
|
273
1441
|
}
|
|
1442
|
+
else {
|
|
1443
|
+
/* CHUNKED PATH: both ca2 and ca3 non-alias arrays. ca1 (= self =
|
|
1444
|
+
output) is already attached; its ptr is contig (= ca_attach
|
|
1445
|
+
materialise + alias for entity, or full materialise for view).
|
|
1446
|
+
Write to ca1->ptr + off*bytes in chunks; sync at end. */
|
|
1447
|
+
void *s2_arena = NULL, *s3_arena = NULL;
|
|
1448
|
+
int8_t dt = ca1->data_type;
|
|
1449
|
+
ca_size_t chunk_n;
|
|
1450
|
+
ca_size_t off;
|
|
1451
|
+
ca_size_t n_total = ca1->elements;
|
|
1452
|
+
|
|
1453
|
+
{
|
|
1454
|
+
ca_size_t inner = ca_chunk_inner_size(ca1);
|
|
1455
|
+
ca_size_t inn2 = ca_chunk_inner_size(ca2);
|
|
1456
|
+
ca_size_t inn3 = ca_chunk_inner_size(ca3);
|
|
1457
|
+
ca_size_t maxb = ca1->bytes;
|
|
1458
|
+
if ( ca2->bytes > maxb ) maxb = ca2->bytes;
|
|
1459
|
+
if ( ca3->bytes > maxb ) maxb = ca3->bytes;
|
|
1460
|
+
if ( inn2 > inner ) inner = inn2;
|
|
1461
|
+
if ( inn3 > inner ) inner = inn3;
|
|
1462
|
+
chunk_n = ca_chunk_compute_n(n_total, inner, maxb);
|
|
1463
|
+
}
|
|
274
1464
|
|
|
1465
|
+
ca_lazy_arena_enter();
|
|
1466
|
+
s2_arena = ca_op_acquire_operand_scratch(ca2, chunk_n);
|
|
1467
|
+
s3_arena = ca_op_acquire_operand_scratch(ca3, chunk_n);
|
|
1468
|
+
|
|
1469
|
+
for ( off = 0; off < n_total; off += chunk_n ) {
|
|
1470
|
+
ca_size_t n_done = (off + chunk_n > n_total) ? n_total - off
|
|
1471
|
+
: chunk_n;
|
|
1472
|
+
ca_chunked_gather(ca2, off, n_done, s2_arena);
|
|
1473
|
+
ca_chunked_gather(ca3, off, n_done, s3_arena);
|
|
1474
|
+
|
|
1475
|
+
func[dt](n_done,
|
|
1476
|
+
ca1->mask ? ((boolean8_t *) ca1->mask->ptr) + off
|
|
1477
|
+
: NULL,
|
|
1478
|
+
(char *) ca1->ptr + off * ca1->bytes, 1,
|
|
1479
|
+
(char *) s2_arena, s2,
|
|
1480
|
+
(char *) s3_arena, s3,
|
|
1481
|
+
(char *) ca1->ptr + off * ca1->bytes, 1);
|
|
1482
|
+
}
|
|
1483
|
+
|
|
1484
|
+
ca_lazy_arena_release(s3_arena);
|
|
1485
|
+
ca_lazy_arena_release(s2_arena);
|
|
1486
|
+
ca_lazy_arena_exit();
|
|
1487
|
+
}
|
|
275
1488
|
}
|
|
276
1489
|
|
|
277
1490
|
ca_sync(ca1);
|
|
278
|
-
|
|
1491
|
+
ca_detach(ca1);
|
|
279
1492
|
|
|
280
1493
|
return self;
|
|
281
1494
|
}
|
|
282
1495
|
|
|
1496
|
+
/* moncmp driver. ca1 input-only (EAGER_ACQUIRE/RELEASE fast/slow), ca2 =
|
|
1497
|
+
new boolean entity output (attach legit). */
|
|
283
1498
|
VALUE
|
|
284
1499
|
rb_ca_call_moncmp (VALUE self, ca_moncmp_func_t func[])
|
|
285
1500
|
{
|
|
@@ -297,30 +1512,43 @@ rb_ca_call_moncmp (VALUE self, ca_moncmp_func_t func[])
|
|
|
297
1512
|
|
|
298
1513
|
TypedData_Get_Struct(out, CArray, &carray_data_type, ca2);
|
|
299
1514
|
|
|
300
|
-
|
|
301
|
-
ca_copy_mask_overlay(ca2, ca2->elements, 1, ca1);
|
|
302
|
-
func[ca1->data_type](ca1->elements,
|
|
303
|
-
( ca2->mask ) ? (boolean8_t *) ca2->mask->ptr : NULL,
|
|
304
|
-
ca1->ptr, 1,
|
|
305
|
-
(boolean8_t *) ca2->ptr, 1);
|
|
306
|
-
ca_detach(ca1);
|
|
1515
|
+
ca_mask_overlay_safe(ca2, 1, ca1);
|
|
307
1516
|
|
|
308
|
-
/*
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
1517
|
+
/* The kernel's masked branch skips masked cells, so they would keep the
|
|
1518
|
+
uninitialised data from rb_carray_new. Zero the output when a mask is
|
|
1519
|
+
present so masked cells read as 0 (matching binop's ca_template_safe
|
|
1520
|
+
convention) instead of exposing uninitialised memory. */
|
|
1521
|
+
if ( ca2->mask ) {
|
|
1522
|
+
MEMZERO(ca2->ptr, char, ca2->elements * ca2->bytes);
|
|
1523
|
+
}
|
|
1524
|
+
|
|
1525
|
+
{
|
|
1526
|
+
volatile VALUE h1 = Qnil;
|
|
1527
|
+
char *p1;
|
|
1528
|
+
int attached1;
|
|
1529
|
+
(void) h1;
|
|
1530
|
+
|
|
1531
|
+
EAGER_ACQUIRE_INPUT(ca1, p1, h1, attached1);
|
|
1532
|
+
func[ca1->data_type](ca1->elements,
|
|
1533
|
+
( ca2->mask ) ? (boolean8_t *) ca2->mask->ptr : NULL,
|
|
1534
|
+
p1, 1,
|
|
1535
|
+
(boolean8_t *) ca2->ptr, 1);
|
|
1536
|
+
EAGER_RELEASE_INPUT(ca1, h1, attached1);
|
|
312
1537
|
}
|
|
313
1538
|
|
|
314
1539
|
return out;
|
|
315
1540
|
}
|
|
316
1541
|
|
|
317
1542
|
|
|
318
|
-
|
|
319
|
-
|
|
1543
|
+
/* ca_bincmp_eq / ca_bincmp_ne are declared (correctly typed) in
|
|
1544
|
+
ca_bincmp_dispatch.h, reached via the carray.h umbrella. The
|
|
1545
|
+
UNDEF-comparison identity checks below cast to ca_bincmp_func_t
|
|
1546
|
+
explicitly. */
|
|
320
1547
|
|
|
321
1548
|
VALUE
|
|
322
1549
|
rb_ca_call_bincmp (volatile VALUE self, volatile VALUE other,
|
|
323
|
-
ca_bincmp_func_t func[]
|
|
1550
|
+
ca_bincmp_func_t func[],
|
|
1551
|
+
double tol)
|
|
324
1552
|
{
|
|
325
1553
|
volatile VALUE out = Qnil;
|
|
326
1554
|
CArray *ca1, *ca2, *ca3; /* ca3 = ca1.op(ca2) */
|
|
@@ -338,74 +1566,145 @@ rb_ca_call_bincmp (volatile VALUE self, volatile VALUE other,
|
|
|
338
1566
|
}
|
|
339
1567
|
}
|
|
340
1568
|
|
|
1569
|
+
/* Face gate: an ORDERABLE Face over numeric storage descends to storage
|
|
1570
|
+
(fixing the surface-fixlen memcmp mis-order) and reconciles a Face RHS
|
|
1571
|
+
via to_comparable (e.g. unit alignment). No-op for non-Face self and
|
|
1572
|
+
for fixlen-storage Faces (memcmp is already correct there). */
|
|
1573
|
+
ca_face_reconcile_comparison(&self, &other);
|
|
1574
|
+
|
|
341
1575
|
/* do implicit casting and resolving unbound repeat array */
|
|
342
1576
|
rb_ca_cast_self_or_other(&self, &other);
|
|
343
1577
|
|
|
344
1578
|
TypedData_Get_Struct(self, CArray, &carray_data_type, ca1);
|
|
345
1579
|
TypedData_Get_Struct(other, CArray, &carray_data_type, ca2);
|
|
346
1580
|
|
|
347
|
-
|
|
1581
|
+
/* Same fast/slow shape as rb_ca_call_binop, but the output data_type is
|
|
1582
|
+
fixed boolean (rb_ca_call_binop's output data_type matches its input). */
|
|
1583
|
+
{
|
|
1584
|
+
int self_is_scalar = RTEST(rb_obj_is_cscalar(self));
|
|
1585
|
+
int other_is_scalar = RTEST(rb_obj_is_cscalar(other));
|
|
1586
|
+
ca_size_t n_kernel, i1, i2, i3;
|
|
348
1587
|
|
|
349
|
-
|
|
350
|
-
if ( rb_obj_is_cscalar(self) ) {
|
|
351
|
-
if ( rb_obj_is_cscalar(other) ) { /* scalar vs scalar */
|
|
1588
|
+
if ( self_is_scalar && other_is_scalar ) {
|
|
352
1589
|
out = rb_cscalar_new(CA_BOOLEAN, 0, NULL);
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
ca_copy_mask_overlay(ca3, ca3->elements, 2, ca1, ca2);
|
|
356
|
-
func[ca1->data_type](ca1->elements,
|
|
357
|
-
( ca3->mask ) ? (boolean8_t *) ca3->mask->ptr : NULL,
|
|
358
|
-
ca1->ptr, ca1->bytes, 0,
|
|
359
|
-
ca2->ptr, ca2->bytes, 0,
|
|
360
|
-
ca3->ptr, ca3->bytes, 0);
|
|
1590
|
+
n_kernel = ca1->elements; i1 = 0; i2 = 0; i3 = 0;
|
|
361
1591
|
}
|
|
362
|
-
else
|
|
1592
|
+
else if ( self_is_scalar /* && !other_is_scalar */ ) {
|
|
363
1593
|
out = rb_carray_new(CA_BOOLEAN, ca2->ndim, ca2->dim, 0, NULL);
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
ca_copy_mask_overlay(ca3, ca3->elements, 2, ca1, ca2);
|
|
367
|
-
func[ca1->data_type](ca2->elements,
|
|
368
|
-
( ca3->mask ) ? (boolean8_t *) ca3->mask->ptr : NULL,
|
|
369
|
-
ca1->ptr, ca1->bytes, 0,
|
|
370
|
-
ca2->ptr, ca2->bytes, 1,
|
|
371
|
-
ca3->ptr, ca3->bytes, 1);
|
|
1594
|
+
n_kernel = ca2->elements; i1 = 0; i2 = 1; i3 = 1;
|
|
372
1595
|
}
|
|
373
|
-
|
|
374
|
-
else {
|
|
375
|
-
if ( rb_obj_is_cscalar(other) ) { /* array vs scalar */
|
|
1596
|
+
else if ( other_is_scalar /* && !self_is_scalar */ ) {
|
|
376
1597
|
out = rb_carray_new(CA_BOOLEAN, ca1->ndim, ca1->dim, 0, NULL);
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
ca_copy_mask_overlay(ca3, ca3->elements, 2, ca1, ca2);
|
|
380
|
-
func[ca1->data_type](ca1->elements,
|
|
381
|
-
( ca3->mask ) ? (boolean8_t *) ca3->mask->ptr : NULL,
|
|
382
|
-
ca1->ptr, ca1->bytes, 1,
|
|
383
|
-
ca2->ptr, ca2->bytes, 0,
|
|
384
|
-
ca3->ptr, ca3->bytes, 1);
|
|
1598
|
+
n_kernel = ca1->elements; i1 = 1; i2 = 0; i3 = 1;
|
|
385
1599
|
}
|
|
386
|
-
else {
|
|
1600
|
+
else {
|
|
387
1601
|
if ( ca1->elements != ca2->elements ) {
|
|
388
|
-
rb_raise(rb_eRuntimeError, "elements mismatch in bincmp (%
|
|
389
|
-
|
|
1602
|
+
rb_raise(rb_eRuntimeError, "elements mismatch in bincmp (%" PRId64 " <-> %" PRId64 ")",
|
|
1603
|
+
(ca_size_t) ca1->elements,
|
|
1604
|
+
(ca_size_t) ca2->elements);
|
|
390
1605
|
}
|
|
391
1606
|
out = rb_carray_new(CA_BOOLEAN, ca1->ndim, ca1->dim, 0, NULL);
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
ca_copy_mask_overlay(ca3, ca3->elements, 2, ca1, ca2);
|
|
395
|
-
func[ca1->data_type](ca1->elements,
|
|
396
|
-
( ca3->mask ) ? (boolean8_t *) ca3->mask->ptr : NULL,
|
|
397
|
-
ca1->ptr, ca1->bytes, 1,
|
|
398
|
-
ca2->ptr, ca2->bytes, 1,
|
|
399
|
-
ca3->ptr, ca3->bytes, 1);
|
|
1607
|
+
n_kernel = ca1->elements; i1 = 1; i2 = 1; i3 = 1;
|
|
400
1608
|
}
|
|
401
|
-
|
|
1609
|
+
TypedData_Get_Struct(out, CArray, &carray_data_type, ca3);
|
|
1610
|
+
|
|
1611
|
+
ca_mask_overlay_safe(ca3, 2, ca1, ca2);
|
|
402
1612
|
|
|
403
|
-
|
|
1613
|
+
/* The kernel's masked branch skips masked cells, so they would keep the
|
|
1614
|
+
uninitialised data from rb_carray_new. Zero the output when a mask is
|
|
1615
|
+
present so masked cells read as 0 (matching binop's ca_template_safe
|
|
1616
|
+
convention) instead of exposing uninitialised memory. */
|
|
1617
|
+
if ( ca3->mask ) {
|
|
1618
|
+
MEMZERO(ca3->ptr, char, ca3->elements * ca3->bytes);
|
|
1619
|
+
}
|
|
404
1620
|
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
1621
|
+
/* SAME-OPERAND SHARING: prevents materialising the same view twice in
|
|
1622
|
+
cases like `view < view`. */
|
|
1623
|
+
if ( ca1 == ca2 && !ca_attach_is_alias(ca1) ) {
|
|
1624
|
+
volatile VALUE h_shared = Qnil;
|
|
1625
|
+
char *p_shared;
|
|
1626
|
+
(void) h_shared;
|
|
1627
|
+
p_shared = ALLOCV_N(char, h_shared, ca1->elements * ca1->bytes);
|
|
1628
|
+
ca_xfer_all(ca1, p_shared, CA_XFER_GET);
|
|
1629
|
+
func[ca1->data_type](n_kernel,
|
|
1630
|
+
( ca3->mask ) ? (boolean8_t *) ca3->mask->ptr : NULL,
|
|
1631
|
+
p_shared, ca1->bytes, i1,
|
|
1632
|
+
p_shared, ca2->bytes, i2,
|
|
1633
|
+
ca3->ptr, ca3->bytes, i3,
|
|
1634
|
+
tol);
|
|
1635
|
+
ALLOCV_END(h_shared);
|
|
1636
|
+
}
|
|
1637
|
+
else {
|
|
1638
|
+
/* Binop-style threshold dispatch + chunked path for bincmp: only
|
|
1639
|
+
when both operands need per-region gather (both non-alias array)
|
|
1640
|
+
do we use the chunked path; else 1-shot ALLOCV for the single
|
|
1641
|
+
non-alias operand. */
|
|
1642
|
+
int nonalias_arrays = 0;
|
|
1643
|
+
int use_chunked;
|
|
1644
|
+
if ( i1 == 1 && !ca_attach_is_alias(ca1) ) nonalias_arrays++;
|
|
1645
|
+
if ( i2 == 1 && !ca_attach_is_alias(ca2) ) nonalias_arrays++;
|
|
1646
|
+
use_chunked = (nonalias_arrays >= 2);
|
|
1647
|
+
|
|
1648
|
+
if ( !use_chunked ) {
|
|
1649
|
+
volatile VALUE h1 = Qnil, h2 = Qnil;
|
|
1650
|
+
char *p1, *p2;
|
|
1651
|
+
int attached1, attached2;
|
|
1652
|
+
(void) h1; (void) h2;
|
|
1653
|
+
|
|
1654
|
+
EAGER_ACQUIRE_INPUT(ca1, p1, h1, attached1);
|
|
1655
|
+
EAGER_ACQUIRE_INPUT(ca2, p2, h2, attached2);
|
|
1656
|
+
|
|
1657
|
+
func[ca1->data_type](n_kernel,
|
|
1658
|
+
( ca3->mask ) ? (boolean8_t *) ca3->mask->ptr : NULL,
|
|
1659
|
+
p1, ca1->bytes, i1,
|
|
1660
|
+
p2, ca2->bytes, i2,
|
|
1661
|
+
ca3->ptr, ca3->bytes, i3,
|
|
1662
|
+
tol);
|
|
1663
|
+
|
|
1664
|
+
EAGER_RELEASE_INPUT(ca2, h2, attached2);
|
|
1665
|
+
EAGER_RELEASE_INPUT(ca1, h1, attached1);
|
|
1666
|
+
}
|
|
1667
|
+
else {
|
|
1668
|
+
/* CHUNKED PATH (both operands non-alias array): per-chunk gather
|
|
1669
|
+
into arena scratch. Mirrors the binop chunked branch. */
|
|
1670
|
+
void *s1_arena = NULL, *s2_arena = NULL;
|
|
1671
|
+
ca_size_t b1 = ca1->bytes, b2 = ca2->bytes, b3 = ca3->bytes;
|
|
1672
|
+
int8_t dt = ca1->data_type;
|
|
1673
|
+
ca_size_t chunk_n, off;
|
|
1674
|
+
|
|
1675
|
+
{
|
|
1676
|
+
ca_size_t inner1 = ca_chunk_inner_size(ca1);
|
|
1677
|
+
ca_size_t inner2 = ca_chunk_inner_size(ca2);
|
|
1678
|
+
ca_size_t inner = inner1 > inner2 ? inner1 : inner2;
|
|
1679
|
+
ca_size_t maxb = b1 > b2 ? b1 : b2;
|
|
1680
|
+
if ( b3 > maxb ) maxb = b3;
|
|
1681
|
+
chunk_n = ca_chunk_compute_n(n_kernel, inner, maxb);
|
|
1682
|
+
}
|
|
1683
|
+
|
|
1684
|
+
ca_lazy_arena_enter();
|
|
1685
|
+
s1_arena = ca_op_acquire_operand_scratch(ca1, chunk_n);
|
|
1686
|
+
s2_arena = ca_op_acquire_operand_scratch(ca2, chunk_n);
|
|
1687
|
+
|
|
1688
|
+
for ( off = 0; off < n_kernel; off += chunk_n ) {
|
|
1689
|
+
ca_size_t n_done = (off + chunk_n > n_kernel) ? n_kernel - off
|
|
1690
|
+
: chunk_n;
|
|
1691
|
+
ca_chunked_gather(ca1, off, n_done, s1_arena);
|
|
1692
|
+
ca_chunked_gather(ca2, off, n_done, s2_arena);
|
|
1693
|
+
|
|
1694
|
+
func[dt](n_done,
|
|
1695
|
+
ca3->mask ? ((boolean8_t *) ca3->mask->ptr) + off
|
|
1696
|
+
: NULL,
|
|
1697
|
+
(char *) s1_arena, b1, i1,
|
|
1698
|
+
(char *) s2_arena, b2, i2,
|
|
1699
|
+
(char *) ca3->ptr + off * b3, b3, i3,
|
|
1700
|
+
tol);
|
|
1701
|
+
}
|
|
1702
|
+
|
|
1703
|
+
ca_lazy_arena_release(s2_arena);
|
|
1704
|
+
ca_lazy_arena_release(s1_arena);
|
|
1705
|
+
ca_lazy_arena_exit();
|
|
1706
|
+
}
|
|
1707
|
+
}
|
|
409
1708
|
}
|
|
410
1709
|
|
|
411
1710
|
return out;
|
|
@@ -421,15 +1720,26 @@ ca_monop_not_implement(ca_size_t n, boolean8_t *m,
|
|
|
421
1720
|
}
|
|
422
1721
|
|
|
423
1722
|
void
|
|
424
|
-
ca_binop_not_implement(ca_size_t n, boolean8_t *m,
|
|
425
|
-
char *ptr1, ca_size_t i1,
|
|
426
|
-
char *ptr2, ca_size_t i2,
|
|
1723
|
+
ca_binop_not_implement(ca_size_t n, boolean8_t *m,
|
|
1724
|
+
char *ptr1, ca_size_t i1,
|
|
1725
|
+
char *ptr2, ca_size_t i2,
|
|
427
1726
|
char *ptr3, ca_size_t i3)
|
|
428
1727
|
{
|
|
429
1728
|
rb_raise(rb_eCADataTypeError,
|
|
430
1729
|
"invalid data_type for binop (not implemented)");
|
|
431
1730
|
}
|
|
432
1731
|
|
|
1732
|
+
void
|
|
1733
|
+
ca_triop_not_implement(ca_size_t n, boolean8_t *m,
|
|
1734
|
+
char *ptr1, ca_size_t i1,
|
|
1735
|
+
char *ptr2, ca_size_t i2,
|
|
1736
|
+
char *ptr3, ca_size_t i3,
|
|
1737
|
+
char *ptr4, ca_size_t i4)
|
|
1738
|
+
{
|
|
1739
|
+
rb_raise(rb_eCADataTypeError,
|
|
1740
|
+
"invalid data_type for triop (not implemented)");
|
|
1741
|
+
}
|
|
1742
|
+
|
|
433
1743
|
void
|
|
434
1744
|
ca_moncmp_not_implement(ca_size_t n, boolean8_t *m,
|
|
435
1745
|
char *ptr1, ca_size_t i1,
|
|
@@ -440,10 +1750,11 @@ ca_moncmp_not_implement(ca_size_t n, boolean8_t *m,
|
|
|
440
1750
|
}
|
|
441
1751
|
|
|
442
1752
|
void
|
|
443
|
-
ca_bincmp_not_implement (ca_size_t n, boolean8_t *m,
|
|
444
|
-
char *ptr1, ca_size_t b1, ca_size_t i1,
|
|
445
|
-
char *ptr2, ca_size_t b2, ca_size_t i2,
|
|
446
|
-
char *ptr3, ca_size_t b3, ca_size_t i3
|
|
1753
|
+
ca_bincmp_not_implement (ca_size_t n, boolean8_t *m,
|
|
1754
|
+
char *ptr1, ca_size_t b1, ca_size_t i1,
|
|
1755
|
+
char *ptr2, ca_size_t b2, ca_size_t i2,
|
|
1756
|
+
char *ptr3, ca_size_t b3, ca_size_t i3,
|
|
1757
|
+
double tol)
|
|
447
1758
|
{
|
|
448
1759
|
rb_raise(rb_eTypeError, "invalid data_type for bincmp (not implemented)");
|
|
449
1760
|
}
|
|
@@ -455,7 +1766,7 @@ ca_math_call (VALUE mod, VALUE arg, ID id)
|
|
|
455
1766
|
return rb_funcall(arg, id, 0);
|
|
456
1767
|
}
|
|
457
1768
|
#ifdef HAVE_COMPLEX_H
|
|
458
|
-
else if (
|
|
1769
|
+
else if ( RB_TYPE_P(arg, T_COMPLEX) ) {
|
|
459
1770
|
if ( rb_respond_to(arg, id) ) {
|
|
460
1771
|
return rb_funcall(arg, id, 0);
|
|
461
1772
|
}
|
|
@@ -478,7 +1789,7 @@ ca_math_call (VALUE mod, VALUE arg, ID id)
|
|
|
478
1789
|
}
|
|
479
1790
|
}
|
|
480
1791
|
|
|
481
|
-
/* @overload
|
|
1792
|
+
/* @overload coerce (other)
|
|
482
1793
|
|
|
483
1794
|
[TBD]
|
|
484
1795
|
*/
|
|
@@ -486,15 +1797,9 @@ ca_math_call (VALUE mod, VALUE arg, ID id)
|
|
|
486
1797
|
static VALUE
|
|
487
1798
|
rb_ca_coerce (VALUE self, VALUE other)
|
|
488
1799
|
{
|
|
489
|
-
CArray *ca;
|
|
490
|
-
TypedData_Get_Struct(self, CArray, &carray_data_type, ca);
|
|
491
|
-
|
|
492
1800
|
if ( rb_obj_is_carray(other) ) {
|
|
493
1801
|
return Qnil;
|
|
494
1802
|
}
|
|
495
|
-
else if ( rb_respond_to(other, rb_intern("ca")) ) {
|
|
496
|
-
return rb_ca_coerce(self, rb_funcall(other,rb_intern("ca"),0));
|
|
497
|
-
}
|
|
498
1803
|
else if ( rb_respond_to(other, rb_intern("to_ca")) ) {
|
|
499
1804
|
return rb_ca_coerce(self, rb_funcall(other,rb_intern("to_ca"),0));
|
|
500
1805
|
}
|
|
@@ -506,123 +1811,23 @@ rb_ca_coerce (VALUE self, VALUE other)
|
|
|
506
1811
|
}
|
|
507
1812
|
|
|
508
1813
|
|
|
509
|
-
/* CArray#mul_add
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
ca_size_t count = 0; \
|
|
519
|
-
ca_size_t i; \
|
|
520
|
-
ca_set_iterator(1, cw, &p2, &s2); \
|
|
521
|
-
if ( m ) { \
|
|
522
|
-
count = 0; \
|
|
523
|
-
for (i=ca->elements; i; i--, p1++, p2+=s2) { \
|
|
524
|
-
if ( ! *m++ ) { \
|
|
525
|
-
sum += (type)conv(*p1) * (type)conv(*p2); \
|
|
526
|
-
} \
|
|
527
|
-
else { \
|
|
528
|
-
count++; \
|
|
529
|
-
} \
|
|
530
|
-
} \
|
|
531
|
-
} \
|
|
532
|
-
else { \
|
|
533
|
-
for (i=ca->elements; i; i--, p1++, p2+=s2) { \
|
|
534
|
-
sum += (type)conv(*p1) * (type)conv(*p2); \
|
|
535
|
-
} \
|
|
536
|
-
} \
|
|
537
|
-
if ( ( ! NIL_P(rmin_count) ) && count > min_count ) { \
|
|
538
|
-
out = ( NIL_P(rfval) ) ? CA_UNDEF : rfval;\
|
|
539
|
-
} \
|
|
540
|
-
else { \
|
|
541
|
-
out = to(sum); \
|
|
542
|
-
} \
|
|
543
|
-
}
|
|
544
|
-
|
|
545
|
-
/* @overload mul_add (weight, min_count=nil, fill_value=nil)
|
|
546
|
-
|
|
547
|
-
[TBD]
|
|
1814
|
+
/* CArray#mul_add was retired in 3.0 — superseded by `wsum` (mkkernel
|
|
1815
|
+
array_arg reduction, ext/mkkernel.rb). `wsum` is the strict superset:
|
|
1816
|
+
- f64 accumulator (overflow-safe for integer input)
|
|
1817
|
+
- per-axis (`a.wsum(w, axis)`)
|
|
1818
|
+
- kernel_iterator universal dispatch (= mask + lazy operand)
|
|
1819
|
+
- 3.0-unified min_count semantic ("min valid required").
|
|
1820
|
+
Migration: a.mul_add(b) -> a.wsum(b)
|
|
1821
|
+
a.mul_add(b, mc, fill) -> a.wsum(b, min_count: mc,
|
|
1822
|
+
fill_value: fill)
|
|
548
1823
|
*/
|
|
549
1824
|
|
|
550
|
-
static VALUE
|
|
551
|
-
rb_ca_mul_add (int argc, VALUE *argv, volatile VALUE self)
|
|
552
|
-
{
|
|
553
|
-
volatile VALUE out;
|
|
554
|
-
volatile VALUE weight = Qnil;
|
|
555
|
-
volatile VALUE rmin_count = Qnil;
|
|
556
|
-
volatile VALUE rfval = Qnil;
|
|
557
|
-
CArray *ca, *cw;
|
|
558
|
-
boolean8_t *mi = NULL;
|
|
559
|
-
ca_size_t min_count;
|
|
560
|
-
|
|
561
|
-
/* FIXME: to parse :mask_limit, :fill_value */
|
|
562
|
-
rb_scan_args(argc, argv, "12", (VALUE *) &weight, (VALUE *) &rmin_count, (VALUE *) &rfval);
|
|
563
|
-
|
|
564
|
-
/* do implicit casting and resolving unbound repeat array */
|
|
565
|
-
rb_ca_cast_self_or_other(&self, &weight);
|
|
566
|
-
|
|
567
|
-
TypedData_Get_Struct(self, CArray, &carray_data_type, ca);
|
|
568
|
-
TypedData_Get_Struct(weight, CArray, &carray_data_type, cw);
|
|
569
|
-
|
|
570
|
-
/* checking elements and data_type */
|
|
571
|
-
ca_check_same_elements(ca, cw);
|
|
572
|
-
ca_check_same_data_type(ca, cw);
|
|
573
|
-
|
|
574
|
-
if ( ca->elements == 0 ) {
|
|
575
|
-
return ( NIL_P(rfval) ) ? CA_UNDEF : rfval;
|
|
576
|
-
}
|
|
577
|
-
|
|
578
|
-
if ( ca_has_mask(ca) || ca_has_mask(cw) ) {
|
|
579
|
-
mi = ca_allocate_mask_iterator(2, ca, cw);
|
|
580
|
-
}
|
|
581
|
-
|
|
582
|
-
min_count = ( NIL_P(rmin_count) || ( ! mi ) ) ?
|
|
583
|
-
ca->elements - 1 : NUM2SIZE(rmin_count);
|
|
584
|
-
|
|
585
|
-
if ( min_count < 0 ) {
|
|
586
|
-
min_count += ca->elements;
|
|
587
|
-
}
|
|
588
|
-
|
|
589
|
-
ca_attach_n(2, ca, cw);
|
|
590
|
-
|
|
591
|
-
switch ( ca->data_type ) {
|
|
592
|
-
case CA_INT8: proc_mul_add(int8_t, ,LONG2NUM); break;
|
|
593
|
-
case CA_UINT8: proc_mul_add(uint8_t,,ULONG2NUM); break;
|
|
594
|
-
case CA_INT16: proc_mul_add(int16_t,,LONG2NUM); break;
|
|
595
|
-
case CA_UINT16: proc_mul_add(uint16_t,,ULONG2NUM); break;
|
|
596
|
-
case CA_INT32: proc_mul_add(int32_t,,LONG2NUM); break;
|
|
597
|
-
case CA_UINT32: proc_mul_add(uint32_t,,ULONG2NUM); break;
|
|
598
|
-
case CA_INT64: proc_mul_add(int64_t,,LL2NUM); break;
|
|
599
|
-
case CA_UINT64: proc_mul_add(uint64_t,,ULL2NUM); break;
|
|
600
|
-
case CA_FLOAT32: proc_mul_add(float32_t,,rb_float_new); break;
|
|
601
|
-
case CA_FLOAT64: proc_mul_add(float64_t,,rb_float_new); break;
|
|
602
|
-
case CA_FLOAT128: proc_mul_add(float128_t,,rb_float_new); break;
|
|
603
|
-
#ifdef HAVE_COMPLEX_H
|
|
604
|
-
case CA_CMPLX64: proc_mul_add(cmplx64_t,,rb_ccomplex_new); break;
|
|
605
|
-
case CA_CMPLX128: proc_mul_add(cmplx128_t,,rb_ccomplex_new); break;
|
|
606
|
-
case CA_CMPLX256: proc_mul_add(cmplx256_t,,rb_ccomplex_new); break;
|
|
607
|
-
#endif
|
|
608
|
-
/* case CA_OBJECT: proc_mul_add(VALUE,NUM2DBL,rb_float_new); break; */
|
|
609
|
-
default: rb_raise(rb_eCADataTypeError, "invalid data type");
|
|
610
|
-
}
|
|
611
|
-
|
|
612
|
-
ca_detach_n(2, ca, cw);
|
|
613
|
-
|
|
614
|
-
free(mi);
|
|
615
|
-
|
|
616
|
-
return out;
|
|
617
|
-
}
|
|
618
|
-
|
|
619
1825
|
void
|
|
620
|
-
Init_carray_operator ()
|
|
1826
|
+
Init_carray_operator (void)
|
|
621
1827
|
{
|
|
622
1828
|
rb_mCAMath = rb_define_module("CAMath");
|
|
623
1829
|
|
|
624
1830
|
rb_define_method(rb_cCArray, "coerce", rb_ca_coerce, 1);
|
|
625
|
-
rb_define_method(rb_cCArray, "mul_add", rb_ca_mul_add, -1);
|
|
626
1831
|
}
|
|
627
1832
|
|
|
628
1833
|
|