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,305 @@
1
+ # CASlabIterator.
2
+ #
3
+ # Created by the C indexer dispatch when an index uses the `:>` slab-axis
4
+ # sigil (e.g. `ca[nil, :>, :>]`). It is a thin sugar over each_slab /
5
+ # map_slab / reduce_slab: `:>` axes become the slab handed to the block;
6
+ # the remaining (sliced) axes are the outer iteration space.
7
+ #
8
+ # ca[nil, :>].each { |row| ... } # = ca.each_slab(axis: 1)
9
+ # ca[2..5, :>, :>].map { |slab| ... } # = ca[2..5,nil,nil].map_slab(axis: [-2,-1])
10
+ #
11
+ # Surface is intentionally minimal (each / map / reduce); to_a / count /
12
+ # size and other Enumerable extras are deferred.
13
+ #
14
+ # Naming note: distinct from the C-defined `CArray::SlabIterator` engine
15
+ # in ext/carray_slab.c -- this is the Ruby-level sigil iterator that
16
+ # delegates to each_slab / map_slab / reduce_slab. Loaded lazily via
17
+ # autoload from lib/carray/autoload/autoload_base.rb on first
18
+ # `ca[..., :>]` evaluation.
19
+
20
+ class CASlabIterator < CAIterator
21
+ # Built from C via `CASlabIterator.new(reference, slab_axes)`: `reference`
22
+ # is the sliced base view (with `:>` axes as full range), `slab_axes` are
23
+ # the positions marked with `:>`.
24
+ def initialize (reference, slab_axes)
25
+ @reference = reference
26
+ @slab_axes = slab_axes
27
+ rdim = reference.shape
28
+ @outer_positions = (0...reference.ndim).reject { |k| slab_axes.include?(k) }.freeze
29
+ @shape = @outer_positions.map { |k| rdim[k] }
30
+ @ndim = @shape.size
31
+ self
32
+ end
33
+
34
+ # The array being iterated (slab exposes it as `reference`; block / window
35
+ # iterators expose theirs as `source`). The base has no common accessor.
36
+ attr_reader :reference, :slab_axes
37
+
38
+ # Slab view at outer index `idx` (Array, length = self.ndim). Returns
39
+ # `@reference[*full_idx]` where slab axes are nil (full range) and
40
+ # outer axes take their values from idx.
41
+ def kernel_at_index (idx)
42
+ full = Array.new(@reference.ndim) # nil at every position
43
+ @outer_positions.each_with_index { |k, i| full[k] = idx[i] }
44
+ @reference[*full]
45
+ end
46
+
47
+ # @overload each { |slab| ... }
48
+ # Yields each slab as an inner CArray. Without a block, returns
49
+ # an `Enumerator` from `each_slab`.
50
+ # @yieldparam slab [CArray]
51
+ # @return [Enumerator, self]
52
+ def each (&block)
53
+ return @reference.each_slab(axis: @slab_axes) unless block
54
+ @reference.each_slab(axis: @slab_axes, &block)
55
+ end
56
+
57
+ # @overload map { |slab| ... }
58
+ # Returns a new CArray built by applying the block to each slab,
59
+ # delegating to `map_slab`.
60
+ # @yieldparam slab [CArray]
61
+ # @yieldreturn [Object] per-slab result.
62
+ # @return [CArray]
63
+ def map (&block)
64
+ @reference.map_slab(axis: @slab_axes, &block)
65
+ end
66
+
67
+ # @overload reduce { |acc, slab| ... }
68
+ # Reduces the slabs with `reduce_slab`. The block-only form uses
69
+ # the first slab as the seed; the `init` form starts from `init`.
70
+ # @yieldparam acc [Object] running accumulator.
71
+ # @yieldparam slab [CArray] next slab.
72
+ # @yieldreturn [Object] updated accumulator.
73
+ # @return [Object]
74
+ # @overload reduce(init) { |acc, slab| ... }
75
+ # @param init [Object] initial accumulator value.
76
+ # @yieldparam acc [Object]
77
+ # @yieldparam slab [CArray]
78
+ # @yieldreturn [Object]
79
+ # @return [Object]
80
+ def reduce (*args, &block)
81
+ if args.empty?
82
+ @reference.reduce_slab(axis: @slab_axes, &block)
83
+ else
84
+ @reference.reduce_slab(axis: @slab_axes, init: args[0], &block)
85
+ end
86
+ end
87
+
88
+ # ---- named reductions -------------------------------------------------
89
+ #
90
+ # A per-slab reduction that folds each slab to one value is exactly the core
91
+ # per-axis reduction over the slab axes, so every reduction is a direct
92
+ # delegation to `reference.<op>(axis: slab_axes)`. This inherits the core
93
+ # data type, mask, empty/all-masked (identity vs UNDEF) and epsilon-close
94
+ # contracts unchanged -- there is no separate slab reduction kernel. (Unlike
95
+ # the map / reduce block surface, these are mask-aware: they route through the
96
+ # core reduction, which handles masked sources.)
97
+
98
+ # @overload sum
99
+ # Per-slab sum, delegating to `reference.sum(axis: slab_axes)`.
100
+ # @return [CArray] one value per slab (shape = self.dim minus the slab axes)
101
+ # @overload accumulate
102
+ # Per-slab sum kept in the source's own data type, wrapping at its width,
103
+ # as the core `accumulate` does -- `sum` answers in the type the core
104
+ # promotes to (float64 for integers).
105
+ # @return [CArray] one value per slab
106
+ # The rest are analogous: prod / mean / min / max, sample and population
107
+ # variance / stddev, all / any, fused minmax ([min, max] pair), the axis-local
108
+ # position min_index / max_index (index within the slab axes), and the flat
109
+ # source address min_addr / max_addr (which source cell holds the extremum).
110
+ [:sum, :accumulate, :prod, :mean, :min, :max, :variance, :stddev, :all, :any,
111
+ :variancep, :stddevp, :minmax, :min_index, :max_index,
112
+ :min_addr, :max_addr].each do |op|
113
+ class_eval <<~RUBY, __FILE__, __LINE__ + 1
114
+ def #{op}
115
+ @reference.#{op}(axis: @slab_axes)
116
+ end
117
+ RUBY
118
+ end
119
+
120
+ # @overload count(v = <none>)
121
+ # Per-slab count, delegating to `reference.count(..., axis: slab_axes)`.
122
+ # No argument counts present (non-masked) cells; `count(UNDEF)` counts
123
+ # masked cells; `count(v)` counts cells equal to `v`.
124
+ # @return [CArray] one count per slab
125
+ def count (*args)
126
+ return count_not_masked if args.empty?
127
+ # CABlock / CAWindow shadow #count with a geometry accessor, and @reference
128
+ # may be such a view, so dispatch CArray#count explicitly for count(v).
129
+ CArray.instance_method(:count).bind_call(@reference, *args, axis: @slab_axes)
130
+ end
131
+
132
+ # @overload count_not_masked
133
+ # Per-slab count of present (non-masked) cells.
134
+ # @return [CArray]
135
+ def count_not_masked
136
+ @reference.count_not_masked(axis: @slab_axes)
137
+ end
138
+
139
+ # @overload count_masked
140
+ # Per-slab count of masked cells.
141
+ # @return [CArray]
142
+ def count_masked
143
+ @reference.count_masked(axis: @slab_axes)
144
+ end
145
+
146
+ # @overload elements
147
+ # Per-slab cell count (structural, mask-independent). Every slab has the
148
+ # same shape, so this is a constant array shaped like the outer iteration
149
+ # space.
150
+ # @return [CArray]
151
+ def elements
152
+ sz = @slab_axes.inject(1) { |p, ax| p * @reference.shape[ax] }
153
+ # count_not_masked gives the correct output shape (and, unlike bare #count,
154
+ # is not shadowed by CABlock / CAWindow); overwrite with the constant size.
155
+ out = @reference.count_not_masked(axis: @slab_axes)
156
+ out[] = sz
157
+ out
158
+ end
159
+
160
+ # ---- order statistics (tier 3) ----------------------------------------
161
+ #
162
+ # Core order statistics take a single integer axis, so a single slab axis is
163
+ # delegated directly (fast C path) and a multi-axis slab is folded per slab
164
+ # with reduce_slab (each slab flattened). As with `CArray#median(axis:)`, a
165
+ # masked source raises (the known per-axis masked limitation); strip the mask
166
+ # with `ca.value` first if needed.
167
+
168
+ # @overload median
169
+ # Per-slab median.
170
+ # @return [CArray]
171
+ def median
172
+ if @slab_axes.size == 1
173
+ @reference.median(axis: @slab_axes[0])
174
+ else
175
+ @reference.reduce_slab(axis: @slab_axes) { |s| s.median }
176
+ end
177
+ end
178
+
179
+ # @overload percentile(*pers)
180
+ # Per-slab percentile(s). One argument returns one CArray; several return an
181
+ # array of CArrays (as `CArray#percentile`).
182
+ # @return [CArray, Array<CArray>]
183
+ def percentile (*pers)
184
+ if @slab_axes.size == 1
185
+ @reference.percentile(*pers, axis: @slab_axes[0])
186
+ else
187
+ rs = pers.map { |p| @reference.reduce_slab(axis: @slab_axes) { |s| s.percentile(p) } }
188
+ pers.size == 1 ? rs[0] : rs
189
+ end
190
+ end
191
+
192
+ # @overload quantile
193
+ # Per-slab five-number summary `[min, Q1, median, Q3, max]` (five CArrays),
194
+ # as `CArray#quantile`.
195
+ # @return [Array<CArray>]
196
+ def quantile
197
+ if @slab_axes.size == 1
198
+ @reference.quantile(axis: @slab_axes[0])
199
+ else
200
+ [0, 25, 50, 75, 100].map { |p|
201
+ @reference.reduce_slab(axis: @slab_axes) { |s| s.percentile(p) }
202
+ }
203
+ end
204
+ end
205
+
206
+ # ---- order surface ----------------------------------------------------
207
+ #
208
+ # sort_addr / sort_index lift the core order surface per slab. Core sort takes
209
+ # a single axis, so a one-axis slab delegates straight to the C path (unlike
210
+ # the reductions above, sort does not accept an axis array). A multi-axis slab
211
+ # has no single sort axis, so it raises; sort one axis at a time.
212
+
213
+ # @overload sort_addr
214
+ # Per-slab sort by flat source address. Delegates to
215
+ # `reference.sort_addr(axis: slab_axis)`: reference-shaped, each slab's cells
216
+ # carry the flat source addresses that sort that slab ascending.
217
+ # @return [CArray] reference-shaped int64
218
+ def sort_addr
219
+ @reference.sort_addr(axis: single_sort_axis(:sort_addr))
220
+ end
221
+
222
+ # @overload sort_index
223
+ # Per-slab sort by axis-local index (usable with take_along_axis).
224
+ # Delegates to `reference.sort_index(axis: slab_axis)`: reference-shaped, the
225
+ # axis-local rank order within the slab axis.
226
+ # @return [CArray] reference-shaped int64
227
+ def sort_index
228
+ @reference.sort_index(axis: single_sort_axis(:sort_index))
229
+ end
230
+
231
+ # ---- weighted (tier 2) ------------------------------------------------
232
+ #
233
+ # Delegated to the core weighted reduction over the slab axes; `weights` has
234
+ # the same shape as the reference (one weight per cell). Core wsum / wmean
235
+ # accept multi-axis, so this covers multi-axis slabs directly.
236
+
237
+ # @overload wsum(weights)
238
+ # Per-slab weighted sum, `weights` shaped like the reference.
239
+ # @return [CArray]
240
+ def wsum (weights)
241
+ @reference.wsum(weights, axis: @slab_axes)
242
+ end
243
+
244
+ # @overload wmean(weights)
245
+ # Per-slab weighted mean, `weights` shaped like the reference.
246
+ # @return [CArray]
247
+ def wmean (weights)
248
+ @reference.wmean(weights, axis: @slab_axes)
249
+ end
250
+
251
+ # ---- segment scan: within-slab running statistics ---------------------
252
+ #
253
+ # A per-slab running accumulation along the slab axis is exactly the core
254
+ # per-axis cumulative over that axis, so every scan delegates to
255
+ # `reference.<op>(axis: slab_axis)`, inheriting the core data type / mask (masked
256
+ # cells hold the running total, output unmasked) contracts unchanged. Each
257
+ # cell is in exactly one slab (a partition), so the running value is
258
+ # well-defined; the surface is uniform with the family even though it
259
+ # coincides with a plain per-axis cumulative. Core scan takes a single axis (a
260
+ # multi-axis running accumulation is ambiguous), so a multi-axis slab raises --
261
+ # scan one axis at a time, like the order surface.
262
+
263
+ # @overload cumsum
264
+ # Per-slab inclusive running sum (float64), reference-shaped.
265
+ # @return [CArray]
266
+ # @overload cumprod
267
+ # Per-slab inclusive running product (float64), reference-shaped.
268
+ # @return [CArray]
269
+ # @overload cummax
270
+ # Per-slab inclusive running maximum (reference data type), reference-shaped.
271
+ # @return [CArray]
272
+ # @overload cummin
273
+ # Per-slab inclusive running minimum (reference data type), reference-shaped.
274
+ # @return [CArray]
275
+ # @overload cumcount
276
+ # Per-slab running count of present cells (int64), reference-shaped.
277
+ # @return [CArray]
278
+ [:cumsum, :cumprod, :cummax, :cummin, :cumcount].each do |op|
279
+ class_eval <<~RUBY, __FILE__, __LINE__ + 1
280
+ def #{op}
281
+ @reference.#{op}(axis: single_scan_axis(:#{op}))
282
+ end
283
+ RUBY
284
+ end
285
+
286
+ private
287
+
288
+ # The single slab axis for the order surface (core sort takes one integer
289
+ # axis). A multi-axis slab has no single sort axis, so raise.
290
+ def single_sort_axis (op)
291
+ return @slab_axes[0] if @slab_axes.size == 1
292
+ raise ArgumentError,
293
+ "#{op}: a multi-axis slab (axes #{@slab_axes.inspect}) has no single " \
294
+ "sort axis; sort one axis at a time"
295
+ end
296
+
297
+ # The single slab axis for a segment scan (core scan takes one integer axis).
298
+ # A multi-axis slab has no single scan axis, so raise.
299
+ def single_scan_axis (op)
300
+ return @slab_axes[0] if @slab_axes.size == 1
301
+ raise ArgumentError,
302
+ "#{op}: a multi-axis slab (axes #{@slab_axes.inspect}) has no single " \
303
+ "scan axis; scan one axis at a time"
304
+ end
305
+ end
@@ -0,0 +1,291 @@
1
+ # CAStack view-default composition surface
2
+ #
3
+ # This file holds the composition methods that build (directly or via
4
+ # reshape/transpose) a CAStack view from storage-uniform pieces:
5
+ #
6
+ # stack = K parents pushed onto a new K axis
7
+ # meld = melt + weld; pieces dissolve their boundaries along the
8
+ # named axis and are regarded as one (uniform)
9
+ # montage = uniform tiles arranged in a regular grid (ImageMagick)
10
+ #
11
+ # Their ragged eager counterparts (concatenate / mosaic / tabulate) live in
12
+ # carray/methods/composition.rb. See that file's header for the full
13
+ # composition cheat sheet.
14
+ #
15
+ # Examples (a, b, c are each shape [3, 4]):
16
+ #
17
+ # CArray.stack([a, b, c]) #=> view, shape (3, 3, 4) (K outermost)
18
+ # CArray.stack([a, b, c], axis: -1) #=> view, shape (3, 4, 3) (RGB pattern)
19
+ # CArray.meld([a, b, c]) #=> view, shape (9, 4)
20
+ # CArray.meld([a, b, c], axis: 1) #=> view, shape (3, 12)
21
+ # CArray.montage([a, b, c, d], [2, 2], axis: 0) #=> view, shape (6, 8) (2x2 image tile)
22
+ #
23
+ # Known limitation: scalar indexing on a CAStack-rooted view drops ndim
24
+ # and triggers a full
25
+ # materialise fallback. Use range indexing (e.g. `meld(...)[k..k, nil]`
26
+ # instead of `meld(...)[k, nil]`) for partial-use perf, or `.to_ca` upfront
27
+ # to materialise eagerly.
28
+
29
+ class CArray
30
+
31
+ # ---------------------------------------------------------------- view-default
32
+
33
+ # Stack `list` of CArrays along a new axis inserted at position
34
+ # `axis:` (default 0 = outermost). Returns a view
35
+ # (CAStack with k_axis = axis) when inputs are storage-uniform, or a
36
+ # Face-lifted view (= CATime, CATimedelta, ...) when inputs are
37
+ # homogeneous Face instances. Call `.to_ca` to materialise eagerly.
38
+ #
39
+ # `data_type:` kwarg (optional, primitive Symbol only) forces primitive
40
+ # promotion; cannot be used when the list contains Face elements. Class
41
+ # / Module targets are rejected (= `data_type: CATime` is invalid;
42
+ # use auto-detect for Face round-trip).
43
+ #
44
+ # 3.0 (post-K_AXIS, F.S1-stack landed): replaces `CArray.merge`. The
45
+ # low-level raw constructor is `CAStack.new(list, axis:)`; this method
46
+ # is the high-level surface that performs promote_list + CAStack.new +
47
+ # (face_lift when homogeneous Face).
48
+ # @overload stack(list, axis: 0, data_type: nil)
49
+ # Returns a view stacking uniform-shape arrays along a new K
50
+ # axis at position `axis`. Runs `promote_list` for a common
51
+ # `data_type` and re-wraps homogeneous Face inputs via
52
+ # `face_lift`. Output `ndim` is one greater than each piece.
53
+ # @param list [Array<CArray>] pieces to stack; must not be empty.
54
+ # @param axis [Integer] position of the new K axis.
55
+ # @param data_type [Symbol, Integer, nil] result `data_type`;
56
+ # inferred when `nil`.
57
+ # @return [CArray] CAStack view.
58
+ # @raise [ArgumentError] when `list` is empty.
59
+ def self.stack (list, axis: 0, data_type: nil)
60
+ raise ArgumentError, "stack: list must not be empty" if list.empty?
61
+ list = CArray.promote_list(list, data_type: data_type)
62
+ axis = CArray.normalize_axis(axis, list[0].ndim + 1, "stack")
63
+ CAStack.new(list, axis: axis) # CAStack.new does Face lift internally
64
+ end
65
+
66
+ # Returns a {CAMeld} view of the arrays welded along an existing axis.
67
+ # No data is copied; reads gather from parents on demand and writes flow
68
+ # back to them (chain composability preserved).
69
+ #
70
+ # Pieces must agree on `ndim`, `data_type`, byte width, and every axis
71
+ # length except `axis` (the "meld axis"). Mismatched `data_type` raises:
72
+ # cast the pieces yourself (`.to_type(:float64)`) or use
73
+ # {CArray.concatenate} (eager, auto-casts).
74
+ #
75
+ # "meld" = melt + weld — pieces dissolve their boundaries along the named
76
+ # axis and are regarded as one.
77
+ #
78
+ # @overload meld(*arrays, axis: 0)
79
+ # @overload meld(list, axis: 0)
80
+ # Convenience form: a single Array argument is treated as the list.
81
+ # @param arrays [Array<CArray>] pieces to weld. A single Array
82
+ # argument is accepted for compatibility with older callers.
83
+ # @param axis [Integer] existing axis to extend (normalises negative
84
+ # values against the reference ndim).
85
+ # @return [CAMeld] view over the welded pieces.
86
+ # @raise [ArgumentError] when the list is empty, ndim mismatch,
87
+ # data_type mismatch, or non-axis dim mismatch across pieces
88
+ # (surfaced by CAMeld.new).
89
+ def self.meld (*arrays, axis: 0)
90
+ if arrays.length == 1 && arrays[0].is_a?(Array)
91
+ arrays = arrays[0]
92
+ end
93
+ raise ArgumentError, "meld: list must not be empty" if arrays.empty?
94
+ first = arrays[0]
95
+ unless first.is_a?(CArray)
96
+ raise ArgumentError, "meld: entries must be CArray (got #{first.class})"
97
+ end
98
+ axis_norm = CArray.normalize_axis(axis, first.ndim, "meld")
99
+ # Flatten nested CAMeld inputs that share our meld axis: they already
100
+ # describe a segment sequence, so absorbing their parents keeps chain
101
+ # depth at 1 (avoids 2-level xfer_all / reduce chains through the
102
+ # intermediate CAMeld). A CAMeld with a different meld_axis is left
103
+ # intact — its segment structure is orthogonal.
104
+ if arrays.any? { |a| a.is_a?(CAMeld) && a.meld_axis == axis_norm }
105
+ arrays = arrays.flat_map { |a|
106
+ a.is_a?(CAMeld) && a.meld_axis == axis_norm ? a.parents : [a]
107
+ }
108
+ end
109
+ CAMeld.new(arrays, axis: axis_norm)
110
+ end
111
+ end
112
+
113
+ class CArray
114
+ # Returns a {CAMeld} view of `[self, *others]` welded along `axis`.
115
+ # Instance form of {CArray.meld}; non-destructive, see the class method
116
+ # for full semantics.
117
+ #
118
+ # @overload meld(*others, axis: 0)
119
+ # @param others [Array<CArray>] additional pieces.
120
+ # @param axis [Integer] existing axis to extend.
121
+ # @return [CAMeld]
122
+ # @raise [ArgumentError] when no `others` are given.
123
+ def meld (*others, axis: 0)
124
+ raise ArgumentError, "meld: at least one other array required" if others.empty?
125
+ CArray.meld(self, *others, axis: axis)
126
+ end
127
+
128
+ # Arrange `list` of uniform-shape pieces in a `tdim`-shape grid that
129
+ # extends parent axes `axis..axis+tdim.size-1` by the corresponding
130
+ # `tdim[i]` factor (= ImageMagick `montage` analog). Output ndim equals
131
+ # each piece's ndim; the tile axes occupy positions
132
+ # `axis..axis+tdim.size-1`. Returns a view; call `.to_ca` to materialise.
133
+ #
134
+ # `tdim.product` must equal `list.size`. For non-uniform pieces along
135
+ # tile axes, use `CArray.mosaic`.
136
+ #
137
+ # Example (parent shape (3, 4), 6-element list, tdim=[2, 3], axis: 0):
138
+ #
139
+ # CArray.montage([a, b, c, d, e, f], [2, 3], axis: 0)
140
+ # #=> shape (6, 12) -- 2 rows x 3 cols grid of (3, 4) blocks
141
+ # # +-----+-----+-----+
142
+ # # | a | b | c | rows 0..2
143
+ # # +-----+-----+-----+
144
+ # # | d | e | f | rows 3..5
145
+ # # +-----+-----+-----+
146
+ #
147
+ # 3.0 (post K_AXIS / promote_list / stack rename): renamed from `combine`
148
+ # (= 20-year vocabulary that didn't describe the action). Positional
149
+ # `at` replaced with `axis:` kwarg for consistency with bind / stack.
150
+ # Parameter order changed from `(tdim, list, at)` to `(list, tdim, axis:)`
151
+ # to align with bind / stack (`list` first).
152
+ # @overload montage(list, tdim, axis: 0, data_type: nil)
153
+ # Returns a view arranging uniform-shape pieces in a `tdim`-shape
154
+ # grid that extends parent axes `axis..axis+tdim.size-1` by the
155
+ # corresponding `tdim[i]` factors. Output `ndim` equals each
156
+ # piece's `ndim`. `tdim.product` must equal `list.size`. For
157
+ # non-uniform pieces along tile axes, use `CArray.mosaic`.
158
+ # @param list [Array<CArray>] pieces to arrange.
159
+ # @param tdim [Array<Integer>] tile grid shape.
160
+ # @param axis [Integer] first tile axis in the result.
161
+ # @param data_type [Symbol, Integer, nil] result `data_type`.
162
+ # @return [CArray] tiled view.
163
+ # @raise [ArgumentError] when `list` is empty or `tdim.product !=
164
+ # list.size`.
165
+ def self.montage (list, tdim, axis: 0, data_type: nil)
166
+ raise ArgumentError, "montage: list must not be empty" if list.empty?
167
+ unless tdim.is_a?(Array) && tdim.size > 0
168
+ raise ArgumentError, "montage: tdim must be a non-empty Array of Integer"
169
+ end
170
+ expected = tdim.inject(1) { |acc, n| acc * n }
171
+ unless expected == list.size
172
+ raise ArgumentError,
173
+ "montage: tdim product (#{expected}) must equal list size (#{list.size})"
174
+ end
175
+
176
+ list = CArray.promote_list(list, data_type: data_type)
177
+ parent_shape = list[0].shape
178
+ ntile = tdim.size
179
+ nparent = parent_shape.size
180
+ axis = CArray.normalize_axis(axis, nparent - ntile + 1, "montage")
181
+
182
+ s = CArray.stack(list).reshape(*tdim, *parent_shape) # (K, *) → (*tdim, *)
183
+
184
+ # Interleave: tile axis i (= s axis i, i ∈ [0, ntile)) is moved to
185
+ # just before parent axis (axis + i) in s coordinates (= s axis
186
+ # ntile + axis + i).
187
+ perm = []
188
+ nparent.times do |j|
189
+ if j.between?(axis, axis + ntile - 1)
190
+ perm << (j - axis) # tile axis
191
+ end
192
+ perm << ntile + j # parent axis
193
+ end
194
+ s = s.transpose(*perm)
195
+
196
+ # Merge each (tile[i], parent[axis+i]) pair via reshape.
197
+ new_shape = parent_shape.dup
198
+ ntile.times { |i| new_shape[axis + i] *= tdim[i] }
199
+ s.reshape(*new_shape)
200
+ end
201
+
202
+ # Instance-side stack: build a new K-stack from `[self] + others`
203
+ # along the new K axis at position `axis:`. Always treats `self` as a
204
+ # parent (= even when self is a CAStack, the resulting stack has self
205
+ # as one of its parents, NOT flat-appended into self's parents).
206
+ #
207
+ # For flat-appending into an existing CAStack (= same k_axis, parents
208
+ # extended), use `CAStack#append`.
209
+ #
210
+ # 3.0: high-level Face-aware surface, mirrors `CArray.stack(list, axis:)`.
211
+ # @overload stack(*others, axis: 0, data_type: nil)
212
+ # Returns a K-stack view built from `[self] + others` along a
213
+ # new K axis at position `axis`. Always treats `self` as one
214
+ # parent, even when `self` is a CAStack; use {CAStack#append} to
215
+ # flat-append into an existing CAStack.
216
+ # @param others [Array<CArray>] additional parents.
217
+ # @param axis [Integer] position of the new K axis.
218
+ # @param data_type [Symbol, Integer, nil] result `data_type`.
219
+ # @return [CArray] CAStack view.
220
+ # @raise [ArgumentError] when no `others` are given.
221
+ def stack (*others, axis: 0, data_type: nil)
222
+ raise ArgumentError, "stack: at least one other parent required" if others.empty?
223
+ CArray.stack([self] + others, axis: axis, data_type: data_type)
224
+ end
225
+
226
+ # Split self along a single axis into an Array of (ndim-1)-D slices, each
227
+ # a writable CABlock view. The exact inverse of CArray.stack -- split's
228
+ # slices are all the same shape, so they round-trip back through stack:
229
+ #
230
+ # CArray.stack(a.split(axis: k), axis: k) == a
231
+ #
232
+ # a = CA_INT([[1,2,3], [4,5,6]])
233
+ # a.split(axis: 0) #=> [ <[1,2,3]>, <[4,5,6]> ] (row views)
234
+ # a.split(axis: 1) #=> [ <[1,4]>, <[2,5]>, <[3,6]> ] (column views)
235
+ #
236
+ # 3.0 breaking:
237
+ # - returns a Ruby Array of views (was an object CArray), so it
238
+ # round-trips with CArray.stack (which takes an Array)
239
+ # - +axis:+ takes a single Integer (the multi-axis Array form, which
240
+ # returned an N-D object grid, is no longer accepted)
241
+ # - pieces are CABlock views, NOT copies; writing through a piece
242
+ # mutates +self+. Chain +.copy+ / +.to_ca+ for independent entities.
243
+ # @overload split(axis:)
244
+ # Returns an Array of `(ndim-1)`-D writable CABlock views obtained
245
+ # by splitting `self` along `axis`. Inverse of `CArray.stack`.
246
+ # Slices share storage with `self`; chain `.copy` for independent
247
+ # entities.
248
+ # @param axis [Integer] axis to split along.
249
+ # @return [Array<CArray>] one slice per index along `axis`.
250
+ # @raise [ArgumentError] when `axis` is not a single Integer.
251
+ def split (axis:)
252
+ if axis.is_a?(Array)
253
+ raise ArgumentError, "split: axis must be a single Integer"
254
+ end
255
+ k = normalize_axis(axis, "split")
256
+ (0...shape[k]).map do |i|
257
+ idx = [nil] * ndim
258
+ idx[k] = i
259
+ self[*idx]
260
+ end
261
+ end
262
+
263
+ end
264
+
265
+ # CAStack.new(list, axis: 0) is implemented in C (ext/ca_obj_stack.c
266
+ # rb_ca_stack_s_new): it overrides Class#new to inspect Face homogeneity
267
+ # and (when applicable) re-wrap the raw CAStack via ca_face_lift. The
268
+ # Ruby class only adds the #append instance method below.
269
+ class CAStack
270
+ # Flat-append `others` into self's parents, preserving the receiver's
271
+ # k_axis. Returns a new CAStack with the combined parents list.
272
+ #
273
+ # Distinct from `CArray#stack(other)` which always treats self as a
274
+ # single parent of a new K-stack -- here, the existing K-stack is
275
+ # extended in place.
276
+ #
277
+ # Example:
278
+ # s = CArray.stack([a, b], axis: 1) # 2-parent stack, k_axis=1
279
+ # s.append(c, d) # 4-parent stack, k_axis=1
280
+ # @overload append(*others)
281
+ # Returns a new CAStack whose parents are `self.parents + others`,
282
+ # preserving `self.k_axis`. Distinct from `CArray#stack`, which
283
+ # always treats `self` as a single parent of a new K-stack.
284
+ # @param others [Array<CArray>] extra parents to append.
285
+ # @return [CAStack]
286
+ # @raise [ArgumentError] when no `others` are given.
287
+ def append (*others)
288
+ raise ArgumentError, "append: at least one parent required" if others.empty?
289
+ CArray.stack(self.parents + others, axis: self.k_axis)
290
+ end
291
+ end