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,96 @@
1
+ /* ---------------------------------------------------------------------------
2
+ *
3
+ * ca_for_buffer.h -- sweep WHOLE_BUFFER macro family (whole contig buffer
4
+ * handed to author / third-party library)
5
+ *
6
+ * PROPOSAL_L0_AUTHOR_SURFACE L0.2c (2026-06-11). C-side counterpart of
7
+ * Ruby's `ca.attach! { |a| ... }` block: scope the ca_attach + ca_sync +
8
+ * ca_detach lifecycle to a block, hand the author a contig buffer ptr
9
+ * and element count, let the author do whatever (typically pass the
10
+ * ptr to a third-party library: FFTW, akima init, fitpack surf1, ...).
11
+ *
12
+ * Two forms (Q8 = M1 alias-when-possible; non-contig source materialises
13
+ * into a transient buffer; writable form syncs back on exit):
14
+ *
15
+ * CA_WITH_BUFFER(ca, T, ptr, n)
16
+ * Read-only access. ptr : T const * pointing at native contig
17
+ * layout of `ca` (= ca->ptr alias when contig entity, scratch
18
+ * otherwise). n : ca->elements. No write-back on exit.
19
+ *
20
+ * CA_WITH_BUFFER_WRITABLE(ca, T, ptr, n)
21
+ * Writable; same ptr/n semantics, plus ca_sync on block exit so
22
+ * author writes propagate back to the view's storage.
23
+ *
24
+ * Author pattern:
25
+ *
26
+ * double *ptr;
27
+ * ca_size_t n;
28
+ * CA_WITH_BUFFER_WRITABLE(ca, double, ptr, n) {
29
+ * fftw_execute_dft(plan, ptr, ptr);
30
+ * }
31
+ *
32
+ * Constraints (same as CA_FOR_EACH_FIBER family):
33
+ * - `break;` from body exits cleanly (= outer for's advance clause
34
+ * runs ca_sync / ca_detach).
35
+ * - `return;` from body LEAKS the attach; restructure to break.
36
+ * - Macros are NOT statement-equivalent (= nested for); no trailing
37
+ * `else`.
38
+ * - For Ruby-exception-safe lifecycle (e.g. when calling Ruby code or
39
+ * anything that may raise from inside the body), use the function
40
+ * form `rb_ca_call_with_buffer` instead (= rb_ensure-protected).
41
+ *
42
+ * --------------------------------------------------------------------------- */
43
+
44
+ #ifndef CA_FOR_BUFFER_H
45
+ #define CA_FOR_BUFFER_H
46
+
47
+ #include "carray.h"
48
+ #include "ca_sweep_engine.h"
49
+
50
+ /* ---------- macro forms ---------- */
51
+
52
+ /* Read-only: ca_attach (alias-when-possible) + author body + ca_detach. */
53
+ #define CA_WITH_BUFFER(_ca, T, _ptr, _n) \
54
+ for ( CArray *__cwv_ca = (CArray *)(_ca); \
55
+ __cwv_ca; \
56
+ ca_detach(__cwv_ca), __cwv_ca = NULL ) \
57
+ for ( int __cwv_once = (ca_attach(__cwv_ca), \
58
+ (_ptr) = (T *)__cwv_ca->ptr, \
59
+ (_n) = __cwv_ca->elements, \
60
+ 1); \
61
+ __cwv_once; \
62
+ __cwv_once = 0 )
63
+
64
+ /* Writable: same as above + ca_sync on block exit. */
65
+ #define CA_WITH_BUFFER_WRITABLE(_ca, T, _ptr, _n) \
66
+ for ( CArray *__cwvw_ca = (CArray *)(_ca); \
67
+ __cwvw_ca; \
68
+ ca_sync(__cwvw_ca), ca_detach(__cwvw_ca), __cwvw_ca = NULL ) \
69
+ for ( int __cwvw_once = (ca_attach(__cwvw_ca), \
70
+ (_ptr) = (T *)__cwvw_ca->ptr, \
71
+ (_n) = __cwvw_ca->elements, \
72
+ 1); \
73
+ __cwvw_once; \
74
+ __cwvw_once = 0 )
75
+
76
+ /* ---------- function form (rb_ensure-protected, AC8) ----------
77
+ *
78
+ * Use this when the body may raise a Ruby exception (= calling rb_funcall,
79
+ * type-checking with rb_check_type, indirect Ruby code, etc.). The
80
+ * engine ca_attach's the view, runs body via rb_ensure, then guarantees
81
+ * ca_sync (if writable) and ca_detach run before the exception
82
+ * propagates.
83
+ *
84
+ * body_fn(user_data, ptr, n_elements) -> may raise
85
+ *
86
+ * Returns whatever body_fn returns via its own propagation (= the
87
+ * function itself returns Qnil since rb_ensure body must be VALUE).
88
+ * For richer return semantics, box your result in user_data.
89
+ */
90
+ typedef void (*ca_with_buffer_body_fn) (void *user_data, void *ptr,
91
+ ca_size_t n_elements);
92
+
93
+ void rb_ca_call_with_buffer (VALUE r_ca, int writable,
94
+ ca_with_buffer_body_fn body, void *user_data);
95
+
96
+ #endif /* CA_FOR_BUFFER_H */
@@ -0,0 +1,239 @@
1
+ /* ---------------------------------------------------------------------------
2
+ *
3
+ * ca_for_each_element.h -- sweep ELEMENT macro family (single-array, lexical
4
+ * scope body) for ext authors.
5
+ *
6
+ * PROPOSAL_L0_AUTHOR_SURFACE L0.2a (2026-06-11). Builds on the sweep engine
7
+ * helper (ca_sweep_engine.{c,h}, L0.1) for the xfer_all-aware acquire/release
8
+ * lifecycle. Provides 5 macro forms for single-array element-wise loops
9
+ * with author-inline body and lexical-scope state capture.
10
+ *
11
+ * Forms (Q2 = gamma: READ by-value, INOUT / OUT by-lvalue):
12
+ *
13
+ * CA_FOR_EACH_ELEMENT(st1, ca, T, x)
14
+ * Read-only loop; per-iter x = cell value (T rvalue convention but
15
+ * declared lvalue for assignment). Mask policy: NO_MASK (raises if
16
+ * INPUT has mask; use _MASKED form to handle).
17
+ *
18
+ * CA_FOR_EACH_ELEMENT_MASKED(st1, ca, T, x, m)
19
+ * Read-only with mask byte m (1 = masked). Author inspects m and
20
+ * decides; m == 0 if source has no mask.
21
+ *
22
+ * CA_FOR_EACH_ELEMENT_INOUT(st2, ca_in, ca_out, T_IN, T_OUT, in, out)
23
+ * Map kernel; per-iter in = read input, body assigns to out (lvalue),
24
+ * out is written back to ca_out. Strict same-shape check on init
25
+ * (raises on mismatch). NO_MASK on input.
26
+ *
27
+ * CA_FOR_EACH_ELEMENT_INOUT_MASKED(st2, ca_in, ca_out, T_IN, T_OUT,
28
+ * in, out, m_in, m_out)
29
+ * Map kernel with mask; per-iter m_in = input mask byte (or 0 if no
30
+ * input mask), author writes m_out (1 = mark output cell masked) and
31
+ * out. m_out written back to ca_out's mask.
32
+ *
33
+ * CA_FOR_EACH_ELEMENT_OUT(st1, ca_out, T, out)
34
+ * Write-only; body assigns to out (lvalue). Use for init / iota /
35
+ * fill patterns. NO_MASK (output mask cleared).
36
+ *
37
+ * Usage pattern:
38
+ *
39
+ * ca_each_state_t st;
40
+ * double x;
41
+ * CA_FOR_EACH_ELEMENT(st, ca, double, x) {
42
+ * accumulator += x;
43
+ * }
44
+ *
45
+ * Constraints (same as CA_FOR_EACH_FIBER family):
46
+ * - State (st1 / st2) is author-declared on the stack.
47
+ * - `break;` from body exits cleanly (release runs in outer for's
48
+ * teardown clause).
49
+ * - `return;` from body LEAKS scratch buffers / attached views — do
50
+ * not return early from the macro body; restructure to break.
51
+ * - Macros are NOT statement-equivalent (expand to nested for); no
52
+ * trailing `else`.
53
+ * - `T` must be the data_type of `ca` (matched via ca->data_type and
54
+ * ca->bytes — engine does not auto-cast; use rb_ca_wrap_readonly /
55
+ * rb_ca_template_with_type at the call site if cast needed).
56
+ *
57
+ * --------------------------------------------------------------------------- */
58
+
59
+ #ifndef CA_FOR_EACH_ELEMENT_H
60
+ #define CA_FOR_EACH_ELEMENT_H
61
+
62
+ #include "carray.h"
63
+ #include "ca_sweep_engine.h"
64
+
65
+ /* Stack-allocated state holders for 1- and 2-operand sweep ELEMENT macros.
66
+ * Embeds the per-op bookkeeping arrays + the core ca_sweep_state_t in one
67
+ * struct so authors declare just one variable. */
68
+
69
+ typedef struct {
70
+ CArray *cx[1];
71
+ char *base[1];
72
+ ca_size_t stride[1];
73
+ char *owned_buf[1];
74
+ int attached[1];
75
+ char *base_orig[1];
76
+ ca_sweep_state_t core;
77
+ } ca_each_state_t;
78
+
79
+ typedef struct {
80
+ CArray *cx[2];
81
+ char *base[2];
82
+ ca_size_t stride[2];
83
+ char *owned_buf[2];
84
+ int attached[2];
85
+ char *base_orig[2];
86
+ ca_sweep_state_t core;
87
+ } ca_each_map_state_t;
88
+
89
+ /* Internal init helpers: wire the embedded arrays into core, set fsync /
90
+ * no_mask / src_label. Called via comma-expression inside the macro
91
+ * for-loop init clause. */
92
+
93
+ #define CA_SWEEP_WIRE_1_(_st1, _ca0, _fsync, _no_mask, _label) ( \
94
+ (_st1).cx[0] = (CArray *)(_ca0), \
95
+ (_st1).core.n_ops = 1, \
96
+ (_st1).core.fsync = (_fsync), \
97
+ (_st1).core.cx = (_st1).cx, \
98
+ (_st1).core.base = (_st1).base, \
99
+ (_st1).core.stride = (_st1).stride, \
100
+ (_st1).core.owned_buf = (_st1).owned_buf, \
101
+ (_st1).core.attached = (_st1).attached, \
102
+ (_st1).core.base_orig = (_st1).base_orig, \
103
+ (_st1).core.no_mask = (_no_mask), \
104
+ (_st1).core.src_label = (_label))
105
+
106
+ #define CA_SWEEP_WIRE_2_(_st2, _ca0, _ca1, _fsync, _no_mask, _label) ( \
107
+ (_st2).cx[0] = (CArray *)(_ca0), \
108
+ (_st2).cx[1] = (CArray *)(_ca1), \
109
+ (_st2).core.n_ops = 2, \
110
+ (_st2).core.fsync = (_fsync), \
111
+ (_st2).core.cx = (_st2).cx, \
112
+ (_st2).core.base = (_st2).base, \
113
+ (_st2).core.stride = (_st2).stride, \
114
+ (_st2).core.owned_buf = (_st2).owned_buf, \
115
+ (_st2).core.attached = (_st2).attached, \
116
+ (_st2).core.base_orig = (_st2).base_orig, \
117
+ (_st2).core.no_mask = (_no_mask), \
118
+ (_st2).core.src_label = (_label))
119
+
120
+ /* ---------- 5 author-facing macro forms ----------
121
+ *
122
+ * All 5 forms expand to a 3-level nested for:
123
+ * outer : acquire_chunked / release_chunked (= lifecycle scope)
124
+ * middle : ca_sweep_next_chunk loop (= chunk iteration)
125
+ * inner : per-cell loop within chunk_n
126
+ *
127
+ * Memory peak per AC2: INPUT non-alias views materialise into a single
128
+ * chunk scratch (~32KB at f64). m0 (= masked form) is chunk-sized too and
129
+ * re-gathered per chunk, so the macro reads at m0[k] -- indexed within the
130
+ * chunk, not by the flat cell index.
131
+ *
132
+ * For MASKED forms (m / m_in / m_out): when source has no mask, m0 is
133
+ * NULL and m / m_in == 0 always. m_out writes during INOUT_MASKED are
134
+ * silently dropped if no INPUT mask exists (= AC4 mask-creation on
135
+ * INOUT MASKED with no source mask is a future enhancement; current
136
+ * convention follows the FIBER family's MASKED form).
137
+ */
138
+
139
+ /* (1) READ-only, NO_MASK */
140
+ #define CA_FOR_EACH_ELEMENT(_st1, _ca, T, x) \
141
+ for ( int __cfe_init = (CA_SWEEP_WIRE_1_((_st1), (_ca), "0", 1, \
142
+ "CA_FOR_EACH_ELEMENT"), \
143
+ ca_sweep_acquire_chunked(&(_st1).core), 1); \
144
+ __cfe_init; \
145
+ __cfe_init = 0, ca_sweep_release_chunked(&(_st1).core) ) \
146
+ for ( ; ca_sweep_next_chunk(&(_st1).core); ) \
147
+ for ( ca_size_t __cfe_k = 0; \
148
+ __cfe_k < (_st1).core.chunk_n \
149
+ && (((x) = *(T *)((_st1).core.base[0] \
150
+ + __cfe_k * (_st1).core.stride[0])), 1); \
151
+ __cfe_k++ )
152
+
153
+ /* (2) READ-only, MASKED (m is 1 if masked, 0 otherwise; 0 if no source mask) */
154
+ #define CA_FOR_EACH_ELEMENT_MASKED(_st1, _ca, T, x, m) \
155
+ for ( int __cfem_init = (CA_SWEEP_WIRE_1_((_st1), (_ca), "0", 0, \
156
+ "CA_FOR_EACH_ELEMENT_MASKED"), \
157
+ ca_sweep_acquire_chunked(&(_st1).core), 1); \
158
+ __cfem_init; \
159
+ __cfem_init = 0, ca_sweep_release_chunked(&(_st1).core) ) \
160
+ for ( ; ca_sweep_next_chunk(&(_st1).core); ) \
161
+ for ( ca_size_t __cfem_k = 0; \
162
+ __cfem_k < (_st1).core.chunk_n \
163
+ && (((x) = *(T *)((_st1).core.base[0] \
164
+ + __cfem_k * (_st1).core.stride[0])), \
165
+ ((m) = (_st1).core.m0 \
166
+ ? (_st1).core.m0[__cfem_k] \
167
+ : (boolean8_t)0), \
168
+ 1); \
169
+ __cfem_k++ )
170
+
171
+ /* (3) INOUT map (NO_MASK). Strict same-shape check pre-acquire. */
172
+ #define CA_FOR_EACH_ELEMENT_INOUT(_st2, _ca_in, _ca_out, \
173
+ T_IN, T_OUT, in, out) \
174
+ for ( int __cfei_init = ( \
175
+ ca_sweep_check_same_shape((CArray *)(_ca_in), \
176
+ (CArray *)(_ca_out), \
177
+ "CA_FOR_EACH_ELEMENT_INOUT"), \
178
+ CA_SWEEP_WIRE_2_((_st2), (_ca_in), (_ca_out), "01", 1, \
179
+ "CA_FOR_EACH_ELEMENT_INOUT"), \
180
+ ca_sweep_acquire_chunked(&(_st2).core), \
181
+ 1); \
182
+ __cfei_init; \
183
+ __cfei_init = 0, ca_sweep_release_chunked(&(_st2).core) ) \
184
+ for ( ; ca_sweep_next_chunk(&(_st2).core); ) \
185
+ for ( ca_size_t __cfei_k = 0; \
186
+ __cfei_k < (_st2).core.chunk_n \
187
+ && (((in) = *(T_IN *)((_st2).core.base[0] \
188
+ + __cfei_k * (_st2).core.stride[0])), \
189
+ 1); \
190
+ (*(T_OUT *)((_st2).core.base[1] \
191
+ + __cfei_k * (_st2).core.stride[1]) = (out)), \
192
+ __cfei_k++ )
193
+
194
+ /* (4) INOUT map (MASKED). Author writes m_out per cell. */
195
+ #define CA_FOR_EACH_ELEMENT_INOUT_MASKED(_st2, _ca_in, _ca_out, \
196
+ T_IN, T_OUT, \
197
+ in, out, m_in, m_out) \
198
+ for ( int __cfeim_init = ( \
199
+ ca_sweep_check_same_shape((CArray *)(_ca_in), \
200
+ (CArray *)(_ca_out), \
201
+ "CA_FOR_EACH_ELEMENT_INOUT_MASKED"), \
202
+ CA_SWEEP_WIRE_2_((_st2), (_ca_in), (_ca_out), "01", 0, \
203
+ "CA_FOR_EACH_ELEMENT_INOUT_MASKED"), \
204
+ ca_sweep_acquire_chunked(&(_st2).core), \
205
+ 1); \
206
+ __cfeim_init; \
207
+ __cfeim_init = 0, ca_sweep_release_chunked(&(_st2).core) ) \
208
+ for ( ; ca_sweep_next_chunk(&(_st2).core); ) \
209
+ for ( ca_size_t __cfeim_k = 0; \
210
+ __cfeim_k < (_st2).core.chunk_n \
211
+ && (((in) = *(T_IN *)((_st2).core.base[0] \
212
+ + __cfeim_k * (_st2).core.stride[0])), \
213
+ ((m_in) = (_st2).core.m0 \
214
+ ? (_st2).core.m0[__cfeim_k] \
215
+ : (boolean8_t)0), \
216
+ ((m_out) = (m_in)), \
217
+ 1); \
218
+ (*(T_OUT *)((_st2).core.base[1] \
219
+ + __cfeim_k * (_st2).core.stride[1]) = (out)), \
220
+ (((_st2).core.m0) \
221
+ ? ((_st2).core.m0[__cfeim_k] = (m_out)) \
222
+ : (boolean8_t)0), \
223
+ __cfeim_k++ )
224
+
225
+ /* (5) WRITE-only (init / iota / fill). */
226
+ #define CA_FOR_EACH_ELEMENT_OUT(_st1, _ca_out, T, out) \
227
+ for ( int __cfeo_init = (CA_SWEEP_WIRE_1_((_st1), (_ca_out), "1", 0, \
228
+ "CA_FOR_EACH_ELEMENT_OUT"), \
229
+ ca_sweep_acquire_chunked(&(_st1).core), 1); \
230
+ __cfeo_init; \
231
+ __cfeo_init = 0, ca_sweep_release_chunked(&(_st1).core) ) \
232
+ for ( ; ca_sweep_next_chunk(&(_st1).core); ) \
233
+ for ( ca_size_t __cfeo_k = 0; \
234
+ __cfeo_k < (_st1).core.chunk_n; \
235
+ (*(T *)((_st1).core.base[0] \
236
+ + __cfeo_k * (_st1).core.stride[0]) = (out)), \
237
+ __cfeo_k++ )
238
+
239
+ #endif /* CA_FOR_EACH_ELEMENT_H */
@@ -0,0 +1,304 @@
1
+ /* ---------------------------------------------------------------------------
2
+
3
+ Axis-group reduction surface: the `[]` type gate and CAGroupIterator.
4
+
5
+ The apply path is C-level: the `[]` type gate that routes a CACategorical /
6
+ AxisGroup index to the group path (vs ordinary selection), the
7
+ CAGroupIterator construction and the :group reduce dispatch all live here.
8
+ The classifier is CACategorical (`categorical` (lib/carray/categorical.rb)):
9
+ a READONLY Face over narrow-uint codes; this surface consumes its codes / k
10
+ / labels. The value-independent metadata (AxisGroup spec derivation,
11
+ GroupLabels) is Ruby (`axis_group` (lib/carray/axis_group.rb)) — it is not on
12
+ the `[]` hot path and is plain O(ndim) metadata.
13
+
14
+ Split summary:
15
+ - ca_argv_has_group / rb_ca_fetch_group : here (called by carray_access.c)
16
+ - CAGroupIterator + the tier-1 reduction methods driving
17
+ __axis_group_reduce__ : here
18
+ - reduce_plan / bundle assembly / output-axis permutation (metadata) : Ruby
19
+ - the O(N) compute kernel __axis_group_reduce__ : ca_axis_group.c
20
+
21
+ --------------------------------------------------------------------------- */
22
+
23
+ #include "carray.h"
24
+
25
+ /* lib/carray/axis_group.rb registers these classes here on first load (and
26
+ loading is wired so that load happens as soon as any categorical exists —
27
+ see autoload_base.rb). Until then they stay Qnil and the [] type-gate scan
28
+ short-circuits, so a normal index pays nothing (no kind_of on the hot
29
+ path). */
30
+ static VALUE rb_cCategorical = Qnil;
31
+ static VALUE rb_cAxisGroup = Qnil;
32
+ static VALUE rb_cCAGroupIterator = Qnil;
33
+
34
+ static ID id_value, id_spec, id_axis_group, id_reduce_plan, id_parse_axis;
35
+ static ID id_axis_group_reduce, id_reshape, id_transpose, id_labels;
36
+
37
+ /* CArray.__register_axis_group_classes__(CACategorical, AxisGroup) — called
38
+ from the top of lib/carray/axis_group.rb so the C type gate can recognise
39
+ the classifier/spec types without triggering autoload on every [] call. */
40
+ static VALUE
41
+ rb_ca_register_axis_group_classes (VALUE klass, VALUE cat, VALUE ag)
42
+ {
43
+ rb_cCategorical = cat;
44
+ rb_cAxisGroup = ag;
45
+ rb_gc_register_address(&rb_cCategorical);
46
+ rb_gc_register_address(&rb_cAxisGroup);
47
+ return Qnil;
48
+ }
49
+
50
+ /* Cheap per-arg pre-filter + kind_of check. Returns 1 if any arg is a
51
+ CACategorical (one-shot slot) or an AxisGroup (pre-built spec) — the group
52
+ apply path — and 0 otherwise.
53
+
54
+ The two group-arg forms have different Ruby representations, so each gets a
55
+ pre-filter that keeps ordinary indexing off the kind_of walk:
56
+
57
+ - AxisGroup is a plain Ruby object (T_OBJECT). The scalar index kinds
58
+ (Integer / nil / Symbol / Range) are not T_OBJECT, so they cost only the
59
+ RB_TYPE_P bit test.
60
+
61
+ - CACategorical is a CArray (T_DATA) whose SURFACE data_type is CA_FIXLEN
62
+ (its narrow-uint codes are the storage parent). A plain fancy-index is a
63
+ numeric CArray, so the cheap data_type read lets ordinary selection args
64
+ skip the kind_of — only a fixlen CArray index pays it.
65
+
66
+ Arming: the classes are registered by lib/carray/axis_group.rb. We cannot
67
+ pre-arm on categorical construction (CACategorical is a CArray, so routing
68
+ its autoload through the axis-group file recurses when categorical.rb reopens
69
+ the class), so the gate arms itself lazily the first time it sees a fixlen
70
+ CArray index — the only thing a categorical can be, and never a real plain
71
+ index. That covers a categorical built via CACategorical.from_codes that
72
+ never touched the axis-group file. Scalar / range / integer indices never
73
+ reach this branch, so they never trigger the require. */
74
+ int
75
+ ca_argv_has_group (int argc, VALUE *argv)
76
+ {
77
+ int i;
78
+ for ( i = 0; i < argc; i++ ) {
79
+ VALUE a = argv[i];
80
+ if ( RB_TYPE_P(a, T_OBJECT) ) {
81
+ if ( ! NIL_P(rb_cAxisGroup) && rb_obj_is_kind_of(a, rb_cAxisGroup) ) {
82
+ return 1;
83
+ }
84
+ }
85
+ else if ( RB_TYPE_P(a, T_DATA)
86
+ && rb_typeddata_is_kind_of(a, &carray_data_type) ) {
87
+ CArray *ca = (CArray *) DATA_PTR(a);
88
+ if ( ca->data_type == CA_FIXLEN ) {
89
+ if ( NIL_P(rb_cCategorical) ) {
90
+ rb_require("carray/axis_group"); /* lazily arm (registers) */
91
+ }
92
+ if ( ! NIL_P(rb_cCategorical) && rb_obj_is_kind_of(a, rb_cCategorical) ) {
93
+ return 1;
94
+ }
95
+ }
96
+ }
97
+ }
98
+ return 0;
99
+ }
100
+
101
+ /* Build a CAGroupIterator for `self[*argv]`. Two entry forms:
102
+ (A) one-shot self[idx, nil, ...] -> build the spec via axis_group(*argv)
103
+ (B) pre-built self[g] -> g is already an AxisGroup spec
104
+ Pre-condition: ca_argv_has_group(argc, argv) is true. */
105
+ VALUE
106
+ rb_ca_fetch_group (int argc, VALUE *argv, VALUE self)
107
+ {
108
+ volatile VALUE spec;
109
+
110
+ if ( argc == 1 && ! NIL_P(rb_cAxisGroup)
111
+ && rb_obj_is_kind_of(argv[0], rb_cAxisGroup) ) {
112
+ spec = argv[0];
113
+ }
114
+ else {
115
+ /* one-shot: raw CACategorical / nil slots -> build the spec (axis_group
116
+ validates the slot layout = one slot per source axis). */
117
+ spec = rb_funcallv(self, id_axis_group, argc, argv);
118
+ }
119
+ return rb_funcall(rb_cCAGroupIterator, rb_intern("__build__"), 2, self, spec);
120
+ }
121
+
122
+ /* --- CAGroupIterator -------------------------------------------------------
123
+
124
+ A reduction dispatcher, not an array: it holds the source value + the
125
+ AxisGroup spec and exposes the tier-1 reductions. There is no to_ca / copy
126
+ / materialise path — the grouping only exists as the reduction's argument. */
127
+
128
+ static VALUE
129
+ rb_ca_group_iter_build (VALUE klass, VALUE value, VALUE spec)
130
+ {
131
+ VALUE obj = rb_obj_alloc(klass);
132
+ rb_ivar_set(obj, id_value, value);
133
+ rb_ivar_set(obj, id_spec, spec);
134
+ return obj;
135
+ }
136
+
137
+ static VALUE
138
+ rb_ca_group_iter_value (VALUE self)
139
+ {
140
+ return rb_ivar_get(self, id_value);
141
+ }
142
+
143
+ static VALUE
144
+ rb_ca_group_iter_spec (VALUE self)
145
+ {
146
+ return rb_ivar_get(self, id_spec);
147
+ }
148
+
149
+ static VALUE
150
+ rb_ca_group_iter_labels (int argc, VALUE *argv, VALUE self)
151
+ {
152
+ VALUE spec = rb_ivar_get(self, id_spec);
153
+ return rb_funcallv_kw(spec, id_labels, argc, argv, RB_PASS_CALLED_KEYWORDS);
154
+ }
155
+
156
+ /* Reduce the grouped result by `perm`/`squeeze` from the plan:
157
+ result (= [K_total, *band]) -> reshape [*group_dims, *band]
158
+ -> transpose to slot order (perm)
159
+ -> squeeze the length-1 fused-band axes. */
160
+ static VALUE
161
+ group_iter_shape_output (VALUE result, VALUE group_dims, VALUE perm, VALUE squeeze)
162
+ {
163
+ CArray *cr;
164
+ VALUE dims[CA_RANK_MAX];
165
+ VALUE pargv[CA_RANK_MAX];
166
+ long ng, nband, nd, i;
167
+
168
+ GetCArray(result, cr);
169
+
170
+ /* reshape target = group_dims ++ result.shape[1..] (drop leading K_total) */
171
+ ng = RARRAY_LEN(group_dims);
172
+ nband = cr->ndim - 1;
173
+ nd = ng + nband;
174
+ if ( nd > CA_RANK_MAX ) {
175
+ rb_raise(rb_eRuntimeError, "axis_group: output ndim %ld too large", nd);
176
+ }
177
+ for ( i = 0; i < ng; i++ ) {
178
+ dims[i] = RARRAY_AREF(group_dims, i);
179
+ }
180
+ for ( i = 0; i < nband; i++ ) {
181
+ dims[ng + i] = SIZE2NUM(cr->dim[i + 1]);
182
+ }
183
+ result = rb_funcall2(result, id_reshape, (int) nd, dims);
184
+
185
+ /* transpose to slot order */
186
+ for ( i = 0; i < nd; i++ ) {
187
+ pargv[i] = RARRAY_AREF(perm, i);
188
+ }
189
+ result = rb_funcall2(result, id_transpose, (int) nd, pargv);
190
+
191
+ /* squeeze the fused (length-1) band slots */
192
+ if ( RARRAY_LEN(squeeze) > 0 ) {
193
+ char drop[CA_RANK_MAX];
194
+ VALUE keep[CA_RANK_MAX];
195
+ long nkeep = 0, j;
196
+ GetCArray(result, cr);
197
+ for ( i = 0; i < cr->ndim; i++ ) drop[i] = 0;
198
+ for ( j = 0; j < RARRAY_LEN(squeeze); j++ ) {
199
+ long s = NUM2LONG(RARRAY_AREF(squeeze, j));
200
+ if ( s >= 0 && s < cr->ndim ) drop[s] = 1;
201
+ }
202
+ for ( i = 0; i < cr->ndim; i++ ) {
203
+ if ( ! drop[i] ) keep[nkeep++] = SIZE2NUM(cr->dim[i]);
204
+ }
205
+ result = rb_funcall2(result, id_reshape, (int) nkeep, keep);
206
+ }
207
+ return result;
208
+ }
209
+
210
+ /* Shared driver for every tier-1 reduction. The op is read from the called
211
+ method name (rb_frame_this_func), so the 10 white-list names all bind here.
212
+ axis: absent / no :group -> delegate to the value's plain same-named
213
+ reduction (grouping NOT engaged). axis: contains :group -> group reduction
214
+ (+ any integer band axes folded into the statistic). */
215
+ static VALUE
216
+ group_iter_reduce (int argc, VALUE *argv, VALUE self)
217
+ {
218
+ ID op_id = rb_frame_this_func();
219
+ VALUE op_sym = ID2SYM(op_id);
220
+ VALUE value = rb_ivar_get(self, id_value);
221
+ VALUE spec = rb_ivar_get(self, id_spec);
222
+ VALUE kw = Qnil, vaxis;
223
+ VALUE parsed, plan;
224
+ VALUE group_axes, bundles, group_dims, perm, squeeze, fused;
225
+ VALUE result;
226
+
227
+ rb_scan_args(argc, argv, "0:", &kw);
228
+ vaxis = NIL_P(kw)
229
+ ? Qundef
230
+ : rb_hash_lookup2(kw, ID2SYM(rb_intern("axis")), Qundef);
231
+
232
+ /* (has_group, fused_band_slots) */
233
+ parsed = rb_funcall(rb_cAxisGroup, id_parse_axis,
234
+ 1, (vaxis == Qundef) ? Qnil : vaxis);
235
+ if ( RTEST(RARRAY_AREF(parsed, 0)) == 0 ) {
236
+ /* no :group -> plain reduction on the value (grouping not engaged) */
237
+ if ( vaxis == Qundef ) {
238
+ return rb_funcall(value, op_id, 0);
239
+ }
240
+ else {
241
+ VALUE h = rb_hash_new();
242
+ rb_hash_aset(h, ID2SYM(rb_intern("axis")), vaxis);
243
+ return rb_funcallv_kw(value, op_id, 1, &h, RB_PASS_KEYWORDS);
244
+ }
245
+ }
246
+
247
+ /* group reduction */
248
+ fused = RARRAY_AREF(parsed, 1);
249
+ plan = rb_funcall(spec, id_reduce_plan, 1, fused);
250
+ group_axes = RARRAY_AREF(plan, 0);
251
+ bundles = RARRAY_AREF(plan, 1);
252
+ group_dims = RARRAY_AREF(plan, 2);
253
+ perm = RARRAY_AREF(plan, 3);
254
+ squeeze = RARRAY_AREF(plan, 4);
255
+
256
+ result = rb_funcall(value, id_axis_group_reduce, 3,
257
+ group_axes, bundles, op_sym);
258
+ return group_iter_shape_output(result, group_dims, perm, squeeze);
259
+ }
260
+
261
+ void
262
+ Init_ca_group_iter (void)
263
+ {
264
+ id_value = rb_intern("@value");
265
+ id_spec = rb_intern("@spec");
266
+ id_axis_group = rb_intern("axis_group");
267
+ id_reduce_plan = rb_intern("reduce_plan");
268
+ id_parse_axis = rb_intern("parse_axis");
269
+ id_axis_group_reduce = rb_intern("__axis_group_reduce__");
270
+ id_reshape = rb_intern("reshape");
271
+ id_transpose = rb_intern("transpose");
272
+ id_labels = rb_intern("labels");
273
+
274
+ rb_define_singleton_method(rb_cCArray, "__register_axis_group_classes__",
275
+ rb_ca_register_axis_group_classes, 2);
276
+
277
+ rb_cCAGroupIterator =
278
+ rb_define_class("CAGroupIterator",
279
+ rb_const_get(rb_cObject, rb_intern("CAIterator")));
280
+ rb_gc_register_address(&rb_cCAGroupIterator);
281
+
282
+ rb_define_singleton_method(rb_cCAGroupIterator, "__build__",
283
+ rb_ca_group_iter_build, 2);
284
+ rb_define_method(rb_cCAGroupIterator, "value", rb_ca_group_iter_value, 0);
285
+ rb_define_method(rb_cCAGroupIterator, "spec", rb_ca_group_iter_spec, 0);
286
+ rb_define_method(rb_cCAGroupIterator, "labels", rb_ca_group_iter_labels, -1);
287
+
288
+ /* reduction white-list. All bind to one driver; the op is recovered from
289
+ the called method name. count_not_masked is a synonym of count (present
290
+ count); elements / count_masked / minmax / wsum / wmean are composed in
291
+ Ruby (lib/carray/axis_group.rb). min_addr / max_addr give the flat source
292
+ address of the extremum (there is no group-local min_index for the group
293
+ iterator -- the order-preserving flat address is the meaningful position). */
294
+ {
295
+ const char *ops[] = { "sum", "accumulate", "prod", "mean", "min", "max",
296
+ "variance", "stddev", "variancep", "stddevp",
297
+ "count", "count_not_masked", "min_addr", "max_addr",
298
+ "all", "any", NULL };
299
+ int i;
300
+ for ( i = 0; ops[i]; i++ ) {
301
+ rb_define_method(rb_cCAGroupIterator, ops[i], group_iter_reduce, -1);
302
+ }
303
+ }
304
+ }