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,1035 @@
1
+ /* ---------------------------------------------------------------------------
2
+
3
+ Index classifier for `[]` / `[]=` — turns the argv passed to
4
+ CArray subscription into a CAIndexInfo describing the region
5
+ (POINT / BLOCK / ITERATOR / ADDRESS / GRID / SELECT / …). The
6
+ public entry `rb_ca_scan_index` in ca_obj_access.c forwards to
7
+ `rb_ca_scan_index_v2` here.
8
+
9
+ See docs/topics/Indexer_decision_tree.md for the wire-format spec.
10
+
11
+ Design shape:
12
+ - one REG per handler function (grep-friendly)
13
+ - rubber-dim pre-pass with a stack-allocated argv_expanded slot
14
+ - a single negative-index normaliser used by all handlers
15
+ - fast paths (POINT / ADDRESS, ndim == argc, all-FIXNUM) at
16
+ the top of the entry point
17
+ - single-character alphabetic Symbols (:a-:z / :A-:Z) reserved
18
+ for future contraction notation; the classifier raises
19
+ NotImplementedError when they appear.
20
+
21
+ --------------------------------------------------------------------------- */
22
+
23
+ #include "carray.h"
24
+ #include "carray_index_classifier.h"
25
+
26
+ /* Range accessor macros — carray_access.c keeps a private cache
27
+ with the same shape; the duplication here keeps this file
28
+ self-contained without cross-file symbol exposure. */
29
+ static ID ca_classifier_id_begin = 0;
30
+ static ID ca_classifier_id_end = 0;
31
+ static ID ca_classifier_id_excl_end = 0;
32
+ #define CA_CLASSIFIER_RANGE_BEG(r) (rb_funcall((r), ca_classifier_id_begin, 0))
33
+ #define CA_CLASSIFIER_RANGE_END(r) (rb_funcall((r), ca_classifier_id_end, 0))
34
+ #define CA_CLASSIFIER_RANGE_EXCL(r) (rb_funcall((r), ca_classifier_id_excl_end, 0))
35
+
36
+ /* -------------------------------------------------------------------- */
37
+ /* Symbol cache — see the range-macro comment above for why this
38
+ file keeps its own copy independent of carray_access.c. */
39
+
40
+ static VALUE ca_classifier_sym_star = Qundef;
41
+ static VALUE ca_classifier_sym_perc = Qundef;
42
+ static VALUE ca_classifier_sym_under = Qundef;
43
+ static VALUE ca_classifier_sym_gt = Qundef; /* :> slab-iterator marker */
44
+ static VALUE ca_classifier_sym_tilde = Qundef; /* :~ rubber-dim alias for `false` */
45
+
46
+ static inline void
47
+ ca_classifier_sym_cache_init (void)
48
+ {
49
+ if ( ca_classifier_sym_star == Qundef ) {
50
+ ca_classifier_sym_star = ID2SYM(rb_intern("*"));
51
+ ca_classifier_sym_perc = ID2SYM(rb_intern("%"));
52
+ ca_classifier_sym_under = ID2SYM(rb_intern("_"));
53
+ ca_classifier_sym_gt = ID2SYM(rb_intern(">"));
54
+ ca_classifier_sym_tilde = ID2SYM(rb_intern("~"));
55
+ ca_classifier_id_begin = rb_intern("begin");
56
+ ca_classifier_id_end = rb_intern("end");
57
+ ca_classifier_id_excl_end = rb_intern("exclude_end?");
58
+ }
59
+ }
60
+
61
+ /* -------------------------------------------------------------------- */
62
+ /* Classifier context — every handler reads / writes through this
63
+ struct. Lives on rb_ca_scan_index_v2's stack; not exposed. */
64
+
65
+ typedef struct {
66
+ /* inputs (read-only after init) */
67
+ int ca_ndim;
68
+ ca_size_t *ca_dim;
69
+ ca_size_t ca_elements;
70
+ long argc; /* may be adjusted by rubber-dim expansion */
71
+ VALUE *argv; /* points at argv_expanded after pre-pass */
72
+
73
+ /* output (target of handlers) */
74
+ CAIndexInfo *info;
75
+
76
+ /* working storage for rubber-dim expansion */
77
+ VALUE argv_expanded[CA_RANK_MAX];
78
+ } ca_classifier_ctx_t;
79
+
80
+ /* -------------------------------------------------------------------- */
81
+ /* helpers */
82
+
83
+ /* Negative-index normalisation gathered in one place, replacing
84
+ the CA_CHECK_INDEX_AT macro that used to be expanded at every
85
+ call site. Error-message format is byte-identical with the
86
+ legacy path so downstream error-message assertions still match. */
87
+ static inline ca_size_t
88
+ ca_classifier_normalize_axis_index (ca_size_t k, ca_size_t dim, int axis, int range_check)
89
+ {
90
+ if ( k < 0 ) {
91
+ k += dim;
92
+ }
93
+ if ( range_check && ( k < 0 || k >= dim ) ) {
94
+ rb_raise(rb_eIndexError,
95
+ "index out of range at %i-dim ( %" PRId64 " <=> 0..%" PRId64 " )",
96
+ axis, (ca_size_t) k, (ca_size_t) (dim - 1));
97
+ }
98
+ return k;
99
+ }
100
+
101
+ /* Flat-address normalisation for the `argc == 1, ndim > 1,
102
+ Integer` path. CA_CHECK_INDEX does not carry axis info in its
103
+ message, which is the intended behaviour on the flat path. */
104
+ static inline ca_size_t
105
+ ca_classifier_normalize_flat_address (ca_size_t addr, ca_size_t elements, int range_check)
106
+ {
107
+ if ( range_check ) {
108
+ CA_CHECK_INDEX(addr, elements);
109
+ }
110
+ return addr;
111
+ }
112
+
113
+ /* -------------------------------------------------------------------- */
114
+ /* Fast paths. */
115
+
116
+ /* Fast path 1: ca[i, j, k] all-FIXNUM, argc == ndim → POINT.
117
+ Returns 1 if classified, 0 if the caller should continue. */
118
+ static inline int
119
+ ca_classifier_try_point_fast_path (ca_classifier_ctx_t *ctx)
120
+ {
121
+ long i;
122
+ if ( ctx->argc < 1 || ctx->argc != ctx->ca_ndim ) {
123
+ return 0;
124
+ }
125
+ for (i = 0; i < ctx->argc; i++) {
126
+ if ( ! FIXNUM_P(ctx->argv[i]) ) {
127
+ return 0;
128
+ }
129
+ }
130
+
131
+ ctx->info->type = CA_REG_POINT;
132
+ ctx->info->ndim = (int16_t) ctx->argc;
133
+ for (i = 0; i < ctx->argc; i++) {
134
+ ca_size_t k = FIX2LONG(ctx->argv[i]);
135
+ if ( ctx->info->range_check ) {
136
+ /* Bignum is rejected by FIXNUM_P above; the slow path's
137
+ rb_obj_is_kind_of(arg, rb_cInteger) picks it up so the
138
+ Integer semantics are preserved end-to-end. */
139
+ k = ca_classifier_normalize_axis_index(k, ctx->ca_dim[i], (int) i,
140
+ ctx->info->range_check);
141
+ }
142
+ ctx->info->index_type[i] = CA_IDX_SCALAR;
143
+ ctx->info->index[i].scalar = k;
144
+ }
145
+ return 1;
146
+ }
147
+
148
+ /* Fast path 2: ca[flat_addr] single FIXNUM, ndim > 1 → ADDRESS. */
149
+ static inline int
150
+ ca_classifier_try_address_fast_path (ca_classifier_ctx_t *ctx)
151
+ {
152
+ ca_size_t addr;
153
+ if ( ctx->argc != 1 || ctx->ca_ndim <= 1 || ! FIXNUM_P(ctx->argv[0]) ) {
154
+ return 0;
155
+ }
156
+ addr = FIX2LONG(ctx->argv[0]);
157
+ addr = ca_classifier_normalize_flat_address(addr, ctx->ca_elements,
158
+ ctx->info->range_check);
159
+ ctx->info->type = CA_REG_ADDRESS;
160
+ ctx->info->ndim = 1;
161
+ ctx->info->index[0].scalar = addr;
162
+ return 1;
163
+ }
164
+
165
+ /* -------------------------------------------------------------------- */
166
+ /* Single-character alphabetic Symbols (`:a`-`:z` / `:A`-`:Z`) are
167
+ reserved for future contraction notation. Raises
168
+ NotImplementedError rather than the generic IndexError. The
169
+ caller passes any Symbol that is not `:_` / `:*` / `:%`; a
170
+ non-reserved Symbol returns without action. */
171
+ static inline void
172
+ ca_classifier_check_reserved_contraction_symbol (VALUE sym)
173
+ {
174
+ const char *name;
175
+ if ( ! SYMBOL_P(sym) ) return;
176
+ name = rb_id2name(SYM2ID(sym));
177
+ if ( name != NULL
178
+ && name[0] != '\0' && name[1] == '\0'
179
+ && ( ( name[0] >= 'a' && name[0] <= 'z' )
180
+ || ( name[0] >= 'A' && name[0] <= 'Z' ) )
181
+ && name[0] != '_' /* :_ is iterator marker, handled separately */ ) {
182
+ rb_raise(rb_eNotImpError,
183
+ "symbol :%s is reserved for future contraction notation "
184
+ "(not yet implemented)",
185
+ name);
186
+ }
187
+ }
188
+
189
+ /* -------------------------------------------------------------------- */
190
+ /* Per-axis BLOCK builders.
191
+ *
192
+ * Each helper takes the user-supplied arg + axis index + dim and
193
+ * populates info->index[axis].block plus
194
+ * info->index_type[axis] = BLOCK. Error messages are byte-
195
+ * identical with the legacy path so downstream regexes still
196
+ * match. */
197
+
198
+ /* Range → BLOCK */
199
+ static void
200
+ ca_classifier_axis_from_range (ca_classifier_ctx_t *ctx, int axis, VALUE arg)
201
+ {
202
+ ca_size_t start, last, count, step;
203
+ int excl;
204
+ volatile VALUE iv_beg, iv_end, iv_excl;
205
+ CAIndexInfo *info = ctx->info;
206
+ ca_size_t dim = ctx->ca_dim[axis];
207
+
208
+ iv_beg = CA_CLASSIFIER_RANGE_BEG(arg);
209
+ iv_end = CA_CLASSIFIER_RANGE_END(arg);
210
+ iv_excl = CA_CLASSIFIER_RANGE_EXCL(arg);
211
+
212
+ start = NIL_P(iv_beg) ? 0 : NUM2SIZE(iv_beg);
213
+ last = NIL_P(iv_end) ? -1 : NUM2SIZE(iv_end);
214
+ excl = RTEST(iv_excl);
215
+
216
+ info->index_type[axis] = CA_IDX_BLOCK;
217
+
218
+ if ( info->range_check ) {
219
+ /* Inlined CA_CHECK_INDEX_AT so the format string stays
220
+ byte-identical with the surrounding helpers. */
221
+ if ( start < 0 ) start += dim;
222
+ if ( start < 0 || start >= dim ) {
223
+ rb_raise(rb_eIndexError,
224
+ "index out of range at %i-dim ( %" PRId64 " <=> 0..%" PRId64 " )",
225
+ axis, (ca_size_t) start, (ca_size_t) (dim - 1));
226
+ }
227
+ }
228
+
229
+ if ( last < 0 ) {
230
+ last += dim; /* don't use CA_CHECK_INDEX for excl */
231
+ }
232
+ if ( excl && start == last ) {
233
+ info->index[axis].block.start = start;
234
+ info->index[axis].block.count = 0;
235
+ info->index[axis].block.step = 1;
236
+ return;
237
+ }
238
+ if ( excl ) {
239
+ last += ( last >= start ) ? -1 : 1;
240
+ }
241
+ if ( info->range_check ) {
242
+ if ( last < 0 || last >= dim ) {
243
+ rb_raise(rb_eIndexError,
244
+ "index %" PRId64 " is out of range (0..%" PRId64 ") at %i-dim",
245
+ (ca_size_t) last, (ca_size_t) (dim - 1), axis);
246
+ }
247
+ }
248
+ count = llabs(last - start) + 1;
249
+ step = ( last >= start ) ? 1 : -1;
250
+ info->index[axis].block.start = start;
251
+ info->index[axis].block.count = count;
252
+ info->index[axis].block.step = step;
253
+ }
254
+
255
+ #ifdef HAVE_RB_ARITHMETIC_SEQUENCE_EXTRACT
256
+ /* ArithSeq → BLOCK */
257
+ static void
258
+ ca_classifier_axis_from_arithseq (ca_classifier_ctx_t *ctx, int axis, VALUE arg)
259
+ {
260
+ ca_size_t start, last, count, step, bound;
261
+ int excl;
262
+ volatile VALUE iv_beg, iv_end, iv_excl;
263
+ rb_arithmetic_sequence_components_t x;
264
+ CAIndexInfo *info = ctx->info;
265
+ ca_size_t dim = ctx->ca_dim[axis];
266
+
267
+ rb_arithmetic_sequence_extract(arg, &x);
268
+ iv_beg = x.begin;
269
+ iv_end = x.end;
270
+ iv_excl = x.exclude_end;
271
+ step = NUM2SIZE(x.step);
272
+
273
+ start = NIL_P(iv_beg) ? 0 : NUM2SIZE(iv_beg);
274
+ last = NIL_P(iv_end) ? -1 : NUM2SIZE(iv_end);
275
+ excl = RTEST(iv_excl);
276
+
277
+ if ( step == 0 ) {
278
+ rb_raise(rb_eRuntimeError,
279
+ "step in index equals to 0 in block reference");
280
+ }
281
+
282
+ info->index_type[axis] = CA_IDX_BLOCK;
283
+
284
+ /* CAREFUL: always range-check start on the ArithSeq path,
285
+ ignoring info->range_check. An ArithSeq with a negative or
286
+ out-of-range start silently produces an empty region if this
287
+ check is deferred, which upstream callers do not expect. */
288
+ if ( start < 0 ) start += dim;
289
+ if ( start < 0 || start >= dim ) {
290
+ rb_raise(rb_eIndexError,
291
+ "index out of range at %i-dim ( %" PRId64 " <=> 0..%" PRId64 " )",
292
+ axis, (ca_size_t) start, (ca_size_t) (dim - 1));
293
+ }
294
+
295
+ if ( last < 0 ) {
296
+ last += dim;
297
+ }
298
+ if ( excl && start == last ) {
299
+ info->index[axis].block.start = start;
300
+ info->index[axis].block.count = 0;
301
+ info->index[axis].block.step = 1;
302
+ return;
303
+ }
304
+ if ( excl ) {
305
+ last += ( last >= start ) ? -1 : 1;
306
+ }
307
+ if ( last < 0 || last >= dim ) {
308
+ rb_raise(rb_eIndexError,
309
+ "index %" PRId64 " is out of range (0..%" PRId64 ") at %i-dim",
310
+ (ca_size_t) last, (ca_size_t) (dim - 1), axis);
311
+ }
312
+ if ( (last - start) * (long long) step < 0 ) {
313
+ count = 1;
314
+ }
315
+ else {
316
+ count = llabs(last - start) / llabs((long long) step) + 1;
317
+ }
318
+ bound = start + (count - 1) * step;
319
+ if ( bound < 0 ) bound += dim; /* CA_CHECK_INDEX_AT signature */
320
+ if ( bound < 0 || bound >= dim ) {
321
+ rb_raise(rb_eIndexError,
322
+ "index out of range at %i-dim ( %" PRId64 " <=> 0..%" PRId64 " )",
323
+ axis, (ca_size_t) bound, (ca_size_t) (dim - 1));
324
+ }
325
+ info->index[axis].block.start = start;
326
+ info->index[axis].block.count = count;
327
+ info->index[axis].block.step = step;
328
+ }
329
+ #endif
330
+
331
+ /* T_ARRAY → BLOCK. Accepts [nil] / [Range] / [Integer] (len 1),
332
+ [nil, step] / [Range, step] / [start, count] (len 2), or
333
+ [start, count, step] (len 3). */
334
+ static void
335
+ ca_classifier_axis_from_array (ca_classifier_ctx_t *ctx, int axis, VALUE arg)
336
+ {
337
+ CAIndexInfo *info = ctx->info;
338
+ ca_size_t dim = ctx->ca_dim[axis];
339
+ long len = RARRAY_LEN(arg);
340
+
341
+ info->index_type[axis] = CA_IDX_BLOCK;
342
+
343
+ if ( len == 1 ) {
344
+ VALUE a0 = rb_ary_entry(arg, 0);
345
+ if ( NIL_P(a0) ) { /* [nil] → ALL */
346
+ info->index_type[axis] = CA_IDX_ALL;
347
+ return;
348
+ }
349
+ if ( rb_obj_is_kind_of(a0, rb_cRange) ) { /* [Range] → recurse */
350
+ ca_classifier_axis_from_range(ctx, axis, a0);
351
+ return;
352
+ }
353
+ /* [Integer] */
354
+ {
355
+ ca_size_t start = NUM2SIZE(a0);
356
+ if ( start < 0 ) start += dim;
357
+ if ( start < 0 || start >= dim ) {
358
+ rb_raise(rb_eIndexError,
359
+ "index out of range at %i-dim ( %" PRId64 " <=> 0..%" PRId64 " )",
360
+ axis, (ca_size_t) start, (ca_size_t) (dim - 1));
361
+ }
362
+ info->index[axis].block.start = start;
363
+ info->index[axis].block.count = 1;
364
+ info->index[axis].block.step = 1;
365
+ return;
366
+ }
367
+ }
368
+
369
+ if ( len == 2 ) {
370
+ VALUE a0 = rb_ary_entry(arg, 0);
371
+ VALUE a1 = rb_ary_entry(arg, 1);
372
+
373
+ if ( NIL_P(a0) ) { /* [nil, step] */
374
+ ca_size_t start = 0, last, count, step, bound;
375
+ step = NUM2SIZE(a1);
376
+ if ( step == 0 ) {
377
+ rb_raise(rb_eRuntimeError,
378
+ "step in index equals to 0 in block reference");
379
+ }
380
+ last = dim - 1;
381
+ count = ( step < 0 ) ? 1 : (last / step + 1);
382
+ bound = start + (count - 1) * step;
383
+ if ( bound < 0 ) bound += dim;
384
+ if ( bound < 0 || bound >= dim ) {
385
+ rb_raise(rb_eIndexError,
386
+ "index out of range at %i-dim ( %" PRId64 " <=> 0..%" PRId64 " )",
387
+ axis, (ca_size_t) bound, (ca_size_t) (dim - 1));
388
+ }
389
+ info->index[axis].block.start = start;
390
+ info->index[axis].block.count = count;
391
+ info->index[axis].block.step = step;
392
+ return;
393
+ }
394
+
395
+ if ( rb_obj_is_kind_of(a0, rb_cRange) ) { /* [Range, step] */
396
+ ca_size_t start, last, count, step, bound;
397
+ int excl;
398
+ volatile VALUE iv_beg, iv_end, iv_excl;
399
+ iv_beg = CA_CLASSIFIER_RANGE_BEG(a0);
400
+ iv_end = CA_CLASSIFIER_RANGE_END(a0);
401
+ iv_excl = CA_CLASSIFIER_RANGE_EXCL(a0);
402
+ start = NIL_P(iv_beg) ? 0 : NUM2SIZE(iv_beg);
403
+ last = NIL_P(iv_end) ? -1 : NUM2SIZE(iv_end);
404
+ excl = RTEST(iv_excl);
405
+ step = NUM2SIZE(a1);
406
+ if ( step == 0 ) {
407
+ rb_raise(rb_eRuntimeError,
408
+ "step in index equals to 0 in block reference");
409
+ }
410
+ if ( start < 0 ) start += dim;
411
+ if ( start < 0 || start >= dim ) {
412
+ rb_raise(rb_eIndexError,
413
+ "index out of range at %i-dim ( %" PRId64 " <=> 0..%" PRId64 " )",
414
+ axis, (ca_size_t) start, (ca_size_t) (dim - 1));
415
+ }
416
+ if ( last < 0 ) last += dim;
417
+ if ( excl && start == last ) {
418
+ info->index[axis].block.start = start;
419
+ info->index[axis].block.count = 0;
420
+ info->index[axis].block.step = 1;
421
+ return;
422
+ }
423
+ if ( excl ) {
424
+ last += ( last >= start ) ? -1 : 1;
425
+ }
426
+ if ( last < 0 || last >= dim ) {
427
+ rb_raise(rb_eIndexError,
428
+ "index %" PRId64 " is out of range (0..%" PRId64 ") at %i-dim",
429
+ (ca_size_t) last, (ca_size_t) (dim - 1), axis);
430
+ }
431
+ if ( (last - start) * (long long) step < 0 ) {
432
+ count = 1;
433
+ }
434
+ else {
435
+ count = llabs(last - start) / llabs((long long) step) + 1;
436
+ }
437
+ bound = start + (count - 1) * step;
438
+ if ( bound < 0 ) bound += dim;
439
+ if ( bound < 0 || bound >= dim ) {
440
+ rb_raise(rb_eIndexError,
441
+ "index out of range at %i-dim ( %" PRId64 " <=> 0..%" PRId64 " )",
442
+ axis, (ca_size_t) bound, (ca_size_t) (dim - 1));
443
+ }
444
+ info->index[axis].block.start = start;
445
+ info->index[axis].block.count = count;
446
+ info->index[axis].block.step = step;
447
+ return;
448
+ }
449
+
450
+ /* [start, count] */
451
+ {
452
+ ca_size_t start, count, bound;
453
+ start = NUM2SIZE(a0);
454
+ count = NUM2SIZE(a1);
455
+ bound = start + (count - 1);
456
+ if ( start < 0 ) start += dim;
457
+ if ( start < 0 || start >= dim ) {
458
+ rb_raise(rb_eIndexError,
459
+ "index out of range at %i-dim ( %" PRId64 " <=> 0..%" PRId64 " )",
460
+ axis, (ca_size_t) start, (ca_size_t) (dim - 1));
461
+ }
462
+ if ( bound < 0 ) bound += dim;
463
+ if ( bound < 0 || bound >= dim ) {
464
+ rb_raise(rb_eIndexError,
465
+ "index out of range at %i-dim ( %" PRId64 " <=> 0..%" PRId64 " )",
466
+ axis, (ca_size_t) bound, (ca_size_t) (dim - 1));
467
+ }
468
+ info->index[axis].block.start = start;
469
+ info->index[axis].block.count = count;
470
+ info->index[axis].block.step = 1;
471
+ return;
472
+ }
473
+ }
474
+
475
+ if ( len == 3 ) { /* [start, count, step] */
476
+ ca_size_t start, count, step, bound;
477
+ start = NUM2SIZE(rb_ary_entry(arg, 0));
478
+ count = NUM2SIZE(rb_ary_entry(arg, 1));
479
+ step = NUM2SIZE(rb_ary_entry(arg, 2));
480
+ if ( step == 0 ) {
481
+ rb_raise(rb_eRuntimeError,
482
+ "step in index equals to 0 in block reference");
483
+ }
484
+ bound = start + (count - 1) * step;
485
+ if ( start < 0 ) start += dim;
486
+ if ( start < 0 || start >= dim ) {
487
+ rb_raise(rb_eIndexError,
488
+ "index out of range at %i-dim ( %" PRId64 " <=> 0..%" PRId64 " )",
489
+ axis, (ca_size_t) start, (ca_size_t) (dim - 1));
490
+ }
491
+ if ( bound < 0 ) bound += dim;
492
+ if ( bound < 0 || bound >= dim ) {
493
+ rb_raise(rb_eIndexError,
494
+ "index out of range at %i-dim ( %" PRId64 " <=> 0..%" PRId64 " )",
495
+ axis, (ca_size_t) bound, (ca_size_t) (dim - 1));
496
+ }
497
+ info->index[axis].block.start = start;
498
+ info->index[axis].block.count = count;
499
+ info->index[axis].block.step = step;
500
+ return;
501
+ }
502
+
503
+ rb_raise(rb_eIndexError,
504
+ "invalid form of index range at %i-dim "
505
+ "(should be [start[,count[,step]]], [range, step])",
506
+ axis);
507
+ }
508
+
509
+ /* -------------------------------------------------------------------- */
510
+ /* Per-axis dispatcher — main-loop body.
511
+ *
512
+ * Returns 1 to signal the main loop to STOP (a CArray at an axis
513
+ * position triggers a REG = GRID short-circuit that skips
514
+ * remaining axes and the post-loop ndim check). Otherwise
515
+ * returns 0. Sets *out_is_grid when the GRID short-circuit
516
+ * fires; out_is_grid must not be NULL. */
517
+ static int
518
+ ca_classifier_dispatch_axis_arg (ca_classifier_ctx_t *ctx, int axis, VALUE arg,
519
+ int *out_is_grid)
520
+ {
521
+ CAIndexInfo *info = ctx->info;
522
+ ca_size_t dim = ctx->ca_dim[axis];
523
+
524
+ if ( rb_obj_is_kind_of(arg, rb_cInteger) ) {
525
+ ca_size_t k = NUM2SIZE(arg);
526
+ info->index_type[axis] = CA_IDX_SCALAR;
527
+ if ( info->range_check ) {
528
+ k = ca_classifier_normalize_axis_index(k, dim, axis, info->range_check);
529
+ }
530
+ info->index[axis].scalar = k;
531
+ return 0;
532
+ }
533
+ if ( NIL_P(arg) ) {
534
+ info->index_type[axis] = CA_IDX_ALL;
535
+ return 0;
536
+ }
537
+ if ( rb_obj_is_kind_of(arg, rb_cRange) ) {
538
+ ca_classifier_axis_from_range(ctx, axis, arg);
539
+ return 0;
540
+ }
541
+ #ifdef HAVE_RB_ARITHMETIC_SEQUENCE_EXTRACT
542
+ if ( rb_obj_is_kind_of(arg, rb_cArithSeq) ) {
543
+ ca_classifier_axis_from_arithseq(ctx, axis, arg);
544
+ return 0;
545
+ }
546
+ #endif
547
+ if ( TYPE(arg) == T_ARRAY ) {
548
+ ca_classifier_axis_from_array(ctx, axis, arg);
549
+ return 0;
550
+ }
551
+ if ( SYMBOL_P(arg) ) {
552
+ /* :> is the slab-iterator marker. :_ is newaxis, intercepted
553
+ at the [] / []= entry before scan_index sees it — if it
554
+ reaches this branch it slipped past the newaxis hook. */
555
+ if ( arg == ca_classifier_sym_gt ) {
556
+ info->index_type[axis] = CA_IDX_SYMBOL;
557
+ info->index[axis].symbol.id = SYM2ID(arg);
558
+ info->index[axis].symbol.spec = Qnil;
559
+ return 0;
560
+ }
561
+ if ( arg == ca_classifier_sym_under ) {
562
+ /* :_ reached here because it slipped past the [] / []=
563
+ newaxis hook (typically CArray.scan_index called directly). */
564
+ rb_raise(rb_eIndexError,
565
+ "symbol :_ (newaxis) is handled at the [] / []= level, "
566
+ "not valid in scan_index");
567
+ }
568
+ ca_classifier_check_reserved_contraction_symbol(arg); /* raises if reserved */
569
+ rb_raise(rb_eIndexError,
570
+ "symbol :%s is invalid as the index for slab iterator "
571
+ "(use :> instead)",
572
+ rb_id2name(SYM2ID(arg)));
573
+ }
574
+ if ( rb_obj_is_carray(arg) ) {
575
+ /* CArray (boolean / integer) at an axis position → GRID.
576
+ Signal the caller to stop populating later axes; the
577
+ main-loop caller breaks out after this axis. */
578
+ CArray *ci;
579
+ TypedData_Get_Struct(arg, CArray, &carray_data_type, ci);
580
+ if ( ca_is_boolean_type(ci) || ca_is_integer_type(ci) ) {
581
+ if ( out_is_grid ) *out_is_grid = 1;
582
+ return 1; /* signal caller: stop main loop, REG = GRID */
583
+ }
584
+ rb_raise(rb_eIndexError,
585
+ "data_type %s is invalid for reference by gridding at %i-dim "
586
+ "(should be boolean or integer)",
587
+ ca_type_name[ci->data_type], axis);
588
+ }
589
+ {
590
+ VALUE inspect = rb_inspect(arg);
591
+ rb_raise(rb_eIndexError,
592
+ "object '%s' is invalid for the index for reference at %i-dim",
593
+ StringValuePtr(inspect), axis);
594
+ }
595
+ }
596
+
597
+ /* -------------------------------------------------------------------- */
598
+ /* Rubber-dim pre-pass. Walks ctx->argv detecting Qfalse (or its
599
+ `:~` alias), expands the rubber slot into `Qnil` fills in
600
+ ctx->argv_expanded, and re-points ctx->argv at it. At most one
601
+ rubber marker is honoured; a second one falls through to the
602
+ per-axis dispatcher and raises. */
603
+
604
+ static void
605
+ ca_classifier_expand_rubber_dim (ca_classifier_ctx_t *ctx)
606
+ {
607
+ long i, j;
608
+ long argc = ctx->argc;
609
+ VALUE *argv = ctx->argv;
610
+ int rubber_pos = -1;
611
+ long expanded_argc;
612
+ int rndim;
613
+
614
+ for (i = 0; i < argc; i++) {
615
+ if ( argv[i] == Qfalse || argv[i] == ca_classifier_sym_tilde ) {
616
+ rubber_pos = (int) i; /* :~ is an alias for the `false` rubber marker */
617
+ /* Only the first rubber marker is honoured; later occurrences fall
618
+ through to the per-axis dispatcher and raise. */
619
+ break;
620
+ }
621
+ }
622
+
623
+ if ( rubber_pos < 0 ) {
624
+ /* No rubber dim — validate argc == ndim and pass through. */
625
+ if ( argc != ctx->ca_ndim ) {
626
+ rb_raise(rb_eIndexError,
627
+ "number of indices exceeds the ndim of carray (%i > %i)",
628
+ (int) argc, ctx->ca_ndim);
629
+ }
630
+ return;
631
+ }
632
+
633
+ if ( argc > ctx->ca_ndim + 1 ) {
634
+ rb_raise(rb_eIndexError,
635
+ "index specification exceeds the ndim of carray (%i)",
636
+ ctx->ca_ndim);
637
+ }
638
+
639
+ /* rndim = number of nils the rubber expands to.
640
+ *
641
+ * CAREFUL: rndim == 0 is a valid outcome — the rubber consumes
642
+ * nothing when argc == ndim + 1 (e.g. `[false, 0, 1]` on
643
+ * ndim = 2 collapses to `[0, 1]`). Clamping only at the
644
+ * negative side preserves that path. */
645
+ rndim = ctx->ca_ndim - (int)argc + 1;
646
+ if ( rndim < 0 ) rndim = 0;
647
+ expanded_argc = argc - 1 + rndim;
648
+
649
+ j = 0;
650
+ for (i = 0; i < argc; i++) {
651
+ if ( i == rubber_pos ) {
652
+ int k;
653
+ for (k = 0; k < rndim; k++) {
654
+ ctx->argv_expanded[j++] = Qnil;
655
+ }
656
+ } else {
657
+ ctx->argv_expanded[j++] = argv[i];
658
+ }
659
+ }
660
+ ctx->argc = expanded_argc;
661
+ ctx->argv = ctx->argv_expanded;
662
+ }
663
+
664
+ /* -------------------------------------------------------------------- */
665
+ /* Final REG decision — walks info->index_type[] and decides
666
+ POINT / BLOCK / ITERATOR / GRID. */
667
+
668
+ static void
669
+ ca_classifier_finalize_reg (ca_classifier_ctx_t *ctx, int is_grid)
670
+ {
671
+ int i, is_point = 1, is_iterator = 0;
672
+ CAIndexInfo *info = ctx->info;
673
+
674
+ for (i = 0; i < info->ndim; i++) {
675
+ switch ( info->index_type[i] ) {
676
+ case CA_IDX_SCALAR: break;
677
+ case CA_IDX_ALL: is_point = 0; break;
678
+ case CA_IDX_SYMBOL: is_iterator = 1; break;
679
+ default: is_point = 0; break;
680
+ }
681
+ }
682
+
683
+ if ( is_grid ) info->type = CA_REG_GRID;
684
+ else if ( is_iterator ) info->type = CA_REG_ITERATOR;
685
+ else if ( is_point ) info->type = CA_REG_POINT;
686
+ else info->type = CA_REG_BLOCK;
687
+
688
+ /* CAREFUL: when REG == ITERATOR, every CA_IDX_SCALAR axis must
689
+ be rewritten into a BLOCK with {start, count = 1, step = 1}.
690
+ Downstream iterator machinery treats SCALAR and BLOCK
691
+ differently, and skipping this rewrite silently changes the
692
+ iteration shape. */
693
+ if ( info->type == CA_REG_ITERATOR ) {
694
+ for (i = 0; i < info->ndim; i++) {
695
+ if ( info->index_type[i] == CA_IDX_SCALAR ) {
696
+ ca_size_t start = info->index[i].scalar;
697
+ info->index_type[i] = CA_IDX_BLOCK;
698
+ info->index[i].block.start = start;
699
+ info->index[i].block.step = 1;
700
+ info->index[i].block.count = 1;
701
+ }
702
+ }
703
+ }
704
+ }
705
+
706
+ /* -------------------------------------------------------------------- */
707
+ /* argc == 1 special dispatch. Returns 1 if the region was
708
+ classified here, 0 to continue to the main loop. */
709
+
710
+ static int
711
+ ca_classifier_try_argc1_special (ca_classifier_ctx_t *ctx)
712
+ {
713
+ VALUE arg = ctx->argv[0];
714
+ CAIndexInfo *info = ctx->info;
715
+
716
+ /* A single Qfalse / :~ means ALL, so `ca[:~]` and `ca[false]`
717
+ both collapse to `ca[]`. */
718
+ if ( arg == Qfalse || arg == ca_classifier_sym_tilde ) {
719
+ info->type = CA_REG_ALL;
720
+ return 1;
721
+ }
722
+
723
+ /* T_STRING: "@name" → ATTRIBUTE, "field" → MEMBER. */
724
+ if ( TYPE(arg) == T_STRING ) {
725
+ const char *s = StringValuePtr(arg);
726
+ if ( s[0] == '@' ) {
727
+ info->type = CA_REG_ATTRIBUTE;
728
+ info->symbol = rb_str_new2(s + 1);
729
+ } else {
730
+ info->type = CA_REG_MEMBER;
731
+ info->symbol = ID2SYM(rb_intern(s));
732
+ }
733
+ return 1;
734
+ }
735
+
736
+ /* argc == 1 CArray → GRID / MAPPING / SELECT. */
737
+ if ( rb_obj_is_carray(arg) ) {
738
+ CArray *cs;
739
+ TypedData_Get_Struct(arg, CArray, &carray_data_type, cs);
740
+ if ( ca_is_integer_type(cs) ) {
741
+ if ( ctx->ca_ndim == 1 && cs->ndim == 1 ) {
742
+ info->type = CA_REG_GRID;
743
+ } else {
744
+ info->type = CA_REG_MAPPING;
745
+ }
746
+ return 1;
747
+ }
748
+ if ( ca_is_boolean_type(cs) ) {
749
+ if ( ctx->ca_elements != cs->elements ) {
750
+ rb_raise(rb_eRuntimeError,
751
+ "mismatch of # of elements ( %" PRId64 " <=> %" PRId64 " ) "
752
+ "in reference by selection",
753
+ (ca_size_t) cs->elements,
754
+ (ca_size_t) ctx->ca_elements);
755
+ }
756
+ info->type = CA_REG_SELECT;
757
+ info->select = cs;
758
+ return 1;
759
+ }
760
+ rb_raise(rb_eIndexError,
761
+ "data_type %s is invalid for reference by selection/mapping"
762
+ "(should be boolean or integer)",
763
+ ca_type_name[cs->data_type]);
764
+ }
765
+
766
+ /* :* → UNBOUND_REPEAT. */
767
+ if ( arg == ca_classifier_sym_star ) {
768
+ info->type = CA_REG_UNBOUND_REPEAT;
769
+ return 1;
770
+ }
771
+
772
+ /* ndim > 1 with Integer → ADDRESS. Fixnum was caught by the
773
+ fast path; this branch handles Bignum. */
774
+ if ( ctx->ca_ndim > 1 && rb_obj_is_kind_of(arg, rb_cInteger) ) {
775
+ ca_size_t addr = NUM2SIZE(arg);
776
+ info->type = CA_REG_ADDRESS;
777
+ info->ndim = 1;
778
+ if ( info->range_check ) {
779
+ CA_CHECK_INDEX(addr, ctx->ca_elements);
780
+ }
781
+ info->index[0].scalar = addr;
782
+ return 1;
783
+ }
784
+
785
+ /* ndim > 1 with nil → FLATTEN. */
786
+ if ( ctx->ca_ndim > 1 && NIL_P(arg) ) {
787
+ info->type = CA_REG_FLATTEN;
788
+ return 1;
789
+ }
790
+
791
+ /* ndim > 1 with Range / ArithSeq / T_ARRAY → ADDRESS_COMPLEX.
792
+ The classifier only sets info->type here; the actual
793
+ [start, count, step] triple is built by re-scanning argv
794
+ against a flat dim = [elements] at the Ruby surface
795
+ (rb_ca_s_scan_index_v2 below). */
796
+ if ( ctx->ca_ndim > 1 ) {
797
+ info->type = CA_REG_ADDRESS_COMPLEX;
798
+ return 1;
799
+ }
800
+
801
+ /* argc == 1 && ndim == 1: fall through to the main loop
802
+ (which classifies this as POINT 1-D). */
803
+ return 0;
804
+ }
805
+
806
+ /* -------------------------------------------------------------------- */
807
+ /* Entry point. */
808
+
809
+ void
810
+ rb_ca_scan_index_v2 (int ca_ndim, ca_size_t *ca_dim, ca_size_t ca_elements,
811
+ long argc, VALUE *argv, CAIndexInfo *info)
812
+ {
813
+ ca_classifier_ctx_t ctx;
814
+ long i;
815
+
816
+ ca_classifier_sym_cache_init();
817
+
818
+ ctx.ca_ndim = ca_ndim;
819
+ ctx.ca_dim = ca_dim;
820
+ ctx.ca_elements = ca_elements;
821
+ ctx.argc = argc;
822
+ ctx.argv = argv;
823
+ ctx.info = info;
824
+
825
+ info->ndim = 0;
826
+ info->select = NULL;
827
+
828
+ /* argc == 0 → ALL */
829
+ if ( argc == 0 ) {
830
+ info->type = CA_REG_ALL;
831
+ return;
832
+ }
833
+
834
+ /* argv[0] = multi-char Symbol → METHOD_CALL. Single-char
835
+ specials (:_ / :* / :%) fall through the strlen check and are
836
+ handled downstream (argc == 1 special / fast paths / main
837
+ loop). Single-char alphabetic reserved-contraction Symbols
838
+ also fall through here and are raised later by the per-axis
839
+ dispatcher's SYMBOL_P branch. */
840
+ if ( SYMBOL_P(argv[0]) ) {
841
+ const char *name = rb_id2name(SYM2ID(argv[0]));
842
+ if ( name != NULL && strlen(name) > 1 ) {
843
+ info->type = CA_REG_METHOD_CALL;
844
+ info->symbol = argv[0];
845
+ return;
846
+ }
847
+ }
848
+
849
+ /* Fast paths. */
850
+ if ( ca_classifier_try_point_fast_path(&ctx) ) return;
851
+ if ( ca_classifier_try_address_fast_path(&ctx) ) return;
852
+
853
+ /* argc == 1 special cases. */
854
+ if ( argc == 1 ) {
855
+ if ( ca_classifier_try_argc1_special(&ctx) ) return;
856
+ }
857
+
858
+ /* Main loop entered when argc >= 1 was not handled above, or
859
+ when argc == 1 && ndim == 1 falls through to the POINT 1-D
860
+ classification below. */
861
+
862
+ /* CAREFUL: pre-scan for :% / :*. Either Symbol at any position
863
+ pins the final REG and short-circuits both the main loop and
864
+ the argc / ndim validation below — skipping the pre-scan lets
865
+ the ndim mismatch check fire before the Symbol is
866
+ recognised. */
867
+ for (i = 0; i < ctx.argc; i++) {
868
+ if ( ctx.argv[i] == ca_classifier_sym_perc ) {
869
+ info->type = CA_REG_REPEAT;
870
+ return;
871
+ }
872
+ if ( ctx.argv[i] == ca_classifier_sym_star ) {
873
+ info->type = CA_REG_UNBOUND_REPEAT;
874
+ return;
875
+ }
876
+ }
877
+
878
+ ca_classifier_expand_rubber_dim(&ctx);
879
+
880
+ info->ndim = (int16_t) ctx.argc;
881
+ {
882
+ int is_grid = 0;
883
+ int stop = 0;
884
+ for (i = 0; i < ctx.argc && ! stop; i++) {
885
+ stop = ca_classifier_dispatch_axis_arg(&ctx, (int) i, ctx.argv[i], &is_grid);
886
+ }
887
+
888
+ /* CAREFUL: the GRID short-circuit skips the post-loop ndim
889
+ validation. A CArray at an axis position may reduce the
890
+ effective info->ndim below ctx.ca_ndim, and re-checking
891
+ here would raise on legitimate grid regions. */
892
+ if ( ! is_grid && ctx.ca_ndim != info->ndim ) {
893
+ rb_raise(rb_eIndexError,
894
+ "number of indices does not equal to the ndim (%i != %i)",
895
+ info->ndim, ctx.ca_ndim);
896
+ }
897
+
898
+ ca_classifier_finalize_reg(&ctx, is_grid);
899
+ }
900
+ }
901
+
902
+ /* -------------------------------------------------------------------- */
903
+ /* Debug / test surface: CArray._scan_index_v2(dim_array, idx_array)
904
+ returns a plain [type_int, index_array] pair rather than the
905
+ full CAIndexInfo struct. Kept for spec regression checks and
906
+ downstream introspection. */
907
+
908
+ static VALUE
909
+ rb_ca_s_scan_index_v2 (VALUE self, VALUE rdim, VALUE ridx)
910
+ {
911
+ CAIndexInfo info;
912
+ ca_size_t dim[CA_RANK_MAX];
913
+ ca_size_t elements;
914
+ int ndim, i;
915
+ VALUE rindex;
916
+
917
+ Check_Type(rdim, T_ARRAY);
918
+ Check_Type(ridx, T_ARRAY);
919
+
920
+ ndim = (int) RARRAY_LEN(rdim);
921
+ elements = 1;
922
+ for (i = 0; i < ndim; i++) {
923
+ dim[i] = NUM2SIZE(rb_ary_entry(rdim, i));
924
+ elements *= dim[i];
925
+ }
926
+
927
+ CA_CHECK_RANK(ndim);
928
+ CA_CHECK_DIM(ndim, dim);
929
+
930
+ info.range_check = 1;
931
+ rb_ca_scan_index_v2(ndim, dim, elements,
932
+ RARRAY_LEN(ridx),
933
+ (VALUE *) RARRAY_CONST_PTR(ridx), &info);
934
+
935
+ rindex = rb_ary_new();
936
+ switch ( info.type ) {
937
+ case CA_REG_ALL:
938
+ case CA_REG_FLATTEN:
939
+ case CA_REG_SELECT:
940
+ case CA_REG_REPEAT:
941
+ case CA_REG_GRID:
942
+ case CA_REG_MAPPING:
943
+ case CA_REG_METHOD_CALL:
944
+ case CA_REG_UNBOUND_REPEAT:
945
+ case CA_REG_MEMBER:
946
+ case CA_REG_ATTRIBUTE:
947
+ break;
948
+ case CA_REG_ADDRESS:
949
+ rb_ary_push(rindex, SIZE2NUM(info.index[0].scalar));
950
+ break;
951
+ case CA_REG_ADDRESS_COMPLEX: {
952
+ /* Recursive flat re-scan: build a 1-D dim = [elements] and
953
+ re-classify; the resulting BLOCK / SCALAR / ALL is wrapped
954
+ in a single-element array so the caller can consume it
955
+ uniformly with the other cases. */
956
+ CAIndexInfo flat_info;
957
+ ca_size_t flat_dim[1];
958
+ VALUE inner;
959
+ flat_dim[0] = elements;
960
+ flat_info.range_check = 1;
961
+ rb_ca_scan_index_v2(1, flat_dim, elements,
962
+ RARRAY_LEN(ridx),
963
+ (VALUE *) RARRAY_CONST_PTR(ridx), &flat_info);
964
+ /* Wrap index[0] as [start, count, step] / scalar / [0, elements, 1]
965
+ depending on which per-axis kind the flat re-scan produced. */
966
+ switch ( flat_info.index_type[0] ) {
967
+ case CA_IDX_BLOCK:
968
+ inner = rb_ary_new3(3,
969
+ SIZE2NUM(flat_info.index[0].block.start),
970
+ SIZE2NUM(flat_info.index[0].block.count),
971
+ SIZE2NUM(flat_info.index[0].block.step));
972
+ rb_ary_push(rindex, inner);
973
+ break;
974
+ case CA_IDX_SCALAR:
975
+ rb_ary_push(rindex, SIZE2NUM(flat_info.index[0].scalar));
976
+ break;
977
+ case CA_IDX_ALL:
978
+ rb_ary_push(rindex,
979
+ rb_ary_new3(3, INT2NUM(0), SIZE2NUM(elements),
980
+ INT2NUM(1)));
981
+ break;
982
+ default: break;
983
+ }
984
+ break;
985
+ }
986
+ case CA_REG_POINT:
987
+ for (i = 0; i < ndim; i++) {
988
+ rb_ary_push(rindex, SIZE2NUM(info.index[i].scalar));
989
+ }
990
+ break;
991
+ case CA_REG_BLOCK:
992
+ case CA_REG_ITERATOR:
993
+ for (i = 0; i < ndim; i++) {
994
+ switch ( info.index_type[i] ) {
995
+ case CA_IDX_SCALAR:
996
+ rb_ary_push(rindex, SIZE2NUM(info.index[i].scalar));
997
+ break;
998
+ case CA_IDX_ALL:
999
+ rb_ary_push(rindex,
1000
+ rb_ary_new3(3, INT2NUM(0),
1001
+ rb_ary_entry(rdim, i),
1002
+ INT2NUM(1)));
1003
+ break;
1004
+ case CA_IDX_BLOCK:
1005
+ rb_ary_push(rindex,
1006
+ rb_ary_new3(3,
1007
+ SIZE2NUM(info.index[i].block.start),
1008
+ SIZE2NUM(info.index[i].block.count),
1009
+ SIZE2NUM(info.index[i].block.step)));
1010
+ break;
1011
+ case CA_IDX_SYMBOL:
1012
+ rb_ary_push(rindex,
1013
+ rb_ary_new3(2,
1014
+ ID2SYM(info.index[i].symbol.id),
1015
+ info.index[i].symbol.spec));
1016
+ break;
1017
+ default:
1018
+ rb_raise(rb_eRuntimeError, "unknown index spec");
1019
+ }
1020
+ }
1021
+ break;
1022
+ default:
1023
+ rb_raise(rb_eArgError, "unknown index specification");
1024
+ }
1025
+
1026
+ return rb_ary_new3(2, INT2NUM(info.type), rindex);
1027
+ }
1028
+
1029
+ void
1030
+ Init_carray_index_classifier (void)
1031
+ {
1032
+ ca_classifier_sym_cache_init();
1033
+ rb_define_singleton_method(rb_cCArray, "_scan_index_v2",
1034
+ rb_ca_s_scan_index_v2, 2);
1035
+ }