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