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
data/ext/ca_obj_meld.c ADDED
@@ -0,0 +1,1034 @@
1
+ /* ---------------------------------------------------------------------------
2
+
3
+ CAMeld view = ragged concatenate along an existing axis of K parents.
4
+
5
+ shape[a] = parents[0]->dim[a] for a != meld_axis
6
+ shape[meld_axis] = sum_k parents[k]->dim[meld_axis]
7
+
8
+ Segment boundaries are held as an explicit prefix-sum table (seg_offset)
9
+ so segment resolution is per-segment (K-1 boundaries) rather than per-cell.
10
+ User sees a welded axis; engine keeps the segments.
11
+
12
+ Op coverage (all accept arbitrary meld_axis):
13
+ xfer_all:
14
+ meld_axis == 0 = K contig xfer_all into per-segment slots (best,
15
+ memcpy-bound)
16
+ meld_axis != 0 = per-parent slab buf gather + row-major
17
+ scatter/gather (adapted from CAStack k_axis!=0)
18
+ xfer_stride:
19
+ structural + ma==0 = K contig xfer_stride into per-segment slot
20
+ structural + ma!=0 = per-segment slab buf + row-major scatter/gather
21
+ non-structural = per-cell fallback via xfer_index (universal
22
+ safety net; correctness-first, not perf-optimised)
23
+ xfer_index: binary-search seg_offset + parent dispatch
24
+ xfer_addrs: naive per-addr binary search (O(n log K))
25
+ (sortedness-aware O(n+K) merge is a future
26
+ optimisation, memo §7.3)
27
+ fill_data: K-fold ca_fill
28
+ create_mask: horizontal propagation (mirrors CAStack)
29
+ fold_stride: decline (return 0)
30
+
31
+ Design ref: devel/MEMO_CAMELD_SEGMENT_MAJOR_ENGINE.md. Meld-axis reduce
32
+ fast path (per-parent decompose, eager parity) lives in Ruby land at
33
+ lib/carray/meld_reduce.rb. The internal-axis paths above give the
34
+ deliver-it-anyway correctness contract; they are not tile-cache
35
+ optimised, but
36
+ since all decomposable reductions bypass xfer_all/xfer_stride via the
37
+ Ruby fast path, this rarely matters in practice.
38
+
39
+ ---------------------------------------------------------------------------- */
40
+
41
+ #include "carray.h"
42
+
43
+ /* ------------------------------------------------------------------- */
44
+ /* TypedData */
45
+ /* ------------------------------------------------------------------- */
46
+
47
+ static size_t
48
+ ca_meld_dsize (const void *ap)
49
+ {
50
+ const CAMeld *ca = (const CAMeld *) ap;
51
+ return sizeof(CAMeld)
52
+ + ca->ndim * sizeof(ca_size_t) /* dim[] */
53
+ + ca->n_parents * sizeof(CArray *) /* parents[] */
54
+ + (ca->n_parents + 1) * sizeof(ca_size_t); /* seg_offset[] */
55
+ }
56
+
57
+ const rb_data_type_t cameld_data_type = {
58
+ .parent = &caview_data_type,
59
+ .wrap_struct_name = "CAMeld",
60
+ .function = {
61
+ .dmark = ca_mark,
62
+ .dfree = ca_free,
63
+ .dsize = ca_meld_dsize,
64
+ .dcompact = NULL
65
+ },
66
+ .flags = RUBY_TYPED_FREE_IMMEDIATELY
67
+ };
68
+
69
+ const rb_data_type_t cameld_mask_data_type = {
70
+ .parent = &cameld_data_type,
71
+ .wrap_struct_name = "CAMeldMask",
72
+ .function = {
73
+ .dmark = ca_mark,
74
+ .dfree = ca_free_nop,
75
+ .dsize = ca_meld_dsize,
76
+ .dcompact = NULL
77
+ },
78
+ .flags = RUBY_TYPED_FREE_IMMEDIATELY
79
+ };
80
+
81
+ int8_t CA_OBJ_MELD;
82
+
83
+ VALUE rb_cCAMeld;
84
+ VALUE rb_cCAMeldMask;
85
+
86
+ static ID id_parents;
87
+
88
+ /* ------------------------------------------------------------------- */
89
+ /* uniform check */
90
+ /* ------------------------------------------------------------------- */
91
+
92
+ /* Parents must share dtype, ndim, bytes, and all dims except meld_axis;
93
+ meld_axis lengths are the ragged dimension (may differ). */
94
+ static void
95
+ ca_meld_check_uniform (int32_t n_parents, CArray **parents, int8_t meld_axis)
96
+ {
97
+ CArray *ref;
98
+ int32_t i;
99
+ int8_t k;
100
+ if ( n_parents <= 0 ) {
101
+ rb_raise(rb_eArgError, "CAMeld requires at least one parent");
102
+ }
103
+ ref = parents[0];
104
+ if ( meld_axis < 0 || meld_axis >= ref->ndim ) {
105
+ rb_raise(rb_eArgError,
106
+ "CAMeld meld_axis %d out of range [0, %d)",
107
+ (int) meld_axis, (int) ref->ndim);
108
+ }
109
+ for ( i = 1; i < n_parents; i++ ) {
110
+ CArray *p = parents[i];
111
+ if ( p->data_type != ref->data_type ) {
112
+ rb_raise(rb_eArgError,
113
+ "CAMeld parents must have uniform data_type "
114
+ "(parent[0]=%d, parent[%d]=%d)",
115
+ ref->data_type, i, p->data_type);
116
+ }
117
+ if ( p->ndim != ref->ndim ) {
118
+ rb_raise(rb_eArgError,
119
+ "CAMeld parents must have uniform ndim "
120
+ "(parent[0]=%d, parent[%d]=%d)",
121
+ ref->ndim, i, p->ndim);
122
+ }
123
+ if ( p->bytes != ref->bytes ) {
124
+ rb_raise(rb_eArgError,
125
+ "CAMeld parents must have uniform bytes "
126
+ "(parent[0]=%lld, parent[%d]=%lld)",
127
+ (long long) ref->bytes, i, (long long) p->bytes);
128
+ }
129
+ for ( k = 0; k < ref->ndim; k++ ) {
130
+ if ( k == meld_axis ) continue;
131
+ if ( p->dim[k] != ref->dim[k] ) {
132
+ rb_raise(rb_eArgError,
133
+ "CAMeld parents must have uniform shape except at meld_axis %d "
134
+ "(mismatch at axis %d: parent[0]=%lld, parent[%d]=%lld)",
135
+ (int) meld_axis, (int) k,
136
+ (long long) ref->dim[k], i, (long long) p->dim[k]);
137
+ }
138
+ }
139
+ }
140
+ }
141
+
142
+ /* ------------------------------------------------------------------- */
143
+ /* setup / new / free / clone */
144
+ /* ------------------------------------------------------------------- */
145
+
146
+ int
147
+ ca_meld_setup (CAMeld *ca, int32_t n_parents, CArray **parents, int8_t meld_axis)
148
+ {
149
+ CArray *ref;
150
+ int32_t i;
151
+ int8_t a;
152
+
153
+ ca_meld_check_uniform(n_parents, parents, meld_axis);
154
+ ref = parents[0];
155
+
156
+ ca->obj_type = CA_OBJ_MELD;
157
+ ca->data_type = ref->data_type;
158
+ ca->flags = CA_FLAG_MULTI_PARENTS;
159
+ ca->ndim = ref->ndim;
160
+ ca->bytes = ref->bytes;
161
+ ca->ptr = NULL;
162
+ ca->mask = NULL;
163
+ ca->_pool = NULL;
164
+
165
+ ca->parent = ref; /* CAView base = parents[0] */
166
+ ca->attach = 0;
167
+ ca->nosync = 0;
168
+
169
+ ca->n_parents = n_parents;
170
+ ca->parents = ALLOC_N(CArray *, n_parents);
171
+ for ( i = 0; i < n_parents; i++ ) {
172
+ ca->parents[i] = parents[i];
173
+ }
174
+ ca->meld_axis = meld_axis;
175
+
176
+ /* Prefix-sum along meld_axis: seg_offset[k+1] = seg_offset[k] + parents[k]->dim[meld_axis]. */
177
+ ca->seg_offset = ALLOC_N(ca_size_t, n_parents + 1);
178
+ ca->seg_offset[0] = 0;
179
+ for ( i = 0; i < n_parents; i++ ) {
180
+ ca->seg_offset[i + 1] = ca->seg_offset[i] + parents[i]->dim[meld_axis];
181
+ }
182
+
183
+ ca->dim = ALLOC_N(ca_size_t, ca->ndim);
184
+ for ( a = 0; a < ca->ndim; a++ ) {
185
+ ca->dim[a] = (a == meld_axis) ? ca->seg_offset[n_parents] : ref->dim[a];
186
+ }
187
+
188
+ ca->elements = 0;
189
+ for ( i = 0; i < n_parents; i++ ) ca->elements += parents[i]->elements;
190
+
191
+ return 0;
192
+ }
193
+
194
+ CAMeld *
195
+ ca_meld_new (int32_t n_parents, CArray **parents, int8_t meld_axis)
196
+ {
197
+ CAMeld *ca = ALLOC(CAMeld);
198
+ ca_meld_setup(ca, n_parents, parents, meld_axis);
199
+ return ca;
200
+ }
201
+
202
+ static void
203
+ free_ca_meld (void *ap)
204
+ {
205
+ /* CAREFUL: `parents[]` is an alias array we allocated with ALLOC_N in
206
+ ca_meld_setup; individual parent CArrays are kept alive by the Ruby
207
+ wrapper's `@parents` ivar (set by rb_ca_meld_new / rb_ca_meld_initialize).
208
+ So we xfree the tail here but never touch parent contents. */
209
+ CAMeld *ca = (CAMeld *) ap;
210
+ if ( ca != NULL ) {
211
+ ca_free(ca->mask);
212
+ xfree(ca->seg_offset);
213
+ xfree(ca->parents);
214
+ xfree(ca->dim);
215
+ xfree(ca);
216
+ }
217
+ }
218
+
219
+ static void *
220
+ ca_meld_func_clone (void *ap)
221
+ {
222
+ CAMeld *ca = (CAMeld *) ap;
223
+ return ca_meld_new(ca->n_parents, ca->parents, ca->meld_axis);
224
+ }
225
+
226
+ /* ------------------------------------------------------------------- */
227
+ /* helpers */
228
+ /* ------------------------------------------------------------------- */
229
+
230
+ /* Binary search: return k such that seg_offset[k] <= v < seg_offset[k+1].
231
+ Precondition: 0 <= v < seg_offset[n_parents]. */
232
+ static inline int32_t
233
+ ca_meld_segment_of (const CAMeld *ca, ca_size_t v)
234
+ {
235
+ int32_t lo = 0, hi = ca->n_parents;
236
+ while ( hi - lo > 1 ) {
237
+ int32_t mid = (lo + hi) >> 1;
238
+ if ( ca->seg_offset[mid] <= v ) lo = mid;
239
+ else hi = mid;
240
+ }
241
+ return lo;
242
+ }
243
+
244
+ /* Product of dims [meld_axis+1 .. ndim-1] (the tail after meld_axis). For
245
+ meld_axis == 0 this is the "row size in elements" for each meld_axis step. */
246
+ static inline ca_size_t
247
+ ca_meld_tail_elements (const CAMeld *ca)
248
+ {
249
+ int8_t a;
250
+ ca_size_t p = 1;
251
+ for ( a = ca->meld_axis + 1; a < ca->ndim; a++ ) p *= ca->dim[a];
252
+ return p;
253
+ }
254
+
255
+ /* ------------------------------------------------------------------- */
256
+ /* xfer_index */
257
+ /* ------------------------------------------------------------------- */
258
+
259
+ /* view[..., v at meld_axis, ...] = parents[k][..., v - seg_offset[k], ...]
260
+ where k = segment_of(v). */
261
+ static void
262
+ ca_meld_func_xfer_index (void *ap, ca_size_t *idx, void *data, int dir)
263
+ {
264
+ CAMeld *ca = (CAMeld *) ap;
265
+ int8_t ma = ca->meld_axis;
266
+ ca_size_t v = idx[ma];
267
+ int32_t k;
268
+ ca_size_t pidx[CA_RANK_MAX];
269
+ int8_t a;
270
+
271
+ if ( v < 0 || v >= ca->dim[ma] ) {
272
+ rb_raise(rb_eIndexError,
273
+ "CAMeld meld_axis (axis %d) index %lld out of range [0, %lld)",
274
+ (int) ma, (long long) v, (long long) ca->dim[ma]);
275
+ }
276
+ k = ca_meld_segment_of(ca, v);
277
+ for ( a = 0; a < ca->ndim; a++ ) pidx[a] = idx[a];
278
+ pidx[ma] = v - ca->seg_offset[k];
279
+ ca_xfer_index(ca->parents[k], pidx, data, dir);
280
+ }
281
+
282
+ /* ------------------------------------------------------------------- */
283
+ /* xfer_addrs — K-pass per-parent bucket */
284
+ /* ------------------------------------------------------------------- */
285
+
286
+ /* For each parent k, scan all n addrs and pick those whose meld-axis
287
+ coordinate lands in [seg_offset[k], seg_offset[k+1]). Total cost is
288
+ O(n·K) (K linear scans over n addrs); per-addr binary-search + K-way
289
+ bucket would drop this to O(n log K) with more temp storage, and a
290
+ sortedness-aware merge on sorted addrs (typical for boolean-mask
291
+ access) would give O(n+K) — both are demand-driven follow-ons. The
292
+ K-pass structure keeps peak scratch to a single (n * bytes) slab
293
+ shared across all parents. */
294
+ static void
295
+ ca_meld_func_xfer_addrs (void *ap, ca_size_t n, ca_size_t *addrs,
296
+ void *data, int dir)
297
+ {
298
+ CAMeld *ca = (CAMeld *) ap;
299
+ int8_t ma = ca->meld_axis;
300
+ ca_size_t bytes = ca->bytes;
301
+ ca_size_t view_div[CA_RANK_MAX]; /* row-major divisor per view axis */
302
+ ca_size_t parent_mul[CA_RANK_MAX]; /* per-parent (recomputed inside k loop) */
303
+ ca_size_t *paddrs;
304
+ char *pdata, *cdata = (char *) data;
305
+ volatile VALUE holder1, holder2;
306
+ ca_size_t s, i;
307
+ int32_t k;
308
+ int8_t a;
309
+
310
+ paddrs = ALLOCV_N(ca_size_t, holder1, n);
311
+ pdata = ALLOCV_N(char, holder2, n * bytes);
312
+
313
+ s = 1;
314
+ for ( a = ca->ndim - 1; a >= 0; a-- ) { view_div[a] = s; s *= ca->dim[a]; }
315
+
316
+ for ( k = 0; k < ca->n_parents; k++ ) {
317
+ ca_size_t seg_lo = ca->seg_offset[k];
318
+ ca_size_t seg_hi = ca->seg_offset[k + 1];
319
+ ca_size_t parent_meld_len = seg_hi - seg_lo;
320
+ ca_size_t m = 0;
321
+
322
+ /* Per-parent row-major multiplier: uses parent[0]'s dims for non-meld
323
+ axes (uniform check) and this parent's segment length at meld_axis. */
324
+ s = 1;
325
+ for ( a = ca->ndim - 1; a >= 0; a-- ) {
326
+ parent_mul[a] = s;
327
+ if ( a == ma ) s *= parent_meld_len;
328
+ else s *= ca->parents[0]->dim[a];
329
+ }
330
+
331
+ for ( i = 0; i < n; i++ ) {
332
+ ca_size_t addr = addrs[i];
333
+ ca_size_t vidx_ma = (addr / view_div[ma]) % ca->dim[ma];
334
+ ca_size_t paddr;
335
+ if ( vidx_ma < seg_lo || vidx_ma >= seg_hi ) continue;
336
+ paddr = 0;
337
+ for ( a = 0; a < ca->ndim; a++ ) {
338
+ ca_size_t vv = (addr / view_div[a]) % ca->dim[a];
339
+ if ( a == ma ) vv -= seg_lo;
340
+ paddr += vv * parent_mul[a];
341
+ }
342
+ paddrs[m] = paddr;
343
+ if ( dir == CA_XFER_PUT ) {
344
+ memcpy(pdata + m * bytes, cdata + i * bytes, bytes);
345
+ }
346
+ m++;
347
+ }
348
+ if ( m > 0 ) {
349
+ ca_xfer_addrs(ca->parents[k], m, paddrs, pdata, dir);
350
+ if ( dir == CA_XFER_GET ) {
351
+ ca_size_t mm = 0;
352
+ for ( i = 0; i < n; i++ ) {
353
+ ca_size_t addr = addrs[i];
354
+ ca_size_t vidx_ma = (addr / view_div[ma]) % ca->dim[ma];
355
+ if ( vidx_ma < seg_lo || vidx_ma >= seg_hi ) continue;
356
+ memcpy(cdata + i * bytes, pdata + mm * bytes, bytes);
357
+ mm++;
358
+ }
359
+ }
360
+ }
361
+ }
362
+
363
+ ALLOCV_END(holder2);
364
+ ALLOCV_END(holder1);
365
+ }
366
+
367
+ /* ------------------------------------------------------------------- */
368
+ /* xfer_stride */
369
+ /* ------------------------------------------------------------------- */
370
+
371
+ /* Structural best path (meld_axis == 0): K contig xfer_stride calls, each
372
+ parent's row-major output is a contig slot in dst -- zero copy. */
373
+ static void
374
+ ca_meld_xfer_stride_ma0 (CAMeld *ca, ca_size_t *starts, ca_size_t *counts,
375
+ ca_size_t *strides, void *data, int dir)
376
+ {
377
+ ca_size_t native[CA_RANK_MAX], dstride[CA_RANK_MAX];
378
+ ca_size_t pstarts[CA_RANK_MAX], pcounts[CA_RANK_MAX], pstrides[CA_RANK_MAX];
379
+ ca_size_t s, req_lo, req_hi;
380
+ int32_t k_lo, k_hi, k;
381
+ int8_t i;
382
+ char *d = (char *) data;
383
+
384
+ s = ca->bytes;
385
+ for ( i = ca->ndim - 1; i >= 0; i-- ) { native[i] = s; s *= ca->dim[i]; }
386
+ for ( i = 0; i < ca->ndim; i++ ) pstrides[i] = native[i];
387
+
388
+ s = ca->bytes;
389
+ for ( i = ca->ndim - 1; i >= 0; i-- ) { dstride[i] = s; s *= counts[i]; }
390
+
391
+ req_lo = starts[0];
392
+ req_hi = starts[0] + counts[0];
393
+ if ( req_hi <= req_lo ) return;
394
+ k_lo = ca_meld_segment_of(ca, req_lo);
395
+ k_hi = ca_meld_segment_of(ca, req_hi - 1) + 1;
396
+
397
+ for ( i = 1; i < ca->ndim; i++ ) { pstarts[i] = starts[i]; pcounts[i] = counts[i]; }
398
+
399
+ for ( k = k_lo; k < k_hi; k++ ) {
400
+ ca_size_t seg_lo = ca->seg_offset[k];
401
+ ca_size_t seg_hi = ca->seg_offset[k + 1];
402
+ ca_size_t view_lo = (req_lo > seg_lo) ? req_lo : seg_lo;
403
+ ca_size_t view_hi = (req_hi < seg_hi) ? req_hi : seg_hi;
404
+ ca_size_t slot_off = (view_lo - req_lo) * dstride[0];
405
+ pstarts[0] = view_lo - seg_lo;
406
+ pcounts[0] = view_hi - view_lo;
407
+ ca_xfer_stride(ca->parents[k], pstarts, pcounts, pstrides, d + slot_off, dir);
408
+ }
409
+ }
410
+
411
+ /* Internal-axis structural path (meld_axis != 0): per-segment slab buf
412
+ gather, then row-major scatter/gather to dst at view-strided positions.
413
+ The K stride sits mid-order so each parent's row-major slab does not fit
414
+ contig in dst. Follows CAStack's k_axis!=0 xfer_stride shape adapted
415
+ to segment-variable lengths. */
416
+ static void
417
+ ca_meld_xfer_stride_ma_internal (CAMeld *ca, ca_size_t *starts,
418
+ ca_size_t *counts, void *data, int dir)
419
+ {
420
+ int8_t ma = ca->meld_axis;
421
+ int8_t ndim = ca->ndim;
422
+ ca_size_t bytes = ca->bytes;
423
+ ca_size_t dstride[CA_RANK_MAX], pstrides[CA_RANK_MAX];
424
+ ca_size_t pstarts[CA_RANK_MAX], pcounts[CA_RANK_MAX];
425
+ ca_size_t req_lo, req_hi;
426
+ int32_t k_lo, k_hi, k;
427
+ int8_t i;
428
+ ca_size_t s;
429
+ char *d = (char *) data;
430
+
431
+ /* dst row-major over output counts[] */
432
+ s = bytes;
433
+ for ( i = ndim - 1; i >= 0; i-- ) { dstride[i] = s; s *= counts[i]; }
434
+
435
+ req_lo = starts[ma];
436
+ req_hi = starts[ma] + counts[ma];
437
+ if ( req_hi <= req_lo ) return;
438
+ k_lo = ca_meld_segment_of(ca, req_lo);
439
+ k_hi = ca_meld_segment_of(ca, req_hi - 1) + 1;
440
+
441
+ /* Non-meld pstarts/pcounts pass through */
442
+ for ( i = 0; i < ndim; i++ ) {
443
+ if ( i == ma ) continue;
444
+ pstarts[i] = starts[i];
445
+ pcounts[i] = counts[i];
446
+ }
447
+
448
+ for ( k = k_lo; k < k_hi; k++ ) {
449
+ CArray *p = ca->parents[k];
450
+ ca_size_t seg_lo = ca->seg_offset[k];
451
+ ca_size_t seg_hi = ca->seg_offset[k + 1];
452
+ ca_size_t view_lo = (req_lo > seg_lo) ? req_lo : seg_lo;
453
+ ca_size_t view_hi = (req_hi < seg_hi) ? req_hi : seg_hi;
454
+ ca_size_t ma_off_in_dst = view_lo - req_lo;
455
+ ca_size_t inner_dim[CA_RANK_MAX];
456
+ ca_size_t slab_bytes = bytes;
457
+ volatile VALUE holder;
458
+ char *buf;
459
+ ca_size_t paddr;
460
+
461
+ pstarts[ma] = view_lo - seg_lo;
462
+ pcounts[ma] = view_hi - view_lo;
463
+ for ( i = 0; i < ndim; i++ ) {
464
+ inner_dim[i] = pcounts[i];
465
+ slab_bytes *= pcounts[i];
466
+ }
467
+
468
+ /* pstrides is the SOURCE stride the parent uses to walk its own memory
469
+ (see ca_xfer_stride_dispatch + ca_xfer_strided_walk in carray_core.c:
470
+ for an entity source `strides` argument = byte offsets into
471
+ parent.ptr; dst walks contig via doff += slab_bytes). So pstrides
472
+ must be p's native row-major byte strides (based on p->dim), not
473
+ pcounts. partial-slab pcounts[i] < p->dim[i] just narrows the walk
474
+ range (counts) without changing the source layout. */
475
+ s = bytes;
476
+ for ( i = ndim - 1; i >= 0; i-- ) {
477
+ pstrides[i] = s;
478
+ s *= p->dim[i];
479
+ }
480
+
481
+ buf = ALLOCV_N(char, holder, slab_bytes);
482
+
483
+ if ( dir == CA_XFER_GET ) {
484
+ ca_xfer_stride(p, pstarts, pcounts, pstrides, buf, CA_XFER_GET);
485
+ }
486
+
487
+ /* Scatter/gather buf to dst using trailing-chunk memcpy.
488
+ Trailing block from meld_axis..ndim-1 is contig in BOTH parent slab
489
+ (row-major over pcounts[]) and dst (dst row-major stride dstride[]),
490
+ because within one parent + one outer combo, cells (ma..ndim-1) map
491
+ to a contig run in dst starting at ma_off_in_dst on meld_axis.
492
+ So we memcpy chunk_bytes = Π pcounts[ma..ndim-1] * bytes and only
493
+ odometer over outer axes 0..ma-1. */
494
+ paddr = 0;
495
+ {
496
+ ca_size_t voff = ma_off_in_dst * dstride[ma];
497
+ ca_size_t outer_step[CA_RANK_MAX], outer_back[CA_RANK_MAX];
498
+ ca_size_t outer_idx[CA_RANK_MAX];
499
+ ca_size_t chunk_bytes;
500
+ /* chunk_bytes = product of pcounts[ma..ndim-1] * bytes */
501
+ chunk_bytes = bytes;
502
+ for ( i = ma; i < ndim; i++ ) chunk_bytes *= inner_dim[i];
503
+ for ( i = 0; i < ma; i++ ) {
504
+ outer_step[i] = dstride[i];
505
+ outer_back[i] = (inner_dim[i] - 1) * dstride[i];
506
+ outer_idx[i] = 0;
507
+ }
508
+ if ( ma == 0 ) {
509
+ /* Guard (should not fire — caller dispatches ma==0 elsewhere) */
510
+ if ( dir == CA_XFER_GET ) memcpy(d + voff, buf, chunk_bytes);
511
+ else memcpy(buf, d + voff, chunk_bytes);
512
+ }
513
+ else if ( dir == CA_XFER_GET ) {
514
+ while ( 1 ) {
515
+ memcpy(d + voff, buf + paddr, chunk_bytes);
516
+ paddr += chunk_bytes;
517
+ i = ma - 1;
518
+ while ( i >= 0 ) {
519
+ if ( ++outer_idx[i] < inner_dim[i] ) { voff += outer_step[i]; break; }
520
+ outer_idx[i] = 0;
521
+ voff -= outer_back[i];
522
+ i--;
523
+ }
524
+ if ( i < 0 ) break;
525
+ }
526
+ } else {
527
+ while ( 1 ) {
528
+ memcpy(buf + paddr, d + voff, chunk_bytes);
529
+ paddr += chunk_bytes;
530
+ i = ma - 1;
531
+ while ( i >= 0 ) {
532
+ if ( ++outer_idx[i] < inner_dim[i] ) { voff += outer_step[i]; break; }
533
+ outer_idx[i] = 0;
534
+ voff -= outer_back[i];
535
+ i--;
536
+ }
537
+ if ( i < 0 ) break;
538
+ }
539
+ }
540
+ }
541
+
542
+ if ( dir == CA_XFER_PUT ) {
543
+ ca_xfer_stride(p, pstarts, pcounts, pstrides, buf, CA_XFER_PUT);
544
+ }
545
+ ALLOCV_END(holder);
546
+ }
547
+ }
548
+
549
+ /* Non-structural fallback: per-cell xfer_index. Universal correctness
550
+ safety net for arbitrary strides (works for any meld_axis, including
551
+ permutations like transpose). Follows CAStack's per-cell shape:
552
+ base = Σ starts[k] * native[k] (byte addr in flat root space)
553
+ toff = base + Σ idx[k] * strides[k] (composed byte offset)
554
+ addr2index(root, toff/bytes) → source N-D index
555
+ xfer_index at source idx, dst walks contig by bytes.
556
+ Slow but correct; hot paths stay on the structural branches above. */
557
+ static void
558
+ ca_meld_xfer_stride_per_cell (CAMeld *ca, ca_size_t *starts, ca_size_t *counts,
559
+ ca_size_t *strides, void *data, int dir)
560
+ {
561
+ int8_t ndim = ca->ndim;
562
+ ca_size_t native[CA_RANK_MAX];
563
+ ca_size_t idx[CA_RANK_MAX], vidx[CA_RANK_MAX];
564
+ ca_size_t base = 0, doff = 0, s;
565
+ char *d = (char *) data;
566
+ int8_t i;
567
+
568
+ CA_ASSUME(ndim >= 0 && ndim <= CA_RANK_MAX); /* bound loops over [CA_RANK_MAX] arrays */
569
+ s = ca->bytes;
570
+ for ( i = ndim - 1; i >= 0; i-- ) { native[i] = s; s *= ca->dim[i]; }
571
+ for ( i = 0; i < ndim; i++ ) base += starts[i] * native[i];
572
+ for ( i = 0; i < ndim; i++ ) idx[i] = 0;
573
+
574
+ while ( 1 ) {
575
+ ca_size_t toff = base;
576
+ for ( i = 0; i < ndim; i++ ) toff += idx[i] * strides[i];
577
+ ca_addr2index((CArray *) ca, toff / ca->bytes, vidx);
578
+ ca_meld_func_xfer_index(ca, vidx, d + doff, dir);
579
+ doff += ca->bytes;
580
+ i = ndim - 1;
581
+ while ( i >= 0 ) {
582
+ if ( ++idx[i] < counts[i] ) break;
583
+ idx[i] = 0;
584
+ i--;
585
+ }
586
+ if ( i < 0 ) break;
587
+ }
588
+ }
589
+
590
+ static void
591
+ ca_meld_func_xfer_stride (void *ap, ca_size_t *starts, ca_size_t *counts,
592
+ ca_size_t *strides, void *data, int dir)
593
+ {
594
+ CAMeld *ca = (CAMeld *) ap;
595
+ int8_t ma = ca->meld_axis;
596
+ ca_size_t native[CA_RANK_MAX];
597
+ ca_size_t s;
598
+ int8_t i;
599
+ int structural = 1;
600
+
601
+ /* Bound check on meld_axis */
602
+ {
603
+ ca_size_t req_lo = starts[ma];
604
+ ca_size_t req_hi = starts[ma] + counts[ma];
605
+ if ( req_lo < 0 || req_hi > ca->dim[ma] ) {
606
+ rb_raise(rb_eIndexError,
607
+ "CAMeld xfer_stride meld_axis (axis %d) [%lld, %lld) out of range [0, %lld)",
608
+ (int) ma, (long long) req_lo, (long long) req_hi, (long long) ca->dim[ma]);
609
+ }
610
+ }
611
+
612
+ s = ca->bytes;
613
+ for ( i = ca->ndim - 1; i >= 0; i-- ) { native[i] = s; s *= ca->dim[i]; }
614
+ for ( i = 0; i < ca->ndim; i++ ) {
615
+ if ( strides[i] != native[i] ) { structural = 0; break; }
616
+ }
617
+
618
+ if ( ! structural ) {
619
+ ca_meld_xfer_stride_per_cell(ca, starts, counts, strides, data, dir);
620
+ return;
621
+ }
622
+ if ( ma == 0 ) {
623
+ ca_meld_xfer_stride_ma0(ca, starts, counts, strides, data, dir);
624
+ } else {
625
+ ca_meld_xfer_stride_ma_internal(ca, starts, counts, data, dir);
626
+ }
627
+ }
628
+
629
+ /* ------------------------------------------------------------------- */
630
+ /* xfer_all */
631
+ /* ------------------------------------------------------------------- */
632
+
633
+ /* meld_axis == 0 best path: K contig xfer_all at prefix-sum offsets. */
634
+ static void
635
+ ca_meld_xfer_all_ma0 (CAMeld *ca, void *data, int dir)
636
+ {
637
+ ca_size_t tail_bytes = ca_meld_tail_elements(ca) * ca->bytes;
638
+ char *d = (char *) data;
639
+ int32_t k;
640
+ for ( k = 0; k < ca->n_parents; k++ ) {
641
+ ca_xfer_all(ca->parents[k], d + ca->seg_offset[k] * tail_bytes, dir);
642
+ }
643
+ }
644
+
645
+ /* Internal-axis xfer_all: per-parent slab buf + trailing-chunk memcpy.
646
+ Key observation — for meld_axis in [1, ndim-1], the trailing block from
647
+ meld_axis..ndim-1 is contig in BOTH parent and view (the meld cells at
648
+ [seg_lo..seg_hi) × non-meld inner cells all sit contig in view row-major
649
+ as long as outer axes 0..ma-1 are fixed). So per parent:
650
+ chunk_bytes = p->dim[ma..ndim-1] product * bytes (= trailing contig run)
651
+ outer_ndim = ma (axes to odometer)
652
+ Iterate outer axes 0..ma-1, memcpy one chunk_bytes block per iter.
653
+ Falls out of the K contig xfer_alls into the buf, then this loop scatters
654
+ the per-parent slab into its segmented slot in view.
655
+ The naive per-cell odometer that this replaces cost ~2x eager on M2;
656
+ chunked memcpy approaches memcpy bandwidth (eager parity target). */
657
+ static void
658
+ ca_meld_xfer_all_ma_internal (CAMeld *ca, void *data, int dir)
659
+ {
660
+ int8_t ma = ca->meld_axis;
661
+ int8_t ndim = ca->ndim;
662
+ ca_size_t bytes = ca->bytes;
663
+ ca_size_t view_stride[CA_RANK_MAX];
664
+ char *d = (char *) data;
665
+ int32_t k;
666
+ int8_t a;
667
+ ca_size_t s;
668
+
669
+ s = bytes;
670
+ for ( a = ndim - 1; a >= 0; a-- ) {
671
+ view_stride[a] = s;
672
+ s *= ca->dim[a];
673
+ }
674
+
675
+ for ( k = 0; k < ca->n_parents; k++ ) {
676
+ CArray *p = ca->parents[k];
677
+ ca_size_t seg_lo = ca->seg_offset[k];
678
+ ca_size_t slab_bytes = (ca_size_t) p->elements * bytes;
679
+ ca_size_t chunk_bytes;
680
+ ca_size_t outer_step[CA_RANK_MAX], outer_back[CA_RANK_MAX];
681
+ ca_size_t outer_idx[CA_RANK_MAX];
682
+ volatile VALUE holder;
683
+ char *buf;
684
+ ca_size_t paddr;
685
+ ca_size_t voff;
686
+ int8_t i;
687
+
688
+ buf = ALLOCV_N(char, holder, slab_bytes);
689
+
690
+ if ( dir == CA_XFER_GET ) {
691
+ ca_xfer_all(p, buf, CA_XFER_GET);
692
+ }
693
+
694
+ /* Trailing contig chunk = product(dim[ma..ndim-1]) elements. */
695
+ chunk_bytes = bytes;
696
+ for ( a = ma; a < ndim; a++ ) chunk_bytes *= p->dim[a];
697
+
698
+ /* Outer axes 0..ma-1: build step[]/back[] over view row-major. */
699
+ for ( a = 0; a < ma; a++ ) {
700
+ outer_step[a] = view_stride[a];
701
+ outer_back[a] = (p->dim[a] - 1) * view_stride[a];
702
+ outer_idx[a] = 0;
703
+ }
704
+ paddr = 0;
705
+ voff = seg_lo * view_stride[ma]; /* meld-axis start in view row-major */
706
+
707
+ if ( ma == 0 ) {
708
+ /* Should not reach here (caller dispatches ma==0 to the external
709
+ best path), but guard anyway: one chunk covers the whole parent. */
710
+ if ( dir == CA_XFER_GET ) memcpy(d + voff, buf, chunk_bytes);
711
+ else memcpy(buf, d + voff, chunk_bytes);
712
+ }
713
+ else if ( dir == CA_XFER_GET ) {
714
+ while ( 1 ) {
715
+ memcpy(d + voff, buf + paddr, chunk_bytes);
716
+ paddr += chunk_bytes;
717
+ i = ma - 1;
718
+ while ( i >= 0 ) {
719
+ if ( ++outer_idx[i] < p->dim[i] ) { voff += outer_step[i]; break; }
720
+ outer_idx[i] = 0;
721
+ voff -= outer_back[i];
722
+ i--;
723
+ }
724
+ if ( i < 0 ) break;
725
+ }
726
+ }
727
+ else {
728
+ while ( 1 ) {
729
+ memcpy(buf + paddr, d + voff, chunk_bytes);
730
+ paddr += chunk_bytes;
731
+ i = ma - 1;
732
+ while ( i >= 0 ) {
733
+ if ( ++outer_idx[i] < p->dim[i] ) { voff += outer_step[i]; break; }
734
+ outer_idx[i] = 0;
735
+ voff -= outer_back[i];
736
+ i--;
737
+ }
738
+ if ( i < 0 ) break;
739
+ }
740
+ }
741
+
742
+ if ( dir == CA_XFER_PUT ) {
743
+ ca_xfer_all(p, buf, CA_XFER_PUT);
744
+ }
745
+ ALLOCV_END(holder);
746
+ }
747
+ }
748
+
749
+ static void
750
+ ca_meld_func_xfer_all (void *ap, void *data, int dir)
751
+ {
752
+ CAMeld *ca = (CAMeld *) ap;
753
+ if ( ca->meld_axis == 0 ) {
754
+ ca_meld_xfer_all_ma0(ca, data, dir);
755
+ } else {
756
+ ca_meld_xfer_all_ma_internal(ca, data, dir);
757
+ }
758
+ }
759
+
760
+ /* ------------------------------------------------------------------- */
761
+ /* attach / sync / detach */
762
+ /* ------------------------------------------------------------------- */
763
+
764
+ static void
765
+ ca_meld_func_allocate (void *ap)
766
+ {
767
+ CAMeld *ca = (CAMeld *) ap;
768
+ int32_t k;
769
+ for ( k = 0; k < ca->n_parents; k++ ) {
770
+ ca_attach(ca->parents[k]);
771
+ }
772
+ ca->ptr = xmalloc(ca_length(ca));
773
+ }
774
+
775
+ static void
776
+ ca_meld_func_attach (void *ap)
777
+ {
778
+ CAMeld *ca = (CAMeld *) ap;
779
+ int32_t k;
780
+ for ( k = 0; k < ca->n_parents; k++ ) {
781
+ ca_attach(ca->parents[k]);
782
+ }
783
+ ca->ptr = xmalloc(ca_length(ca));
784
+ ca_meld_func_xfer_all(ca, ca->ptr, CA_XFER_GET);
785
+ }
786
+
787
+ static void
788
+ ca_meld_func_sync (void *ap)
789
+ {
790
+ CAMeld *ca = (CAMeld *) ap;
791
+ int32_t k;
792
+ ca_meld_func_xfer_all(ca, ca->ptr, CA_XFER_PUT);
793
+ for ( k = 0; k < ca->n_parents; k++ ) {
794
+ ca_sync(ca->parents[k]);
795
+ }
796
+ }
797
+
798
+ static void
799
+ ca_meld_func_detach (void *ap)
800
+ {
801
+ CAMeld *ca = (CAMeld *) ap;
802
+ int32_t k;
803
+ xfree(ca->ptr);
804
+ ca->ptr = NULL;
805
+ for ( k = 0; k < ca->n_parents; k++ ) {
806
+ ca_detach(ca->parents[k]);
807
+ }
808
+ }
809
+
810
+ /* ------------------------------------------------------------------- */
811
+ /* fill_data */
812
+ /* ------------------------------------------------------------------- */
813
+
814
+ static void
815
+ ca_meld_func_fill_data (void *ap, void *ptr)
816
+ {
817
+ CAMeld *ca = (CAMeld *) ap;
818
+ int32_t k;
819
+ for ( k = 0; k < ca->n_parents; k++ ) {
820
+ ca_fill(ca->parents[k], ptr);
821
+ }
822
+ }
823
+
824
+ /* ------------------------------------------------------------------- */
825
+ /* create_mask (horizontal propagation, mirrors CAStack) */
826
+ /* ------------------------------------------------------------------- */
827
+
828
+ static void
829
+ ca_meld_func_create_mask (void *ap)
830
+ {
831
+ CAMeld *ca = (CAMeld *) ap;
832
+ CArray **mask_parents;
833
+ volatile VALUE holder;
834
+ int32_t k;
835
+
836
+ mask_parents = ALLOCV_N(CArray *, holder, ca->n_parents);
837
+ for ( k = 0; k < ca->n_parents; k++ ) {
838
+ ca_update_mask(ca->parents[k]);
839
+ if ( ! ca->parents[k]->mask ) {
840
+ ca_create_mask(ca->parents[k]);
841
+ }
842
+ mask_parents[k] = ca->parents[k]->mask;
843
+ }
844
+ ca->mask = (CArray *) ca_meld_new(ca->n_parents, mask_parents, ca->meld_axis);
845
+ ALLOCV_END(holder);
846
+ }
847
+
848
+ /* ------------------------------------------------------------------- */
849
+ /* fold_stride — always declines */
850
+ /* ------------------------------------------------------------------- */
851
+
852
+ static int
853
+ ca_meld_func_fold_stride (void *ap, ca_fold_t *f, void **next_parent)
854
+ {
855
+ (void) ap; (void) f; (void) next_parent;
856
+ return 0; /* fall through to xfer_stride (which handles structural path) */
857
+ }
858
+
859
+ /* ------------------------------------------------------------------- */
860
+ /* operation table */
861
+ /* ------------------------------------------------------------------- */
862
+
863
+ ca_operation_function_t ca_meld_func = {
864
+ -1, /* CA_OBJ_MELD */
865
+ CA_VIEW_ARRAY,
866
+ free_ca_meld,
867
+ ca_meld_func_clone,
868
+ ca_meld_func_allocate,
869
+ ca_meld_func_attach,
870
+ ca_meld_func_sync,
871
+ ca_meld_func_detach,
872
+ ca_meld_func_fill_data,
873
+ ca_meld_func_create_mask,
874
+ ca_meld_func_xfer_index,
875
+ ca_meld_func_xfer_addrs,
876
+ ca_meld_func_fold_stride,
877
+ ca_meld_func_xfer_stride,
878
+ ca_meld_func_xfer_all,
879
+ };
880
+
881
+ /* ------------------------------------------------------------------- */
882
+ /* Ruby surface */
883
+ /* ------------------------------------------------------------------- */
884
+
885
+ VALUE
886
+ rb_ca_meld_new (VALUE parents_ary, int8_t meld_axis)
887
+ {
888
+ volatile VALUE obj;
889
+ CAMeld *ca;
890
+ CArray **parents;
891
+ volatile VALUE holder;
892
+ long n, i;
893
+
894
+ Check_Type(parents_ary, T_ARRAY);
895
+ n = RARRAY_LEN(parents_ary);
896
+ if ( n <= 0 ) {
897
+ rb_raise(rb_eArgError, "CAMeld requires at least one parent");
898
+ }
899
+ parents = ALLOCV_N(CArray *, holder, n);
900
+ for ( i = 0; i < n; i++ ) {
901
+ VALUE p = rb_ary_entry(parents_ary, i);
902
+ rb_check_carray_object(p);
903
+ TypedData_Get_Struct(p, CArray, &carray_data_type, parents[i]);
904
+ }
905
+ ca = ca_meld_new((int32_t) n, parents, meld_axis);
906
+ obj = ca_wrap_struct(ca);
907
+ rb_ivar_set(obj, id_parents, rb_ary_dup(parents_ary));
908
+ rb_ca_set_parent(obj, rb_ary_entry(parents_ary, 0));
909
+ ALLOCV_END(holder);
910
+ return obj;
911
+ }
912
+
913
+ static VALUE
914
+ rb_ca_meld_s_allocate (VALUE klass)
915
+ {
916
+ CAMeld *ca;
917
+ return TypedData_Make_Struct(klass, CAMeld, &cameld_data_type, ca);
918
+ }
919
+
920
+ static VALUE
921
+ rb_ca_meld_initialize_copy (VALUE self, VALUE other)
922
+ {
923
+ CAMeld *ca, *cs;
924
+ TypedData_Get_Struct(self, CAMeld, &cameld_data_type, ca);
925
+ TypedData_Get_Struct(other, CAMeld, &cameld_data_type, cs);
926
+ ca_meld_setup(ca, cs->n_parents, cs->parents, cs->meld_axis);
927
+ return self;
928
+ }
929
+
930
+ /* CAMeld#initialize(list, axis: 0) */
931
+ static VALUE
932
+ rb_ca_meld_initialize (int argc, VALUE *argv, VALUE self)
933
+ {
934
+ CAMeld *ca;
935
+ CArray **parents;
936
+ volatile VALUE holder;
937
+ VALUE list, kwargs, axis_val = Qnil;
938
+ int8_t meld_axis = 0;
939
+ long n, i;
940
+
941
+ rb_scan_args(argc, argv, "1:", &list, &kwargs);
942
+ Check_Type(list, T_ARRAY);
943
+ rb_scan_options(kwargs, "axis", &axis_val);
944
+ n = RARRAY_LEN(list);
945
+ if ( n <= 0 ) {
946
+ rb_raise(rb_eArgError, "CAMeld.new requires at least one parent");
947
+ }
948
+ if ( ! NIL_P(axis_val) ) {
949
+ CArray *ref;
950
+ VALUE first = rb_ary_entry(list, 0);
951
+ rb_check_carray_object(first);
952
+ TypedData_Get_Struct(first, CArray, &carray_data_type, ref);
953
+ meld_axis = (int8_t) rb_ca_normalize_axis_for_ndim(
954
+ NUM2LONG(axis_val), (int) ref->ndim, "CAMeld.new");
955
+ }
956
+ TypedData_Get_Struct(self, CAMeld, &cameld_data_type, ca);
957
+ parents = ALLOCV_N(CArray *, holder, n);
958
+ for ( i = 0; i < n; i++ ) {
959
+ VALUE p = rb_ary_entry(list, i);
960
+ rb_check_carray_object(p);
961
+ TypedData_Get_Struct(p, CArray, &carray_data_type, parents[i]);
962
+ }
963
+ ca_meld_setup(ca, (int32_t) n, parents, meld_axis);
964
+ rb_ivar_set(self, id_parents, rb_ary_dup(list));
965
+ rb_ca_set_parent(self, rb_ary_entry(list, 0));
966
+ ALLOCV_END(holder);
967
+ return self;
968
+ }
969
+
970
+ static VALUE
971
+ rb_ca_meld_n_parents (VALUE self)
972
+ {
973
+ CAMeld *ca = (CAMeld *) DATA_PTR(self);
974
+ return INT2NUM(ca->n_parents);
975
+ }
976
+
977
+ static VALUE
978
+ rb_ca_meld_parents (VALUE self)
979
+ {
980
+ return rb_ivar_get(self, id_parents);
981
+ }
982
+
983
+ static VALUE
984
+ rb_ca_meld_meld_axis (VALUE self)
985
+ {
986
+ CAMeld *ca = (CAMeld *) DATA_PTR(self);
987
+ return INT2NUM((int) ca->meld_axis);
988
+ }
989
+
990
+ /* Ruby-visible segment offsets (K+1 entries, prefix sum along meld_axis). */
991
+ static VALUE
992
+ rb_ca_meld_seg_offsets (VALUE self)
993
+ {
994
+ CAMeld *ca = (CAMeld *) DATA_PTR(self);
995
+ VALUE ary = rb_ary_new_capa(ca->n_parents + 1);
996
+ int32_t k;
997
+ for ( k = 0; k <= ca->n_parents; k++ ) {
998
+ rb_ary_push(ary, LL2NUM((long long) ca->seg_offset[k]));
999
+ }
1000
+ return ary;
1001
+ }
1002
+
1003
+ void
1004
+ Init_ca_obj_meld (void)
1005
+ {
1006
+ /* CAMultiParent layout convention check. */
1007
+ if ( offsetof(CAMeld, n_parents) != offsetof(CAMultiParent, n_parents) ||
1008
+ offsetof(CAMeld, parents) != offsetof(CAMultiParent, parents) ) {
1009
+ rb_raise(rb_eRuntimeError,
1010
+ "CAMeld/CAMultiParent layout mismatch (build error)");
1011
+ }
1012
+
1013
+ rb_cCAMeld = rb_define_class("CAMeld", rb_cCAView);
1014
+ rb_cCAMeldMask = rb_define_class("CAMeldMask", rb_cCAMeld);
1015
+
1016
+ CA_OBJ_MELD = ca_install_obj_type(rb_cCAMeld,
1017
+ &cameld_data_type,
1018
+ rb_cCAMeldMask,
1019
+ &cameld_mask_data_type, &ca_meld_func, sizeof(ca_meld_func));
1020
+ rb_define_const(rb_cObject, "CA_OBJ_MELD", INT2NUM(CA_OBJ_MELD));
1021
+
1022
+ id_parents = rb_intern("parents");
1023
+
1024
+ rb_define_alloc_func(rb_cCAMeld, rb_ca_meld_s_allocate);
1025
+ rb_define_method(rb_cCAMeld, "initialize",
1026
+ rb_ca_meld_initialize, -1);
1027
+ rb_define_method(rb_cCAMeld, "initialize_copy",
1028
+ rb_ca_meld_initialize_copy, 1);
1029
+
1030
+ rb_define_method(rb_cCAMeld, "n_parents", rb_ca_meld_n_parents, 0);
1031
+ rb_define_method(rb_cCAMeld, "parents", rb_ca_meld_parents, 0);
1032
+ rb_define_method(rb_cCAMeld, "meld_axis", rb_ca_meld_meld_axis, 0);
1033
+ rb_define_method(rb_cCAMeld, "seg_offsets", rb_ca_meld_seg_offsets, 0);
1034
+ }