carray 2.0.1 → 3.0.1

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