carray 2.0.0 → 3.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (339) hide show
  1. checksums.yaml +4 -4
  2. data/.yardopts +5 -25
  3. data/CHANGELOG.md +16 -0
  4. data/LICENSE +1 -1
  5. data/NEWS.md +3 -0
  6. data/README.md +128 -44
  7. data/carray.gemspec +22 -24
  8. data/ext/ca_array_pool.c +91 -0
  9. data/ext/ca_axis_descriptor.h +186 -0
  10. data/ext/ca_axis_dispatch.c +924 -0
  11. data/ext/ca_axis_group.c +1208 -0
  12. data/ext/ca_bincmp_dispatch.c +76 -0
  13. data/ext/ca_bincmp_dispatch.h +85 -0
  14. data/ext/ca_binop_dispatch.c +125 -0
  15. data/ext/ca_binop_dispatch.h +159 -0
  16. data/ext/ca_categorical_iterator.c +1375 -0
  17. data/ext/ca_compare.c +94 -0
  18. data/ext/ca_compare.h +26 -0
  19. data/ext/ca_composite_dispatch.c +414 -0
  20. data/ext/ca_composite_dispatch.h +116 -0
  21. data/ext/ca_for_buffer.h +96 -0
  22. data/ext/ca_for_each_element.h +241 -0
  23. data/ext/ca_group_iter.c +304 -0
  24. data/ext/ca_iter_substrate.h +325 -0
  25. data/ext/ca_kernel_iterator.c +4321 -0
  26. data/ext/ca_kernel_iterator.h +2603 -0
  27. data/ext/ca_moncmp_dispatch.c +37 -0
  28. data/ext/ca_moncmp_dispatch.h +62 -0
  29. data/ext/ca_monop_dispatch.c +200 -0
  30. data/ext/ca_monop_dispatch.h +235 -0
  31. data/ext/ca_obj_array.c +355 -359
  32. data/ext/ca_obj_bincmp.c +809 -0
  33. data/ext/ca_obj_binop.c +892 -0
  34. data/ext/ca_obj_bitarray.c +369 -164
  35. data/ext/ca_obj_bitfield.c +294 -234
  36. data/ext/ca_obj_block.c +189 -711
  37. data/ext/ca_obj_byte_swap.c +766 -0
  38. data/ext/ca_obj_const_string.c +965 -0
  39. data/ext/ca_obj_face.c +670 -0
  40. data/ext/ca_obj_face.h +247 -0
  41. data/ext/ca_obj_fake.c +228 -100
  42. data/ext/ca_obj_farray.c +54 -441
  43. data/ext/ca_obj_field.c +82 -529
  44. data/ext/ca_obj_fixlen_string.c +306 -0
  45. data/ext/ca_obj_grid.c +858 -440
  46. data/ext/ca_obj_meld.c +1034 -0
  47. data/ext/ca_obj_moncmp.c +569 -0
  48. data/ext/ca_obj_monop.c +1111 -0
  49. data/ext/ca_obj_object.c +774 -298
  50. data/ext/ca_obj_record.c +468 -0
  51. data/ext/ca_obj_reduce.c +97 -82
  52. data/ext/ca_obj_refer.c +569 -459
  53. data/ext/ca_obj_remap.c +475 -0
  54. data/ext/ca_obj_repeat.c +92 -477
  55. data/ext/ca_obj_roll.c +616 -0
  56. data/ext/ca_obj_select.c +344 -296
  57. data/ext/ca_obj_select_axis.c +1296 -0
  58. data/ext/ca_obj_shift.c +230 -792
  59. data/ext/ca_obj_source.c +78 -0
  60. data/ext/ca_obj_stack.c +1173 -0
  61. data/ext/ca_obj_stride.c +2501 -0
  62. data/ext/ca_obj_string.c +268 -0
  63. data/ext/ca_obj_tile.c +614 -0
  64. data/ext/ca_obj_time.c +546 -0
  65. data/ext/ca_obj_timedelta.c +435 -0
  66. data/ext/ca_obj_transpose.c +62 -516
  67. data/ext/ca_obj_triop.c +746 -0
  68. data/ext/ca_obj_unbound_repeat.c +208 -241
  69. data/ext/ca_obj_window.c +1131 -563
  70. data/ext/ca_op_byte_swap.c +175 -0
  71. data/ext/ca_op_ipower.c +319 -0
  72. data/ext/ca_op_powi.h +88 -0
  73. data/ext/ca_sort_kernels.h +132 -0
  74. data/ext/ca_sweep_engine.c +430 -0
  75. data/ext/ca_sweep_engine.h +157 -0
  76. data/ext/ca_transform_common.c +228 -0
  77. data/ext/ca_triop_dispatch.c +55 -0
  78. data/ext/ca_triop_dispatch.h +62 -0
  79. data/ext/carray.h +795 -402
  80. data/ext/carray_access.c +831 -711
  81. data/ext/carray_attribute.c +98 -330
  82. data/ext/carray_bincount.c +255 -0
  83. data/ext/carray_broadcast.c +283 -0
  84. data/ext/carray_call_cfunc.c +1360 -828
  85. data/ext/carray_call_cfunc.h +160 -0
  86. data/ext/carray_cast.c +1212 -301
  87. data/ext/carray_cast_func.rb +81 -40
  88. data/ext/carray_class.c +53 -63
  89. data/ext/carray_config.h +28 -0
  90. data/ext/carray_conversion.c +350 -346
  91. data/ext/carray_copy.c +156 -268
  92. data/ext/carray_core.c +1342 -199
  93. data/ext/carray_count.c +312 -0
  94. data/ext/carray_data_type.c +43 -19
  95. data/ext/carray_element.c +585 -213
  96. data/ext/carray_factorize.c +2542 -0
  97. data/ext/carray_generate.c +230 -559
  98. data/ext/carray_histogram.c +490 -0
  99. data/ext/carray_hold.c +228 -0
  100. data/ext/carray_index_classifier.c +1035 -0
  101. data/ext/carray_index_classifier.h +27 -0
  102. data/ext/carray_internal.h +120 -0
  103. data/ext/carray_kernels_bincmp.c +4445 -0
  104. data/ext/carray_kernels_binop.c +10979 -0
  105. data/ext/carray_kernels_init.c +36 -0
  106. data/ext/carray_kernels_map.c +3466 -0
  107. data/ext/carray_kernels_moncmp.c +2096 -0
  108. data/ext/carray_kernels_monop.c +18312 -0
  109. data/ext/carray_kernels_reduce_aggregate.c +25836 -0
  110. data/ext/carray_kernels_reduce_boolean.c +329 -0
  111. data/ext/carray_kernels_reduce_cumulative.c +14592 -0
  112. data/ext/carray_kernels_reduce_extreme.c +16947 -0
  113. data/ext/carray_kernels_reduce_variance.c +3909 -0
  114. data/ext/carray_kernels_scan.c +3692 -0
  115. data/ext/carray_kernels_search.c +32137 -0
  116. data/ext/carray_kernels_sort.c +10625 -0
  117. data/ext/carray_kernels_triop.c +1391 -0
  118. data/ext/carray_lazy.c +567 -0
  119. data/ext/carray_loop.c +88 -200
  120. data/ext/carray_mask.c +848 -154
  121. data/ext/carray_math_kernel.h +120 -0
  122. data/ext/carray_mathfunc.c +10 -241
  123. data/ext/carray_median_percentile.c +1257 -0
  124. data/ext/carray_memory_view.c +1625 -0
  125. data/ext/carray_operator.c +1526 -318
  126. data/ext/carray_order.c +664 -1394
  127. data/ext/carray_partition.c +416 -0
  128. data/ext/carray_random.c +518 -0
  129. data/ext/carray_scatter.c +357 -0
  130. data/ext/carray_slab.c +1219 -0
  131. data/ext/carray_slab.h +84 -0
  132. data/ext/carray_sort.c +829 -0
  133. data/ext/carray_sort_kernel.c +620 -0
  134. data/ext/carray_struct.c +695 -0
  135. data/ext/carray_test.c +343 -229
  136. data/ext/carray_undef.c +34 -17
  137. data/ext/carray_utils.c +175 -74
  138. data/ext/extconf.rb +216 -55
  139. data/ext/mk_call_cfunc.rb +480 -0
  140. data/ext/mkkernel.rb +8842 -0
  141. data/ext/ruby_carray.c +202 -101
  142. data/ext/version.h +4 -14
  143. data/ext/version.rb +5 -13
  144. data/lib/carray/arrow_tensor.rb +401 -0
  145. data/lib/carray/attribute.rb +166 -0
  146. data/lib/carray/autoload_carray.rb +220 -0
  147. data/lib/carray/autoload_method_extension.rb +44 -0
  148. data/lib/carray/axis_group.rb +711 -0
  149. data/lib/carray/basics.rb +481 -0
  150. data/lib/carray/bincount_nd.rb +358 -0
  151. data/lib/carray/block_iterator.rb +604 -0
  152. data/lib/carray/boolean_reduce.rb +109 -0
  153. data/lib/carray/categorical.rb +561 -0
  154. data/lib/carray/categorical_iterator.rb +1062 -0
  155. data/lib/carray/complex.rb +150 -0
  156. data/lib/carray/conditional.rb +216 -0
  157. data/lib/carray/const_string.rb +228 -0
  158. data/lib/carray/construct.rb +139 -328
  159. data/lib/carray/core_extensions.rb +240 -0
  160. data/lib/carray/data_type_extension.rb +233 -0
  161. data/lib/carray/fixlen_string.rb +95 -0
  162. data/lib/carray/frame/concat.rb +132 -0
  163. data/lib/carray/frame/convert.rb +95 -0
  164. data/lib/carray/frame/csv_parser.rb +211 -0
  165. data/lib/carray/frame/frame.rb +649 -0
  166. data/lib/carray/frame/group.rb +186 -0
  167. data/lib/carray/frame/io.rb +164 -0
  168. data/lib/carray/frame/join.rb +248 -0
  169. data/lib/carray/frame/records.rb +99 -0
  170. data/lib/carray/frame/sort.rb +113 -0
  171. data/lib/carray/frame/verbs.rb +299 -0
  172. data/lib/carray/frame.rb +16 -0
  173. data/lib/carray/histogram.rb +512 -0
  174. data/lib/carray/inspect.rb +37 -20
  175. data/lib/carray/iterator.rb +57 -349
  176. data/lib/carray/lazy.rb +889 -0
  177. data/lib/carray/mask_gap_fill.rb +200 -0
  178. data/lib/carray/math.rb +78 -342
  179. data/lib/carray/meld_reduce.rb +289 -0
  180. data/lib/carray/methods/align_addr.rb +116 -0
  181. data/lib/carray/methods/bin.rb +128 -0
  182. data/lib/carray/methods/bincount.rb +87 -0
  183. data/lib/carray/methods/bit_string.rb +92 -0
  184. data/lib/carray/methods/broadcast.rb +63 -0
  185. data/lib/carray/methods/choose.rb +39 -0
  186. data/lib/carray/methods/composition.rb +280 -0
  187. data/lib/carray/methods/gather_nd.rb +206 -0
  188. data/lib/carray/methods/index.rb +39 -0
  189. data/lib/carray/methods/insert_block.rb +99 -0
  190. data/lib/carray/methods/is_in.rb +141 -0
  191. data/lib/carray/methods/join.rb +90 -0
  192. data/lib/carray/methods/locate_addr.rb +47 -0
  193. data/lib/carray/methods/mask_duplicates.rb +41 -0
  194. data/lib/carray/methods/meshgrid.rb +91 -0
  195. data/lib/carray/methods/mode.rb +126 -0
  196. data/lib/carray/methods/nunique.rb +46 -0
  197. data/lib/carray/methods/resize.rb +56 -0
  198. data/lib/carray/methods/snap.rb +156 -0
  199. data/lib/carray/methods/string_format.rb +57 -0
  200. data/lib/carray/methods/unique.rb +47 -0
  201. data/lib/carray/methods/value_counts.rb +71 -0
  202. data/lib/carray/mkmf.rb +124 -101
  203. data/lib/carray/runtime.rb +108 -0
  204. data/lib/carray/serialize.rb +478 -167
  205. data/lib/carray/slab_iterator.rb +292 -0
  206. data/lib/carray/stack.rb +291 -0
  207. data/lib/carray/string.rb +56 -180
  208. data/lib/carray/string_operation_extension.rb +289 -0
  209. data/lib/carray/struct.rb +335 -323
  210. data/lib/carray/struct_builder.rb +697 -0
  211. data/lib/carray/table.rb +41 -2
  212. data/lib/carray/time.rb +2255 -38
  213. data/lib/carray/window_iterator.rb +655 -0
  214. data/lib/carray.rb +55 -57
  215. metadata +163 -130
  216. data/Rakefile +0 -51
  217. data/TODO.md +0 -18
  218. data/ext/ca_iter_block.c +0 -257
  219. data/ext/ca_iter_dimension.c +0 -299
  220. data/ext/ca_iter_window.c +0 -214
  221. data/ext/ca_obj_mapping.c +0 -644
  222. data/ext/carray_iterator.c +0 -641
  223. data/ext/carray_math.rb +0 -850
  224. data/ext/carray_numeric.c +0 -259
  225. data/ext/carray_sort_addr.c +0 -254
  226. data/ext/carray_stat.c +0 -2100
  227. data/ext/carray_stat_proc.rb +0 -1999
  228. data/ext/mkmath.rb +0 -741
  229. data/ext/ruby_ccomplex.c +0 -509
  230. data/ext/ruby_float_func.c +0 -86
  231. data/lib/carray/array.rb +0 -8
  232. data/lib/carray/autoload/autoload_base.rb +0 -19
  233. data/lib/carray/autoload/autoload_gem_cairo.rb +0 -9
  234. data/lib/carray/autoload/autoload_gem_ffi.rb +0 -9
  235. data/lib/carray/autoload/autoload_gem_gnuplot.rb +0 -2
  236. data/lib/carray/autoload/autoload_gem_io_csv.rb +0 -14
  237. data/lib/carray/autoload/autoload_gem_io_pg.rb +0 -6
  238. data/lib/carray/autoload/autoload_gem_io_sqlite3.rb +0 -12
  239. data/lib/carray/autoload/autoload_gem_narray.rb +0 -10
  240. data/lib/carray/autoload/autoload_gem_numo_narray.rb +0 -15
  241. data/lib/carray/autoload/autoload_gem_opencv.rb +0 -16
  242. data/lib/carray/autoload/autoload_gem_random.rb +0 -8
  243. data/lib/carray/autoload/autoload_gem_rmagick.rb +0 -23
  244. data/lib/carray/autoload/autoload_gem_zimg.rb +0 -3
  245. data/lib/carray/autoload/autoload_io_imagemagick.rb +0 -6
  246. data/lib/carray/autoload/autoload_math_histogram.rb +0 -5
  247. data/lib/carray/autoload/autoload_math_recurrence.rb +0 -6
  248. data/lib/carray/autoload/autoload_object_iterator.rb +0 -1
  249. data/lib/carray/autoload/autoload_object_link.rb +0 -1
  250. data/lib/carray/autoload/autoload_object_pack.rb +0 -2
  251. data/lib/carray/autoload.rb +0 -141
  252. data/lib/carray/basic.rb +0 -191
  253. data/lib/carray/broadcast.rb +0 -101
  254. data/lib/carray/compose.rb +0 -315
  255. data/lib/carray/convert.rb +0 -115
  256. data/lib/carray/info.rb +0 -110
  257. data/lib/carray/io/imagemagick.rb +0 -235
  258. data/lib/carray/mask.rb +0 -102
  259. data/lib/carray/math/histogram.rb +0 -177
  260. data/lib/carray/math/recurrence.rb +0 -93
  261. data/lib/carray/object/ca_obj_iterator.rb +0 -50
  262. data/lib/carray/object/ca_obj_link.rb +0 -50
  263. data/lib/carray/object/ca_obj_pack.rb +0 -99
  264. data/lib/carray/obsolete.rb +0 -256
  265. data/lib/carray/ordering.rb +0 -181
  266. data/lib/carray/testing.rb +0 -51
  267. data/lib/carray/transform.rb +0 -109
  268. data/misc/Methods.ja.md +0 -182
  269. data/misc/NOTE +0 -51
  270. data/spec/Classes/CABitfield_spec.rb +0 -58
  271. data/spec/Classes/CABlockIterator_spec.rb +0 -114
  272. data/spec/Classes/CABlock_spec.rb +0 -205
  273. data/spec/Classes/CAField_spec.rb +0 -39
  274. data/spec/Classes/CAGrid_spec.rb +0 -75
  275. data/spec/Classes/CAMap_spec.rb +0 -0
  276. data/spec/Classes/CAMapping_spec.rb +0 -105
  277. data/spec/Classes/CAObject_attribute_spec.rb +0 -33
  278. data/spec/Classes/CAObject_spec.rb +0 -33
  279. data/spec/Classes/CARefer_spec.rb +0 -93
  280. data/spec/Classes/CARepeat_spec.rb +0 -65
  281. data/spec/Classes/CASelect_spec.rb +0 -22
  282. data/spec/Classes/CAShift_spec.rb +0 -16
  283. data/spec/Classes/CAStruct_spec.rb +0 -71
  284. data/spec/Classes/CATranspose_spec.rb +0 -60
  285. data/spec/Classes/CAUnboudRepeat_spec.rb +0 -102
  286. data/spec/Classes/CAWindow_spec.rb +0 -54
  287. data/spec/Classes/CAWrap_spec.rb +0 -8
  288. data/spec/Classes/CArray_spec.rb +0 -184
  289. data/spec/Classes/CScalar_spec.rb +0 -55
  290. data/spec/Classes/ex1.rb +0 -46
  291. data/spec/Features/feature_130_spec.rb +0 -19
  292. data/spec/Features/feature_attributes_spec.rb +0 -280
  293. data/spec/Features/feature_boolean_spec.rb +0 -98
  294. data/spec/Features/feature_broadcast.rb +0 -116
  295. data/spec/Features/feature_cast_function.rb +0 -19
  296. data/spec/Features/feature_cast_spec.rb +0 -33
  297. data/spec/Features/feature_class_spec.rb +0 -84
  298. data/spec/Features/feature_complex_spec.rb +0 -42
  299. data/spec/Features/feature_composite_spec.rb +0 -124
  300. data/spec/Features/feature_convert_spec.rb +0 -46
  301. data/spec/Features/feature_copy_spec.rb +0 -123
  302. data/spec/Features/feature_creation_spec.rb +0 -84
  303. data/spec/Features/feature_element_spec.rb +0 -144
  304. data/spec/Features/feature_extream_spec.rb +0 -54
  305. data/spec/Features/feature_generate_spec.rb +0 -74
  306. data/spec/Features/feature_index_spec.rb +0 -69
  307. data/spec/Features/feature_mask_spec.rb +0 -580
  308. data/spec/Features/feature_math_spec.rb +0 -97
  309. data/spec/Features/feature_order_spec.rb +0 -146
  310. data/spec/Features/feature_ref_store_spec.rb +0 -209
  311. data/spec/Features/feature_serialization_spec.rb +0 -125
  312. data/spec/Features/feature_stat_spec.rb +0 -397
  313. data/spec/Features/feature_virtual_spec.rb +0 -48
  314. data/spec/Features/method_eq_spec.rb +0 -81
  315. data/spec/Features/method_is_nan_spec.rb +0 -12
  316. data/spec/Features/method_map_spec.rb +0 -54
  317. data/spec/Features/method_max_with.rb +0 -20
  318. data/spec/Features/method_min_with.rb +0 -19
  319. data/spec/Features/method_ne_spec.rb +0 -18
  320. data/spec/Features/method_project_spec.rb +0 -188
  321. data/spec/Features/method_ref_spec.rb +0 -27
  322. data/spec/Features/method_round_spec.rb +0 -11
  323. data/spec/Features/method_s_linspace_spec.rb +0 -48
  324. data/spec/Features/method_s_span_spec.rb +0 -14
  325. data/spec/Features/method_seq_spec.rb +0 -47
  326. data/spec/Features/method_sort_with.rb +0 -43
  327. data/spec/Features/method_sorted_with.rb +0 -29
  328. data/spec/Features/method_span_spec.rb +0 -42
  329. data/spec/Features/method_wrap_readonly_spec.rb +0 -43
  330. data/spec/UnitTest/test_CAVirtual.rb +0 -214
  331. data/spec/spec_all.rb +0 -10
  332. data/utils/ca_ase.rb +0 -21
  333. data/utils/ca_methods.rb +0 -15
  334. data/utils/cast_checker.rb +0 -30
  335. data/utils/convert_test.rb +0 -73
  336. data/utils/extract_yard.rb +0 -22
  337. data/utils/guess_shape.rb +0 -76
  338. data/utils/monkey_patch_methods.rb +0 -62
  339. data/utils/remove_resource_fork.sh +0 -5
@@ -0,0 +1,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,241 @@
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 full size (=
129
+ * n_kernel bytes) for simplicity; macro reads at m0[chunk_off + k].
130
+ *
131
+ * For MASKED forms (m / m_in / m_out): when source has no mask, m0 is
132
+ * NULL and m / m_in == 0 always. m_out writes during INOUT_MASKED are
133
+ * silently dropped if no INPUT mask exists (= AC4 mask-creation on
134
+ * INOUT MASKED with no source mask is a future enhancement; current
135
+ * convention follows the FIBER family's MASKED form).
136
+ */
137
+
138
+ /* (1) READ-only, NO_MASK */
139
+ #define CA_FOR_EACH_ELEMENT(_st1, _ca, T, x) \
140
+ for ( int __cfe_init = (CA_SWEEP_WIRE_1_((_st1), (_ca), "0", 1, \
141
+ "CA_FOR_EACH_ELEMENT"), \
142
+ ca_sweep_acquire_chunked(&(_st1).core), 1); \
143
+ __cfe_init; \
144
+ __cfe_init = 0, ca_sweep_release_chunked(&(_st1).core) ) \
145
+ for ( ; ca_sweep_next_chunk(&(_st1).core); ) \
146
+ for ( ca_size_t __cfe_k = 0; \
147
+ __cfe_k < (_st1).core.chunk_n \
148
+ && (((x) = *(T *)((_st1).core.base[0] \
149
+ + __cfe_k * (_st1).core.stride[0])), 1); \
150
+ __cfe_k++ )
151
+
152
+ /* (2) READ-only, MASKED (m is 1 if masked, 0 otherwise; 0 if no source mask) */
153
+ #define CA_FOR_EACH_ELEMENT_MASKED(_st1, _ca, T, x, m) \
154
+ for ( int __cfem_init = (CA_SWEEP_WIRE_1_((_st1), (_ca), "0", 0, \
155
+ "CA_FOR_EACH_ELEMENT_MASKED"), \
156
+ ca_sweep_acquire_chunked(&(_st1).core), 1); \
157
+ __cfem_init; \
158
+ __cfem_init = 0, ca_sweep_release_chunked(&(_st1).core) ) \
159
+ for ( ; ca_sweep_next_chunk(&(_st1).core); ) \
160
+ for ( ca_size_t __cfem_k = 0; \
161
+ __cfem_k < (_st1).core.chunk_n \
162
+ && (((x) = *(T *)((_st1).core.base[0] \
163
+ + __cfem_k * (_st1).core.stride[0])), \
164
+ ((m) = (_st1).core.m0 \
165
+ ? (_st1).core.m0[(_st1).core.chunk_off \
166
+ + __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[(_st2).core.chunk_off \
215
+ + __cfeim_k] \
216
+ : (boolean8_t)0), \
217
+ ((m_out) = (m_in)), \
218
+ 1); \
219
+ (*(T_OUT *)((_st2).core.base[1] \
220
+ + __cfeim_k * (_st2).core.stride[1]) = (out)), \
221
+ (((_st2).core.m0) \
222
+ ? ((_st2).core.m0[(_st2).core.chunk_off + __cfeim_k] \
223
+ = (m_out)) \
224
+ : (boolean8_t)0), \
225
+ __cfeim_k++ )
226
+
227
+ /* (5) WRITE-only (init / iota / fill). */
228
+ #define CA_FOR_EACH_ELEMENT_OUT(_st1, _ca_out, T, out) \
229
+ for ( int __cfeo_init = (CA_SWEEP_WIRE_1_((_st1), (_ca_out), "1", 0, \
230
+ "CA_FOR_EACH_ELEMENT_OUT"), \
231
+ ca_sweep_acquire_chunked(&(_st1).core), 1); \
232
+ __cfeo_init; \
233
+ __cfeo_init = 0, ca_sweep_release_chunked(&(_st1).core) ) \
234
+ for ( ; ca_sweep_next_chunk(&(_st1).core); ) \
235
+ for ( ca_size_t __cfeo_k = 0; \
236
+ __cfeo_k < (_st1).core.chunk_n; \
237
+ (*(T *)((_st1).core.base[0] \
238
+ + __cfeo_k * (_st1).core.stride[0]) = (out)), \
239
+ __cfeo_k++ )
240
+
241
+ #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", "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
+ }