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.
Files changed (339) hide show
  1. checksums.yaml +4 -4
  2. data/.yardopts +5 -25
  3. data/CHANGELOG.md +16 -0
  4. data/LICENSE +1 -1
  5. data/NEWS.md +3 -0
  6. data/README.md +128 -44
  7. data/carray.gemspec +22 -24
  8. data/ext/ca_array_pool.c +91 -0
  9. data/ext/ca_axis_descriptor.h +186 -0
  10. data/ext/ca_axis_dispatch.c +924 -0
  11. data/ext/ca_axis_group.c +1208 -0
  12. data/ext/ca_bincmp_dispatch.c +76 -0
  13. data/ext/ca_bincmp_dispatch.h +85 -0
  14. data/ext/ca_binop_dispatch.c +125 -0
  15. data/ext/ca_binop_dispatch.h +159 -0
  16. data/ext/ca_categorical_iterator.c +1375 -0
  17. data/ext/ca_compare.c +94 -0
  18. data/ext/ca_compare.h +26 -0
  19. data/ext/ca_composite_dispatch.c +414 -0
  20. data/ext/ca_composite_dispatch.h +116 -0
  21. data/ext/ca_for_buffer.h +96 -0
  22. data/ext/ca_for_each_element.h +241 -0
  23. data/ext/ca_group_iter.c +304 -0
  24. data/ext/ca_iter_substrate.h +325 -0
  25. data/ext/ca_kernel_iterator.c +4321 -0
  26. data/ext/ca_kernel_iterator.h +2603 -0
  27. data/ext/ca_moncmp_dispatch.c +37 -0
  28. data/ext/ca_moncmp_dispatch.h +62 -0
  29. data/ext/ca_monop_dispatch.c +200 -0
  30. data/ext/ca_monop_dispatch.h +235 -0
  31. data/ext/ca_obj_array.c +355 -359
  32. data/ext/ca_obj_bincmp.c +809 -0
  33. data/ext/ca_obj_binop.c +892 -0
  34. data/ext/ca_obj_bitarray.c +369 -164
  35. data/ext/ca_obj_bitfield.c +294 -234
  36. data/ext/ca_obj_block.c +189 -711
  37. data/ext/ca_obj_byte_swap.c +766 -0
  38. data/ext/ca_obj_const_string.c +965 -0
  39. data/ext/ca_obj_face.c +670 -0
  40. data/ext/ca_obj_face.h +247 -0
  41. data/ext/ca_obj_fake.c +228 -100
  42. data/ext/ca_obj_farray.c +54 -441
  43. data/ext/ca_obj_field.c +82 -529
  44. data/ext/ca_obj_fixlen_string.c +306 -0
  45. data/ext/ca_obj_grid.c +858 -440
  46. data/ext/ca_obj_meld.c +1034 -0
  47. data/ext/ca_obj_moncmp.c +569 -0
  48. data/ext/ca_obj_monop.c +1111 -0
  49. data/ext/ca_obj_object.c +774 -298
  50. data/ext/ca_obj_record.c +468 -0
  51. data/ext/ca_obj_reduce.c +97 -82
  52. data/ext/ca_obj_refer.c +569 -459
  53. data/ext/ca_obj_remap.c +475 -0
  54. data/ext/ca_obj_repeat.c +92 -477
  55. data/ext/ca_obj_roll.c +616 -0
  56. data/ext/ca_obj_select.c +344 -296
  57. data/ext/ca_obj_select_axis.c +1296 -0
  58. data/ext/ca_obj_shift.c +230 -792
  59. data/ext/ca_obj_source.c +78 -0
  60. data/ext/ca_obj_stack.c +1173 -0
  61. data/ext/ca_obj_stride.c +2501 -0
  62. data/ext/ca_obj_string.c +268 -0
  63. data/ext/ca_obj_tile.c +614 -0
  64. data/ext/ca_obj_time.c +546 -0
  65. data/ext/ca_obj_timedelta.c +435 -0
  66. data/ext/ca_obj_transpose.c +62 -516
  67. data/ext/ca_obj_triop.c +746 -0
  68. data/ext/ca_obj_unbound_repeat.c +208 -241
  69. data/ext/ca_obj_window.c +1131 -563
  70. data/ext/ca_op_byte_swap.c +175 -0
  71. data/ext/ca_op_ipower.c +319 -0
  72. data/ext/ca_op_powi.h +88 -0
  73. data/ext/ca_sort_kernels.h +132 -0
  74. data/ext/ca_sweep_engine.c +430 -0
  75. data/ext/ca_sweep_engine.h +157 -0
  76. data/ext/ca_transform_common.c +228 -0
  77. data/ext/ca_triop_dispatch.c +55 -0
  78. data/ext/ca_triop_dispatch.h +62 -0
  79. data/ext/carray.h +795 -402
  80. data/ext/carray_access.c +831 -711
  81. data/ext/carray_attribute.c +98 -330
  82. data/ext/carray_bincount.c +255 -0
  83. data/ext/carray_broadcast.c +283 -0
  84. data/ext/carray_call_cfunc.c +1360 -828
  85. data/ext/carray_call_cfunc.h +160 -0
  86. data/ext/carray_cast.c +1212 -301
  87. data/ext/carray_cast_func.rb +81 -40
  88. data/ext/carray_class.c +53 -63
  89. data/ext/carray_config.h +28 -0
  90. data/ext/carray_conversion.c +350 -346
  91. data/ext/carray_copy.c +156 -268
  92. data/ext/carray_core.c +1342 -199
  93. data/ext/carray_count.c +312 -0
  94. data/ext/carray_data_type.c +43 -19
  95. data/ext/carray_element.c +585 -213
  96. data/ext/carray_factorize.c +2542 -0
  97. data/ext/carray_generate.c +230 -559
  98. data/ext/carray_histogram.c +490 -0
  99. data/ext/carray_hold.c +228 -0
  100. data/ext/carray_index_classifier.c +1035 -0
  101. data/ext/carray_index_classifier.h +27 -0
  102. data/ext/carray_internal.h +120 -0
  103. data/ext/carray_kernels_bincmp.c +4445 -0
  104. data/ext/carray_kernels_binop.c +10979 -0
  105. data/ext/carray_kernels_init.c +36 -0
  106. data/ext/carray_kernels_map.c +3466 -0
  107. data/ext/carray_kernels_moncmp.c +2096 -0
  108. data/ext/carray_kernels_monop.c +18312 -0
  109. data/ext/carray_kernels_reduce_aggregate.c +25836 -0
  110. data/ext/carray_kernels_reduce_boolean.c +329 -0
  111. data/ext/carray_kernels_reduce_cumulative.c +14592 -0
  112. data/ext/carray_kernels_reduce_extreme.c +16947 -0
  113. data/ext/carray_kernels_reduce_variance.c +3909 -0
  114. data/ext/carray_kernels_scan.c +3692 -0
  115. data/ext/carray_kernels_search.c +32137 -0
  116. data/ext/carray_kernels_sort.c +10625 -0
  117. data/ext/carray_kernels_triop.c +1391 -0
  118. data/ext/carray_lazy.c +567 -0
  119. data/ext/carray_loop.c +88 -200
  120. data/ext/carray_mask.c +848 -154
  121. data/ext/carray_math_kernel.h +120 -0
  122. data/ext/carray_mathfunc.c +10 -241
  123. data/ext/carray_median_percentile.c +1257 -0
  124. data/ext/carray_memory_view.c +1625 -0
  125. data/ext/carray_operator.c +1526 -318
  126. data/ext/carray_order.c +664 -1394
  127. data/ext/carray_partition.c +416 -0
  128. data/ext/carray_random.c +518 -0
  129. data/ext/carray_scatter.c +357 -0
  130. data/ext/carray_slab.c +1219 -0
  131. data/ext/carray_slab.h +84 -0
  132. data/ext/carray_sort.c +829 -0
  133. data/ext/carray_sort_kernel.c +620 -0
  134. data/ext/carray_struct.c +695 -0
  135. data/ext/carray_test.c +343 -229
  136. data/ext/carray_undef.c +34 -17
  137. data/ext/carray_utils.c +175 -74
  138. data/ext/extconf.rb +216 -55
  139. data/ext/mk_call_cfunc.rb +480 -0
  140. data/ext/mkkernel.rb +8842 -0
  141. data/ext/ruby_carray.c +202 -101
  142. data/ext/version.h +4 -14
  143. data/ext/version.rb +5 -13
  144. data/lib/carray/arrow_tensor.rb +401 -0
  145. data/lib/carray/attribute.rb +166 -0
  146. data/lib/carray/autoload_carray.rb +220 -0
  147. data/lib/carray/autoload_method_extension.rb +44 -0
  148. data/lib/carray/axis_group.rb +711 -0
  149. data/lib/carray/basics.rb +481 -0
  150. data/lib/carray/bincount_nd.rb +358 -0
  151. data/lib/carray/block_iterator.rb +604 -0
  152. data/lib/carray/boolean_reduce.rb +109 -0
  153. data/lib/carray/categorical.rb +561 -0
  154. data/lib/carray/categorical_iterator.rb +1062 -0
  155. data/lib/carray/complex.rb +150 -0
  156. data/lib/carray/conditional.rb +216 -0
  157. data/lib/carray/const_string.rb +228 -0
  158. data/lib/carray/construct.rb +139 -328
  159. data/lib/carray/core_extensions.rb +240 -0
  160. data/lib/carray/data_type_extension.rb +233 -0
  161. data/lib/carray/fixlen_string.rb +95 -0
  162. data/lib/carray/frame/concat.rb +132 -0
  163. data/lib/carray/frame/convert.rb +95 -0
  164. data/lib/carray/frame/csv_parser.rb +211 -0
  165. data/lib/carray/frame/frame.rb +649 -0
  166. data/lib/carray/frame/group.rb +186 -0
  167. data/lib/carray/frame/io.rb +164 -0
  168. data/lib/carray/frame/join.rb +248 -0
  169. data/lib/carray/frame/records.rb +99 -0
  170. data/lib/carray/frame/sort.rb +113 -0
  171. data/lib/carray/frame/verbs.rb +299 -0
  172. data/lib/carray/frame.rb +16 -0
  173. data/lib/carray/histogram.rb +512 -0
  174. data/lib/carray/inspect.rb +37 -20
  175. data/lib/carray/iterator.rb +57 -349
  176. data/lib/carray/lazy.rb +889 -0
  177. data/lib/carray/mask_gap_fill.rb +200 -0
  178. data/lib/carray/math.rb +78 -342
  179. data/lib/carray/meld_reduce.rb +289 -0
  180. data/lib/carray/methods/align_addr.rb +116 -0
  181. data/lib/carray/methods/bin.rb +128 -0
  182. data/lib/carray/methods/bincount.rb +87 -0
  183. data/lib/carray/methods/bit_string.rb +92 -0
  184. data/lib/carray/methods/broadcast.rb +63 -0
  185. data/lib/carray/methods/choose.rb +39 -0
  186. data/lib/carray/methods/composition.rb +280 -0
  187. data/lib/carray/methods/gather_nd.rb +206 -0
  188. data/lib/carray/methods/index.rb +39 -0
  189. data/lib/carray/methods/insert_block.rb +99 -0
  190. data/lib/carray/methods/is_in.rb +141 -0
  191. data/lib/carray/methods/join.rb +90 -0
  192. data/lib/carray/methods/locate_addr.rb +47 -0
  193. data/lib/carray/methods/mask_duplicates.rb +41 -0
  194. data/lib/carray/methods/meshgrid.rb +91 -0
  195. data/lib/carray/methods/mode.rb +126 -0
  196. data/lib/carray/methods/nunique.rb +46 -0
  197. data/lib/carray/methods/resize.rb +56 -0
  198. data/lib/carray/methods/snap.rb +156 -0
  199. data/lib/carray/methods/string_format.rb +57 -0
  200. data/lib/carray/methods/unique.rb +47 -0
  201. data/lib/carray/methods/value_counts.rb +71 -0
  202. data/lib/carray/mkmf.rb +124 -101
  203. data/lib/carray/runtime.rb +108 -0
  204. data/lib/carray/serialize.rb +478 -167
  205. data/lib/carray/slab_iterator.rb +292 -0
  206. data/lib/carray/stack.rb +291 -0
  207. data/lib/carray/string.rb +56 -180
  208. data/lib/carray/string_operation_extension.rb +289 -0
  209. data/lib/carray/struct.rb +335 -323
  210. data/lib/carray/struct_builder.rb +697 -0
  211. data/lib/carray/table.rb +41 -2
  212. data/lib/carray/time.rb +2255 -38
  213. data/lib/carray/window_iterator.rb +655 -0
  214. data/lib/carray.rb +55 -57
  215. metadata +163 -130
  216. data/Rakefile +0 -51
  217. data/TODO.md +0 -18
  218. data/ext/ca_iter_block.c +0 -257
  219. data/ext/ca_iter_dimension.c +0 -299
  220. data/ext/ca_iter_window.c +0 -214
  221. data/ext/ca_obj_mapping.c +0 -644
  222. data/ext/carray_iterator.c +0 -641
  223. data/ext/carray_math.rb +0 -850
  224. data/ext/carray_numeric.c +0 -259
  225. data/ext/carray_sort_addr.c +0 -254
  226. data/ext/carray_stat.c +0 -2100
  227. data/ext/carray_stat_proc.rb +0 -1999
  228. data/ext/mkmath.rb +0 -741
  229. data/ext/ruby_ccomplex.c +0 -509
  230. data/ext/ruby_float_func.c +0 -86
  231. data/lib/carray/array.rb +0 -8
  232. data/lib/carray/autoload/autoload_base.rb +0 -19
  233. data/lib/carray/autoload/autoload_gem_cairo.rb +0 -9
  234. data/lib/carray/autoload/autoload_gem_ffi.rb +0 -9
  235. data/lib/carray/autoload/autoload_gem_gnuplot.rb +0 -2
  236. data/lib/carray/autoload/autoload_gem_io_csv.rb +0 -14
  237. data/lib/carray/autoload/autoload_gem_io_pg.rb +0 -6
  238. data/lib/carray/autoload/autoload_gem_io_sqlite3.rb +0 -12
  239. data/lib/carray/autoload/autoload_gem_narray.rb +0 -10
  240. data/lib/carray/autoload/autoload_gem_numo_narray.rb +0 -15
  241. data/lib/carray/autoload/autoload_gem_opencv.rb +0 -16
  242. data/lib/carray/autoload/autoload_gem_random.rb +0 -8
  243. data/lib/carray/autoload/autoload_gem_rmagick.rb +0 -23
  244. data/lib/carray/autoload/autoload_gem_zimg.rb +0 -3
  245. data/lib/carray/autoload/autoload_io_imagemagick.rb +0 -6
  246. data/lib/carray/autoload/autoload_math_histogram.rb +0 -5
  247. data/lib/carray/autoload/autoload_math_recurrence.rb +0 -6
  248. data/lib/carray/autoload/autoload_object_iterator.rb +0 -1
  249. data/lib/carray/autoload/autoload_object_link.rb +0 -1
  250. data/lib/carray/autoload/autoload_object_pack.rb +0 -2
  251. data/lib/carray/autoload.rb +0 -141
  252. data/lib/carray/basic.rb +0 -191
  253. data/lib/carray/broadcast.rb +0 -101
  254. data/lib/carray/compose.rb +0 -315
  255. data/lib/carray/convert.rb +0 -115
  256. data/lib/carray/info.rb +0 -110
  257. data/lib/carray/io/imagemagick.rb +0 -235
  258. data/lib/carray/mask.rb +0 -102
  259. data/lib/carray/math/histogram.rb +0 -177
  260. data/lib/carray/math/recurrence.rb +0 -93
  261. data/lib/carray/object/ca_obj_iterator.rb +0 -50
  262. data/lib/carray/object/ca_obj_link.rb +0 -50
  263. data/lib/carray/object/ca_obj_pack.rb +0 -99
  264. data/lib/carray/obsolete.rb +0 -256
  265. data/lib/carray/ordering.rb +0 -181
  266. data/lib/carray/testing.rb +0 -51
  267. data/lib/carray/transform.rb +0 -109
  268. data/misc/Methods.ja.md +0 -182
  269. data/misc/NOTE +0 -51
  270. data/spec/Classes/CABitfield_spec.rb +0 -58
  271. data/spec/Classes/CABlockIterator_spec.rb +0 -114
  272. data/spec/Classes/CABlock_spec.rb +0 -205
  273. data/spec/Classes/CAField_spec.rb +0 -39
  274. data/spec/Classes/CAGrid_spec.rb +0 -75
  275. data/spec/Classes/CAMap_spec.rb +0 -0
  276. data/spec/Classes/CAMapping_spec.rb +0 -105
  277. data/spec/Classes/CAObject_attribute_spec.rb +0 -33
  278. data/spec/Classes/CAObject_spec.rb +0 -33
  279. data/spec/Classes/CARefer_spec.rb +0 -93
  280. data/spec/Classes/CARepeat_spec.rb +0 -65
  281. data/spec/Classes/CASelect_spec.rb +0 -22
  282. data/spec/Classes/CAShift_spec.rb +0 -16
  283. data/spec/Classes/CAStruct_spec.rb +0 -71
  284. data/spec/Classes/CATranspose_spec.rb +0 -60
  285. data/spec/Classes/CAUnboudRepeat_spec.rb +0 -102
  286. data/spec/Classes/CAWindow_spec.rb +0 -54
  287. data/spec/Classes/CAWrap_spec.rb +0 -8
  288. data/spec/Classes/CArray_spec.rb +0 -184
  289. data/spec/Classes/CScalar_spec.rb +0 -55
  290. data/spec/Classes/ex1.rb +0 -46
  291. data/spec/Features/feature_130_spec.rb +0 -19
  292. data/spec/Features/feature_attributes_spec.rb +0 -280
  293. data/spec/Features/feature_boolean_spec.rb +0 -98
  294. data/spec/Features/feature_broadcast.rb +0 -116
  295. data/spec/Features/feature_cast_function.rb +0 -19
  296. data/spec/Features/feature_cast_spec.rb +0 -33
  297. data/spec/Features/feature_class_spec.rb +0 -84
  298. data/spec/Features/feature_complex_spec.rb +0 -42
  299. data/spec/Features/feature_composite_spec.rb +0 -124
  300. data/spec/Features/feature_convert_spec.rb +0 -46
  301. data/spec/Features/feature_copy_spec.rb +0 -123
  302. data/spec/Features/feature_creation_spec.rb +0 -84
  303. data/spec/Features/feature_element_spec.rb +0 -144
  304. data/spec/Features/feature_extream_spec.rb +0 -54
  305. data/spec/Features/feature_generate_spec.rb +0 -74
  306. data/spec/Features/feature_index_spec.rb +0 -69
  307. data/spec/Features/feature_mask_spec.rb +0 -580
  308. data/spec/Features/feature_math_spec.rb +0 -97
  309. data/spec/Features/feature_order_spec.rb +0 -146
  310. data/spec/Features/feature_ref_store_spec.rb +0 -209
  311. data/spec/Features/feature_serialization_spec.rb +0 -125
  312. data/spec/Features/feature_stat_spec.rb +0 -397
  313. data/spec/Features/feature_virtual_spec.rb +0 -48
  314. data/spec/Features/method_eq_spec.rb +0 -81
  315. data/spec/Features/method_is_nan_spec.rb +0 -12
  316. data/spec/Features/method_map_spec.rb +0 -54
  317. data/spec/Features/method_max_with.rb +0 -20
  318. data/spec/Features/method_min_with.rb +0 -19
  319. data/spec/Features/method_ne_spec.rb +0 -18
  320. data/spec/Features/method_project_spec.rb +0 -188
  321. data/spec/Features/method_ref_spec.rb +0 -27
  322. data/spec/Features/method_round_spec.rb +0 -11
  323. data/spec/Features/method_s_linspace_spec.rb +0 -48
  324. data/spec/Features/method_s_span_spec.rb +0 -14
  325. data/spec/Features/method_seq_spec.rb +0 -47
  326. data/spec/Features/method_sort_with.rb +0 -43
  327. data/spec/Features/method_sorted_with.rb +0 -29
  328. data/spec/Features/method_span_spec.rb +0 -42
  329. data/spec/Features/method_wrap_readonly_spec.rb +0 -43
  330. data/spec/UnitTest/test_CAVirtual.rb +0 -214
  331. data/spec/spec_all.rb +0 -10
  332. data/utils/ca_ase.rb +0 -21
  333. data/utils/ca_methods.rb +0 -15
  334. data/utils/cast_checker.rb +0 -30
  335. data/utils/convert_test.rb +0 -73
  336. data/utils/extract_yard.rb +0 -22
  337. data/utils/guess_shape.rb +0 -76
  338. data/utils/monkey_patch_methods.rb +0 -62
  339. data/utils/remove_resource_fork.sh +0 -5
@@ -0,0 +1,746 @@
1
+ /* ---------------------------------------------------------------------------
2
+
3
+ Lazy triadic element-wise arithmetic view: holds (op1, op2, op3,
4
+ op_id) and materialises the promoted result on attach. Carries
5
+ CA_FLAG_READ_ONLY; there is no bang path and `[]=` raises.
6
+
7
+ Sibling of ca_obj_binop.c (binary arithmetic) — same 3.0 lazy
8
+ substrate. Dispatched by ca_triop_kernel_lookup_vvv from
9
+ ca_triop_dispatch.h. Casts to the common data_type are inserted
10
+ as CAMonOp nodes on all 3 operands by the public builder before the
11
+ CATriOp is constructed.
12
+
13
+ xfer_stride model (mirrors CABinOp):
14
+ 1. Pull op1 into `data` (the output buffer). op1 and output share
15
+ data_type (cast-before invariant), so this is safe as an
16
+ in-place gather.
17
+ 2. Pull op2 into an arena scratch (per-node).
18
+ 3. Pull op3 into another arena scratch (per-node).
19
+ 4. Call the eager 1-D kernel with src1 == dst == data (in-place
20
+ op1), src2 == scratch2, src3 == scratch3.
21
+
22
+ Mask handling: none of the currently-defined triops (fma / fms /
23
+ clip) trap on integer zero divisor, so m=NULL always at the
24
+ kernel walk (SIMD fast path). Output mask is a blind OR of
25
+ operand masks (create_mask time).
26
+
27
+ Cross-ndim promotion: intentional design rejection, mirroring
28
+ CABinOp. Callers must reshape explicitly.
29
+
30
+ ---------------------------------------------------------------------------- */
31
+
32
+ #include "carray.h"
33
+ #include "carray_internal.h" /* ca_lazy_arena_* */
34
+ #include "ca_triop_dispatch.h"
35
+ #include "ca_monop_dispatch.h" /* CA_MONOP_CAST_BASE */
36
+
37
+ extern VALUE ca_broadcast_view (VALUE src, int8_t ndim,
38
+ ca_size_t *target_dim);
39
+
40
+ int8_t CA_OBJ_TRIOP;
41
+ VALUE rb_cCATriOp;
42
+
43
+ extern int8_t CA_OBJ_LAZY_MARKER;
44
+ extern VALUE ca_lazy_wrap_scalar (VALUE other, CArray *self_ca);
45
+
46
+ /* ------------------------------------------------------------------- */
47
+ /* CATriOp struct */
48
+ /* ------------------------------------------------------------------- */
49
+
50
+ typedef struct CATriOp {
51
+ int16_t obj_type;
52
+ int8_t data_type;
53
+ int8_t ndim;
54
+ int32_t flags;
55
+ ca_size_t bytes;
56
+ ca_size_t elements;
57
+ ca_size_t *dim;
58
+ char *ptr;
59
+ CArray *mask;
60
+ char *_pool;
61
+ CArray *parent; /* = op1 */
62
+ uint32_t attach;
63
+ uint8_t nosync;
64
+ /* CATriOp-specific tail */
65
+ CArray *op2;
66
+ CArray *op3;
67
+ uint16_t op_id;
68
+ uint8_t op2_is_scalar;
69
+ uint8_t op3_is_scalar;
70
+ } CATriOp;
71
+
72
+ static size_t
73
+ ca_triop_dsize (const void *ap)
74
+ {
75
+ const CATriOp *ca = (const CATriOp *) ap;
76
+ return sizeof(CATriOp) + ca->ndim * sizeof(ca_size_t);
77
+ }
78
+
79
+ static size_t
80
+ ca_triop_pool_bytes (int8_t ndim)
81
+ {
82
+ ca_size_t n = (ndim > 0) ? ndim : 1;
83
+ return (size_t) n * sizeof(ca_size_t);
84
+ }
85
+
86
+ static void
87
+ ca_triop_pool_init (void *ap, int8_t ndim)
88
+ {
89
+ CATriOp *ca = (CATriOp *) ap;
90
+ ca->dim = (ca_size_t *) ca->_pool;
91
+ (void) ndim;
92
+ }
93
+
94
+ const rb_data_type_t catriop_data_type = {
95
+ .parent = &caview_data_type,
96
+ .wrap_struct_name = "CATriOp",
97
+ .function = {
98
+ .dmark = ca_mark,
99
+ .dfree = ca_free,
100
+ .dsize = ca_triop_dsize,
101
+ .dcompact = NULL
102
+ },
103
+ .flags = RUBY_TYPED_FREE_IMMEDIATELY
104
+ };
105
+
106
+ /* ------------------------------------------------------------------- */
107
+ /* setup / new / free */
108
+ /* ------------------------------------------------------------------- */
109
+
110
+ static int
111
+ ca_triop_setup (CATriOp *ca, CArray *op1, CArray *op2, CArray *op3,
112
+ uint16_t op_id)
113
+ {
114
+ int8_t out_dt = ca_lazy_promote_triop(op_id, op1->data_type,
115
+ op2->data_type, op3->data_type);
116
+ ca_size_t out_bytes = ca_sizeof[out_dt];
117
+
118
+ ca->obj_type = CA_OBJ_TRIOP;
119
+ ca->data_type = out_dt;
120
+ ca->flags = CA_FLAG_READ_ONLY;
121
+ ca->ndim = op1->ndim;
122
+ ca->bytes = out_bytes;
123
+ ca->elements = op1->elements;
124
+ ca->ptr = NULL;
125
+ ca->mask = NULL;
126
+ if ( ! ca->_pool ) {
127
+ ca->dim = ALLOC_N(ca_size_t, op1->ndim);
128
+ }
129
+ ca->parent = op1;
130
+ ca->attach = 0;
131
+ ca->nosync = 0;
132
+ ca->op2 = op2;
133
+ ca->op3 = op3;
134
+ ca->op_id = op_id;
135
+ ca->op2_is_scalar = ( op2->elements == 1 && op1->elements > 1 ) ? 1 : 0;
136
+ ca->op3_is_scalar = ( op3->elements == 1 && op1->elements > 1 ) ? 1 : 0;
137
+
138
+ memcpy(ca->dim, op1->dim, op1->ndim * sizeof(ca_size_t));
139
+
140
+ if ( ca_has_mask(op1) || ca_has_mask(op2) || ca_has_mask(op3) ) {
141
+ ca_create_mask(ca);
142
+ }
143
+
144
+ if ( ca_is_scalar(op1) && ca_is_scalar(op2) && ca_is_scalar(op3) ) {
145
+ ca_set_flag(ca, CA_FLAG_SCALAR);
146
+ }
147
+
148
+ return 0;
149
+ }
150
+
151
+ CATriOp *
152
+ ca_triop_new (CArray *op1, CArray *op2, CArray *op3, uint16_t op_id)
153
+ {
154
+ CATriOp *ca = (CATriOp *) ca_array_alloc(CA_OBJ_TRIOP, op1->ndim);
155
+ ca_triop_setup(ca, op1, op2, op3, op_id);
156
+ return ca;
157
+ }
158
+
159
+ static void
160
+ free_ca_triop (void *ap)
161
+ {
162
+ CATriOp *ca = (CATriOp *) ap;
163
+ if ( ca != NULL ) {
164
+ ca_free(ca->mask);
165
+ if ( ca->_pool ) {
166
+ ca_array_free(ca);
167
+ }
168
+ else {
169
+ xfree(ca->dim);
170
+ xfree(ca);
171
+ }
172
+ }
173
+ }
174
+
175
+ /* ------------------------------------------------------------------- */
176
+ /* operation function table */
177
+ /* ------------------------------------------------------------------- */
178
+
179
+ static void ca_triop_func_xfer_stride (void *ap, ca_size_t *starts,
180
+ ca_size_t *counts, ca_size_t *strides,
181
+ void *data, int dir);
182
+
183
+ static void *
184
+ ca_triop_func_clone (void *ap)
185
+ {
186
+ CATriOp *ca = (CATriOp *) ap;
187
+ return ca_triop_new(ca->parent, ca->op2, ca->op3, ca->op_id);
188
+ }
189
+
190
+ static void
191
+ ca_triop_func_xfer_index (void *ap, ca_size_t *idx, void *data, int dir)
192
+ {
193
+ CATriOp *ca = (CATriOp *) ap;
194
+ ca_size_t starts[CA_RANK_MAX];
195
+ ca_size_t counts[CA_RANK_MAX];
196
+ ca_size_t strides[CA_RANK_MAX];
197
+ int8_t k;
198
+ ca_size_t s;
199
+
200
+ if ( dir != CA_XFER_GET ) {
201
+ rb_raise(rb_eRuntimeError, "CATriOp is read-only (xfer_index PUT)");
202
+ }
203
+
204
+ s = ca->bytes;
205
+ for ( k = ca->ndim - 1; k >= 0; k-- ) {
206
+ starts[k] = idx[k];
207
+ counts[k] = 1;
208
+ strides[k] = s;
209
+ }
210
+ ca_triop_func_xfer_stride(ca, starts, counts, strides, data, CA_XFER_GET);
211
+ }
212
+
213
+ static void
214
+ ca_triop_func_xfer_addrs (void *ap, ca_size_t n, ca_size_t *addrs,
215
+ void *data, int dir)
216
+ {
217
+ CATriOp *ca = (CATriOp *) ap;
218
+ ca_size_t i;
219
+ char *out = (char *) data;
220
+
221
+ if ( dir != CA_XFER_GET ) {
222
+ rb_raise(rb_eRuntimeError, "CATriOp is read-only (xfer_addrs PUT)");
223
+ }
224
+
225
+ for ( i = 0; i < n; i++ ) {
226
+ ca_size_t idx[CA_RANK_MAX];
227
+ ca_addr2index((CArray *)ca, addrs[i], idx);
228
+ ca_triop_func_xfer_index(ca, idx, out + i * ca->bytes, CA_XFER_GET);
229
+ }
230
+ }
231
+
232
+ /* Test / observability counters (mirror CABinOp's counters). */
233
+ ca_size_t ca_triop_scratch_acquire_count = 0;
234
+ ca_size_t ca_triop_materialise_call_count = 0;
235
+
236
+ /* Pull one operand into a scratch (or one cell if it is a scalar to
237
+ be broadcast at step=0). Returns the scratch pointer; updates
238
+ *step_out to 0 (scalar) or 1 (same-shape). */
239
+ static void *
240
+ pull_operand (CArray *op, int is_scalar, ca_size_t *starts,
241
+ ca_size_t *counts, ca_size_t slab_n, int8_t ndim,
242
+ ca_size_t *step_out)
243
+ {
244
+ void *scratch;
245
+ ca_size_t bytes = op->bytes;
246
+ int8_t k;
247
+
248
+ if ( is_scalar ) {
249
+ ca_size_t one_starts[CA_RANK_MAX] = {0};
250
+ ca_size_t one_counts[CA_RANK_MAX];
251
+ ca_size_t one_strides[CA_RANK_MAX];
252
+ for ( k = 0; k < op->ndim; k++ ) {
253
+ one_counts[k] = 1;
254
+ one_strides[k] = bytes;
255
+ }
256
+ scratch = ca_lazy_arena_acquire(bytes);
257
+ ca_triop_scratch_acquire_count++;
258
+ ca_xfer_stride(op, one_starts, one_counts, one_strides, scratch,
259
+ CA_XFER_GET);
260
+ *step_out = 0;
261
+ }
262
+ else {
263
+ ca_size_t op_strides[CA_RANK_MAX];
264
+ ca_size_t s = bytes;
265
+ for ( k = ndim - 1; k >= 0; k-- ) {
266
+ op_strides[k] = s;
267
+ s *= counts[k];
268
+ }
269
+ scratch = ca_lazy_arena_acquire(slab_n * bytes);
270
+ ca_triop_scratch_acquire_count++;
271
+ ca_xfer_stride(op, starts, counts, op_strides, scratch, CA_XFER_GET);
272
+ *step_out = 1;
273
+ }
274
+ return scratch;
275
+ }
276
+
277
+ static void
278
+ ca_triop_func_xfer_stride (void *ap, ca_size_t *starts, ca_size_t *counts,
279
+ ca_size_t *strides, void *data, int dir)
280
+ {
281
+ CATriOp *to = (CATriOp *) ap;
282
+ ca_size_t slab_n;
283
+ int8_t k;
284
+ void *scratch2, *scratch3;
285
+ ca_size_t op2_step, op3_step;
286
+
287
+ if ( dir != CA_XFER_GET ) {
288
+ rb_raise(rb_eRuntimeError, "CATriOp is read-only (xfer_stride PUT)");
289
+ }
290
+ ca_triop_materialise_call_count++;
291
+
292
+ slab_n = 1;
293
+ for ( k = 0; k < to->ndim; k++ ) slab_n *= counts[k];
294
+
295
+ /* Step 1: pull op1 into the output buffer. */
296
+ ca_xfer_stride(to->parent, starts, counts, strides, data, CA_XFER_GET);
297
+
298
+ /* Step 2: pull op2 into an arena scratch. */
299
+ scratch2 = pull_operand(to->op2, to->op2_is_scalar, starts, counts,
300
+ slab_n, to->ndim, &op2_step);
301
+
302
+ /* Step 3: pull op3 into another arena scratch. */
303
+ scratch3 = pull_operand(to->op3, to->op3_is_scalar, starts, counts,
304
+ slab_n, to->ndim, &op3_step);
305
+
306
+ /* Step 4: apply the kernel. ptr1 == ptr4 (in-place op1); no
307
+ currently-defined triop traps, so m=NULL. */
308
+ {
309
+ ca_triop_func_t fn = ca_triop_kernel_lookup_vvv(to->op_id, to->data_type);
310
+ if ( fn == NULL ) {
311
+ ca_lazy_arena_release(scratch3);
312
+ ca_lazy_arena_release(scratch2);
313
+ rb_raise(rb_eNotImpError,
314
+ "CATriOp: kernel not implemented (op_id=%u data_type=%d)",
315
+ (unsigned) to->op_id, (int) to->data_type);
316
+ }
317
+ fn(slab_n, NULL,
318
+ (char *)data, 1, /* src1 == dst (op1, in-place) */
319
+ (char *)scratch2, op2_step, /* src2 (op2) */
320
+ (char *)scratch3, op3_step, /* src3 (op3) */
321
+ (char *)data, 1); /* dst */
322
+ }
323
+
324
+ ca_lazy_arena_release(scratch3);
325
+ ca_lazy_arena_release(scratch2);
326
+ }
327
+
328
+ static void
329
+ ca_triop_func_xfer_all (void *ap, void *data, int dir)
330
+ {
331
+ CATriOp *ca = (CATriOp *) ap;
332
+ ca_size_t starts[CA_RANK_MAX];
333
+ ca_size_t native[CA_RANK_MAX];
334
+ int8_t k;
335
+ ca_size_t s = ca->bytes;
336
+ for ( k = ca->ndim - 1; k >= 0; k-- ) { native[k] = s; s *= ca->dim[k]; }
337
+ for ( k = 0; k < ca->ndim; k++ ) starts[k] = 0;
338
+ ca_xfer_stride(ca, starts, ca->dim, native, data, dir);
339
+ }
340
+
341
+ static void
342
+ ca_triop_func_allocate (void *ap)
343
+ {
344
+ CATriOp *ca = (CATriOp *) ap;
345
+ ca->ptr = xmalloc(ca_length(ca));
346
+ }
347
+
348
+ static void
349
+ ca_triop_func_attach (void *ap)
350
+ {
351
+ /* CAREFUL: call the view-specific xfer_stride directly (same as
352
+ CABinOp). The public dispatcher's self-memcpy fast path would
353
+ leave the freshly-allocated buffer holding garbage. */
354
+ CATriOp *ca = (CATriOp *) ap;
355
+ ca_size_t starts[CA_RANK_MAX];
356
+ ca_size_t native[CA_RANK_MAX];
357
+ int8_t k;
358
+ ca_size_t s;
359
+
360
+ ca->ptr = xmalloc(ca_length(ca));
361
+
362
+ s = ca->bytes;
363
+ for ( k = ca->ndim - 1; k >= 0; k-- ) { native[k] = s; s *= ca->dim[k]; }
364
+ for ( k = 0; k < ca->ndim; k++ ) starts[k] = 0;
365
+ ca_triop_func_xfer_stride(ca, starts, ca->dim, native, ca->ptr, CA_XFER_GET);
366
+ }
367
+
368
+ static void
369
+ ca_triop_func_sync (void *ap)
370
+ {
371
+ (void) ap; /* read-only */
372
+ }
373
+
374
+ static void
375
+ ca_triop_func_detach (void *ap)
376
+ {
377
+ CATriOp *ca = (CATriOp *) ap;
378
+ xfree(ca->ptr);
379
+ ca->ptr = NULL;
380
+ }
381
+
382
+ NORETURN(static void ca_triop_func_fill_data (void *ap, void *ptr));
383
+ static void
384
+ ca_triop_func_fill_data (void *ap, void *ptr)
385
+ {
386
+ (void) ap; (void) ptr;
387
+ rb_raise(rb_eRuntimeError, "CATriOp is read-only (fill_data)");
388
+ }
389
+
390
+ /* Build out.mask. Blind OR of the three operand masks — none of the
391
+ currently-defined triops carry a Kleene 3-valued fixup. */
392
+ static void
393
+ ca_triop_func_create_mask (void *ap)
394
+ {
395
+ CATriOp *to = (CATriOp *) ap;
396
+ CArray *op1 = to->parent;
397
+ CArray *op2 = to->op2;
398
+ CArray *op3 = to->op3;
399
+ boolean8_t *dst, *m1, *m2, *m3;
400
+ ca_size_t i, n;
401
+ int has1, has2, has3;
402
+
403
+ has1 = ca_has_mask(op1);
404
+ has2 = ca_has_mask(op2);
405
+ has3 = ca_has_mask(op3);
406
+ if ( ! has1 && ! has2 && ! has3 ) return;
407
+
408
+ to->mask = (CArray *) carray_new(CA_BOOLEAN, to->ndim, to->dim, 0, NULL);
409
+ dst = (boolean8_t *) to->mask->ptr;
410
+ n = to->elements;
411
+
412
+ if ( has1 ) ca_attach(op1);
413
+ if ( has2 ) ca_attach(op2);
414
+ if ( has3 ) ca_attach(op3);
415
+
416
+ m1 = has1 ? (boolean8_t *) op1->mask->ptr : NULL;
417
+ m2 = has2 ? (boolean8_t *) op2->mask->ptr : NULL;
418
+ m3 = has3 ? (boolean8_t *) op3->mask->ptr : NULL;
419
+
420
+ for ( i = 0; i < n; i++ ) {
421
+ ca_size_t i2 = to->op2_is_scalar ? 0 : i;
422
+ ca_size_t i3 = to->op3_is_scalar ? 0 : i;
423
+ boolean8_t a = m1 ? m1[i] : 0;
424
+ boolean8_t b = m2 ? m2[i2] : 0;
425
+ boolean8_t c = m3 ? m3[i3] : 0;
426
+ dst[i] = (boolean8_t) ( a | b | c );
427
+ }
428
+
429
+ if ( has3 ) ca_detach(op3);
430
+ if ( has2 ) ca_detach(op2);
431
+ if ( has1 ) ca_detach(op1);
432
+ }
433
+
434
+ ca_operation_function_t ca_triop_func = {
435
+ -1, /* CA_OBJ_TRIOP, set at install time */
436
+ CA_VIEW_ARRAY,
437
+ free_ca_triop,
438
+ ca_triop_func_clone,
439
+ ca_triop_func_allocate,
440
+ ca_triop_func_attach,
441
+ ca_triop_func_sync,
442
+ ca_triop_func_detach,
443
+ ca_triop_func_fill_data,
444
+ ca_triop_func_create_mask,
445
+ ca_triop_func_xfer_index,
446
+ ca_triop_func_xfer_addrs,
447
+ NULL, /* fold_stride: never fold — op boundary */
448
+ ca_triop_func_xfer_stride,
449
+ ca_triop_func_xfer_all,
450
+ };
451
+
452
+ /* ------------------------------------------------------------------- */
453
+ /* Public builder */
454
+ /* ------------------------------------------------------------------- */
455
+
456
+ static VALUE
457
+ collapse_marker (VALUE cary)
458
+ {
459
+ CArray *ca;
460
+ if ( ! rb_obj_is_carray(cary) ) return cary;
461
+ TypedData_Get_Struct(cary, CArray, &carray_data_type, ca);
462
+ if ( ca->obj_type == CA_OBJ_LAZY_MARKER ) {
463
+ return rb_ca_parent(cary);
464
+ }
465
+ return cary;
466
+ }
467
+
468
+ /* Low-level constructor. Does NOT insert cast nodes; the caller must
469
+ have promoted all three operands to the common data_type. */
470
+ static VALUE
471
+ rb_ca_triop_new (VALUE cary1, VALUE cary2, VALUE cary3, uint16_t op_id)
472
+ {
473
+ volatile VALUE obj;
474
+ CArray *op1, *op2, *op3;
475
+ CATriOp *ca;
476
+ rb_check_carray_object(cary1);
477
+ rb_check_carray_object(cary2);
478
+ rb_check_carray_object(cary3);
479
+ TypedData_Get_Struct(cary1, CArray, &carray_data_type, op1);
480
+ TypedData_Get_Struct(cary2, CArray, &carray_data_type, op2);
481
+ TypedData_Get_Struct(cary3, CArray, &carray_data_type, op3);
482
+ ca = ca_triop_new(op1, op2, op3, op_id);
483
+ obj = ca_wrap_struct(ca);
484
+ rb_ca_set_parent(obj, cary1);
485
+ /* Pin op2 / op3 via ivars so GC keeps them alive. parent slot
486
+ already pins op1. */
487
+ rb_ivar_set(obj, rb_intern("__triop_op2__"), cary2);
488
+ rb_ivar_set(obj, rb_intern("__triop_op3__"), cary3);
489
+ return obj;
490
+ }
491
+
492
+ /* Insert a cast node if operand's data_type differs from target. */
493
+ static VALUE
494
+ insert_cast (VALUE cary, int8_t target_dt, CArray **ca_out)
495
+ {
496
+ CArray *ca;
497
+ TypedData_Get_Struct(cary, CArray, &carray_data_type, ca);
498
+ if ( ca->data_type != target_dt ) {
499
+ VALUE cast_op = INT2NUM(CA_MONOP_CAST_BASE + target_dt);
500
+ cary = rb_funcall(rb_const_get(rb_cObject, rb_intern("CAMonOp")),
501
+ rb_intern("__build__"), 2, cary, cast_op);
502
+ TypedData_Get_Struct(cary, CArray, &carray_data_type, ca);
503
+ }
504
+ *ca_out = ca;
505
+ return cary;
506
+ }
507
+
508
+ /* Public builder: build a CATriOp tree node for op_id over
509
+ (cary1, cary2, cary3). Inserts cast nodes when any operand's
510
+ data_type differs from the common data_type, then resolves
511
+ same-ndim size-1 broadcast pairwise against op1 (the walk anchor).
512
+
513
+ Broadcast: each of op2 / op3 is aligned against op1 via
514
+ ca_broadcast_pair. After alignment, each must have either the
515
+ same elements as op1 or be a 1-element CScalar (walked with
516
+ element_step = 0 by the kernel). If op1 itself is a 1-element
517
+ CScalar and any of op2 / op3 is an array, op1 is lifted via
518
+ ca_repeat_new + ca_broadcast_view (same trick as CABinOp).
519
+
520
+ Cross-ndim promotion is rejected, mirroring CABinOp. */
521
+ VALUE
522
+ rb_ca_triop_build (VALUE cary1, VALUE cary2, VALUE cary3, uint16_t op_id)
523
+ {
524
+ CArray *op1, *op2, *op3;
525
+ int8_t dt1, dt2, dt3;
526
+ volatile VALUE r1, r2, r3;
527
+
528
+ r1 = collapse_marker(cary1);
529
+ r2 = collapse_marker(cary2);
530
+ r3 = collapse_marker(cary3);
531
+
532
+ /* Promote non-CArray Ruby values (e.g. clip's Numeric bounds) to
533
+ CScalars carrying an existing operand's data_type. */
534
+ if ( ! rb_obj_is_carray(r1) || ! rb_obj_is_carray(r2) || ! rb_obj_is_carray(r3) ) {
535
+ /* Find an anchor CArray for scalar promotion. */
536
+ CArray *anchor = NULL;
537
+ if ( rb_obj_is_carray(r1) ) TypedData_Get_Struct(r1, CArray, &carray_data_type, anchor);
538
+ else if ( rb_obj_is_carray(r2) ) TypedData_Get_Struct(r2, CArray, &carray_data_type, anchor);
539
+ else if ( rb_obj_is_carray(r3) ) TypedData_Get_Struct(r3, CArray, &carray_data_type, anchor);
540
+ if ( anchor == NULL ) {
541
+ rb_raise(rb_eArgError,
542
+ "CATriOp: at least one operand must be a CArray");
543
+ }
544
+ if ( ! rb_obj_is_carray(r1) ) r1 = ca_lazy_wrap_scalar(r1, anchor);
545
+ if ( ! rb_obj_is_carray(r2) ) r2 = ca_lazy_wrap_scalar(r2, anchor);
546
+ if ( ! rb_obj_is_carray(r3) ) r3 = ca_lazy_wrap_scalar(r3, anchor);
547
+ }
548
+
549
+ TypedData_Get_Struct(r1, CArray, &carray_data_type, op1);
550
+ TypedData_Get_Struct(r2, CArray, &carray_data_type, op2);
551
+ TypedData_Get_Struct(r3, CArray, &carray_data_type, op3);
552
+
553
+ /* Step 1: cast to common data_type. */
554
+ ca_triop_kernel_input_data_types(op_id,
555
+ op1->data_type, op2->data_type, op3->data_type,
556
+ &dt1, &dt2, &dt3);
557
+ r1 = insert_cast(r1, dt1, &op1);
558
+ r2 = insert_cast(r2, dt2, &op2);
559
+ r3 = insert_cast(r3, dt3, &op3);
560
+
561
+ /* Step 2: pairwise broadcast against op1 (the walk anchor). If op1
562
+ is a scalar and any of op2 / op3 is an array, lift op1 up to the
563
+ array shape first. */
564
+ {
565
+ CArray *anchor = NULL;
566
+ volatile VALUE r_anchor = Qnil;
567
+ int8_t anchor_ndim;
568
+ /* Pick the largest-ndim non-scalar operand as anchor. */
569
+ if ( op1->elements > 1 ) { anchor = op1; r_anchor = r1; }
570
+ else if ( op2->elements > 1 ) { anchor = op2; r_anchor = r2; }
571
+ else if ( op3->elements > 1 ) { anchor = op3; r_anchor = r3; }
572
+ if ( anchor && anchor != op1 ) {
573
+ /* op1 is a CScalar but the walk shape is determined by another
574
+ operand. Lift op1 up to anchor's shape via
575
+ ca_repeat_new + ca_broadcast_view (same trick as CABinOp's
576
+ non-commutative left-scalar path). */
577
+ anchor_ndim = anchor->ndim;
578
+ if ( anchor_ndim > op1->ndim ) {
579
+ ca_size_t count[CA_RANK_MAX];
580
+ int8_t k;
581
+ for ( k = 0; k < anchor_ndim - 1; k++ ) count[k] = 1;
582
+ count[anchor_ndim - 1] = 0;
583
+ r1 = rb_ca_repeat_new(r1, anchor_ndim, count);
584
+ TypedData_Get_Struct(r1, CArray, &carray_data_type, op1);
585
+ }
586
+ r1 = ca_broadcast_view(r1, anchor_ndim, anchor->dim);
587
+ TypedData_Get_Struct(r1, CArray, &carray_data_type, op1);
588
+ (void) r_anchor;
589
+ }
590
+ }
591
+
592
+ /* Now align op2 / op3 against op1 for size-1 broadcast. */
593
+ ca_broadcast_pair(&r1, &r2);
594
+ TypedData_Get_Struct(r1, CArray, &carray_data_type, op1);
595
+ TypedData_Get_Struct(r2, CArray, &carray_data_type, op2);
596
+ ca_broadcast_pair(&r1, &r3);
597
+ TypedData_Get_Struct(r1, CArray, &carray_data_type, op1);
598
+ TypedData_Get_Struct(r3, CArray, &carray_data_type, op3);
599
+
600
+ /* Step 3: shape sanity — each of op2 / op3 must match op1's element
601
+ count OR be a 1-element CScalar (kernel walks with element_step
602
+ = 0 in that case). */
603
+ if ( op2->elements != op1->elements && op2->elements != 1 ) {
604
+ rb_raise(rb_eArgError,
605
+ "CATriOp: shape mismatch on op2 (%lld vs %lld) — only "
606
+ "same-ndim size-1 broadcast is supported; cross-ndim "
607
+ "promotion is not adopted in CArray "
608
+ "(reshape explicitly)",
609
+ (long long) op2->elements, (long long) op1->elements);
610
+ }
611
+ if ( op3->elements != op1->elements && op3->elements != 1 ) {
612
+ rb_raise(rb_eArgError,
613
+ "CATriOp: shape mismatch on op3 (%lld vs %lld) — only "
614
+ "same-ndim size-1 broadcast is supported; cross-ndim "
615
+ "promotion is not adopted in CArray "
616
+ "(reshape explicitly)",
617
+ (long long) op3->elements, (long long) op1->elements);
618
+ }
619
+
620
+ return rb_ca_triop_new(r1, r2, r3, op_id);
621
+ }
622
+
623
+ static VALUE
624
+ rb_ca_triop_s_build (VALUE klass, VALUE cary1, VALUE cary2, VALUE cary3,
625
+ VALUE op_id_val)
626
+ {
627
+ uint16_t op_id = (uint16_t) NUM2UINT(op_id_val);
628
+ (void) klass;
629
+ return rb_ca_triop_build(cary1, cary2, cary3, op_id);
630
+ }
631
+
632
+ static VALUE
633
+ rb_ca_triop_op_id (VALUE self)
634
+ {
635
+ CATriOp *to;
636
+ TypedData_Get_Struct(self, CATriOp, &catriop_data_type, to);
637
+ return UINT2NUM(to->op_id);
638
+ }
639
+
640
+ static VALUE
641
+ rb_ca_triop_op2 (VALUE self)
642
+ {
643
+ return rb_ivar_get(self, rb_intern("__triop_op2__"));
644
+ }
645
+
646
+ static VALUE
647
+ rb_ca_triop_op3 (VALUE self)
648
+ {
649
+ return rb_ivar_get(self, rb_intern("__triop_op3__"));
650
+ }
651
+
652
+ /* Test instrumentation. */
653
+ static VALUE
654
+ rb_ca_triop_s_reset_scratch_counter (VALUE klass)
655
+ {
656
+ (void) klass;
657
+ ca_triop_scratch_acquire_count = 0;
658
+ return Qnil;
659
+ }
660
+
661
+ static VALUE
662
+ rb_ca_triop_s_scratch_count (VALUE klass)
663
+ {
664
+ (void) klass;
665
+ return SIZE2NUM(ca_triop_scratch_acquire_count);
666
+ }
667
+
668
+ static VALUE
669
+ rb_ca_triop_s_reset_materialise_counter (VALUE klass)
670
+ {
671
+ (void) klass;
672
+ ca_triop_materialise_call_count = 0;
673
+ return Qnil;
674
+ }
675
+
676
+ static VALUE
677
+ rb_ca_triop_s_materialise_count (VALUE klass)
678
+ {
679
+ (void) klass;
680
+ return SIZE2NUM(ca_triop_materialise_call_count);
681
+ }
682
+
683
+ static VALUE
684
+ rb_ca_triop_s_allocate (VALUE klass)
685
+ {
686
+ CATriOp *ca;
687
+ return TypedData_Make_Struct(klass, CATriOp, &catriop_data_type, ca);
688
+ }
689
+
690
+ static VALUE
691
+ rb_ca_triop_initialize_copy (VALUE self, VALUE other)
692
+ {
693
+ CATriOp *ca, *cs;
694
+ TypedData_Get_Struct(self, CATriOp, &catriop_data_type, ca);
695
+ TypedData_Get_Struct(other, CATriOp, &catriop_data_type, cs);
696
+ if ( ca_func[CA_OBJ_TRIOP].pool_init ) {
697
+ ca_array_pool_alloc(ca, CA_OBJ_TRIOP, cs->parent->ndim);
698
+ }
699
+ ca_triop_setup(ca, cs->parent, cs->op2, cs->op3, cs->op_id);
700
+ return self;
701
+ }
702
+
703
+ void
704
+ Init_ca_obj_triop (void)
705
+ {
706
+ rb_cCATriOp = rb_define_class("CATriOp", rb_cCAView);
707
+
708
+ ca_triop_func.struct_size = sizeof(CATriOp);
709
+ ca_triop_func.pool_bytes = ca_triop_pool_bytes;
710
+ ca_triop_func.pool_init = ca_triop_pool_init;
711
+
712
+ CA_OBJ_TRIOP = ca_install_obj_type(rb_cCATriOp,
713
+ &catriop_data_type,
714
+ rb_cCArrayMask,
715
+ &carray_mask_data_type, &ca_triop_func, sizeof(ca_triop_func));
716
+ rb_define_const(rb_cObject, "CA_OBJ_TRIOP", INT2NUM(CA_OBJ_TRIOP));
717
+
718
+ /* op_id constants shared with lib/carray/lazy.rb. */
719
+ rb_define_const(rb_cCATriOp, "OP_FMA", INT2NUM(CA_TRIOP_FMA));
720
+ rb_define_const(rb_cCATriOp, "OP_FMS", INT2NUM(CA_TRIOP_FMS));
721
+ rb_define_const(rb_cCATriOp, "OP_CLIP", INT2NUM(CA_TRIOP_CLIP));
722
+
723
+ rb_define_alloc_func(rb_cCATriOp, rb_ca_triop_s_allocate);
724
+ rb_define_method(rb_cCATriOp, "initialize_copy",
725
+ rb_ca_triop_initialize_copy, 1);
726
+
727
+ rb_define_singleton_method(rb_cCATriOp, "__build__",
728
+ rb_ca_triop_s_build, 4);
729
+
730
+ rb_define_method(rb_cCATriOp, "__op_id__",
731
+ rb_ca_triop_op_id, 0);
732
+ rb_define_method(rb_cCATriOp, "__triop_op2__",
733
+ rb_ca_triop_op2, 0);
734
+ rb_define_method(rb_cCATriOp, "__triop_op3__",
735
+ rb_ca_triop_op3, 0);
736
+
737
+ /* Test instrumentation. */
738
+ rb_define_singleton_method(rb_cCATriOp, "__reset_scratch_counter__",
739
+ rb_ca_triop_s_reset_scratch_counter, 0);
740
+ rb_define_singleton_method(rb_cCATriOp, "__scratch_count__",
741
+ rb_ca_triop_s_scratch_count, 0);
742
+ rb_define_singleton_method(rb_cCATriOp, "__reset_materialise_counter__",
743
+ rb_ca_triop_s_reset_materialise_counter, 0);
744
+ rb_define_singleton_method(rb_cCATriOp, "__materialise_count__",
745
+ rb_ca_triop_s_materialise_count, 0);
746
+ }