carray 2.0.1 → 3.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (386) hide show
  1. checksums.yaml +4 -4
  2. data/.yardopts +6 -25
  3. data/CHANGELOG.md +338 -0
  4. data/{NEWS.md → CHANGELOG.v1.md} +3 -0
  5. data/LICENSE +1 -1
  6. data/README.md +120 -36
  7. data/carray.gemspec +32 -30
  8. data/ext/ca_array_pool.c +91 -0
  9. data/ext/ca_axis_descriptor.h +186 -0
  10. data/ext/ca_axis_dispatch.c +924 -0
  11. data/ext/ca_axis_group.c +1244 -0
  12. data/ext/ca_bincmp_dispatch.c +76 -0
  13. data/ext/ca_bincmp_dispatch.h +85 -0
  14. data/ext/ca_binop_dispatch.c +124 -0
  15. data/ext/ca_binop_dispatch.h +152 -0
  16. data/ext/ca_categorical_iterator.c +1375 -0
  17. data/ext/ca_compare.c +94 -0
  18. data/ext/ca_compare.h +26 -0
  19. data/ext/ca_composite_dispatch.c +414 -0
  20. data/ext/ca_composite_dispatch.h +116 -0
  21. data/ext/ca_for_buffer.h +96 -0
  22. data/ext/ca_for_each_element.h +239 -0
  23. data/ext/ca_group_iter.c +304 -0
  24. data/ext/ca_iter_substrate.h +325 -0
  25. data/ext/ca_kernel_iterator.c +4367 -0
  26. data/ext/ca_kernel_iterator.h +2596 -0
  27. data/ext/ca_moncmp_dispatch.c +37 -0
  28. data/ext/ca_moncmp_dispatch.h +62 -0
  29. data/ext/ca_monop_dispatch.c +200 -0
  30. data/ext/ca_monop_dispatch.h +235 -0
  31. data/ext/ca_obj_array.c +355 -359
  32. data/ext/ca_obj_bincmp.c +839 -0
  33. data/ext/ca_obj_binop.c +948 -0
  34. data/ext/ca_obj_bitarray.c +369 -164
  35. data/ext/ca_obj_bitfield.c +294 -234
  36. data/ext/ca_obj_block.c +189 -711
  37. data/ext/ca_obj_byte_swap.c +766 -0
  38. data/ext/ca_obj_const_string.c +967 -0
  39. data/ext/ca_obj_face.c +750 -0
  40. data/ext/ca_obj_face.h +279 -0
  41. data/ext/ca_obj_fake.c +239 -100
  42. data/ext/ca_obj_farray.c +54 -441
  43. data/ext/ca_obj_field.c +82 -529
  44. data/ext/ca_obj_fixlen_string.c +308 -0
  45. data/ext/ca_obj_grid.c +866 -440
  46. data/ext/ca_obj_meld.c +1039 -0
  47. data/ext/ca_obj_moncmp.c +588 -0
  48. data/ext/ca_obj_monop.c +1123 -0
  49. data/ext/ca_obj_object.c +866 -296
  50. data/ext/ca_obj_record.c +470 -0
  51. data/ext/ca_obj_reduce.c +97 -82
  52. data/ext/ca_obj_refer.c +593 -459
  53. data/ext/ca_obj_remap.c +475 -0
  54. data/ext/ca_obj_repeat.c +92 -477
  55. data/ext/ca_obj_roll.c +624 -0
  56. data/ext/ca_obj_select.c +344 -296
  57. data/ext/ca_obj_select_axis.c +1306 -0
  58. data/ext/ca_obj_shift.c +231 -793
  59. data/ext/ca_obj_source.c +78 -0
  60. data/ext/ca_obj_stack.c +1173 -0
  61. data/ext/ca_obj_stride.c +2584 -0
  62. data/ext/ca_obj_string.c +270 -0
  63. data/ext/ca_obj_tile.c +622 -0
  64. data/ext/ca_obj_time.c +548 -0
  65. data/ext/ca_obj_timedelta.c +437 -0
  66. data/ext/ca_obj_transpose.c +62 -516
  67. data/ext/ca_obj_triop.c +785 -0
  68. data/ext/ca_obj_window.c +1202 -565
  69. data/ext/ca_op_byte_swap.c +175 -0
  70. data/ext/ca_op_cmplx64.h +123 -0
  71. data/ext/ca_op_ipower.c +316 -0
  72. data/ext/ca_op_powi.h +88 -0
  73. data/ext/ca_sort_kernels.h +132 -0
  74. data/ext/ca_sweep_engine.c +473 -0
  75. data/ext/ca_sweep_engine.h +166 -0
  76. data/ext/ca_transform_common.c +235 -0
  77. data/ext/ca_triop_dispatch.c +55 -0
  78. data/ext/ca_triop_dispatch.h +62 -0
  79. data/ext/carray.h +810 -420
  80. data/ext/carray_access.c +873 -731
  81. data/ext/carray_attribute.c +98 -329
  82. data/ext/carray_bincount.c +255 -0
  83. data/ext/carray_broadcast.c +376 -0
  84. data/ext/carray_build_flags.h +3 -0
  85. data/ext/carray_call_cfunc.c +2897 -874
  86. data/ext/carray_call_cfunc.h +313 -0
  87. data/ext/carray_cast.c +1264 -315
  88. data/ext/carray_cast_func.rb +81 -40
  89. data/ext/carray_class.c +53 -63
  90. data/ext/carray_config.h +28 -0
  91. data/ext/carray_conversion.c +350 -346
  92. data/ext/carray_copy.c +168 -270
  93. data/ext/carray_core.c +1396 -206
  94. data/ext/carray_count.c +312 -0
  95. data/ext/carray_data_type.c +43 -19
  96. data/ext/carray_element.c +585 -213
  97. data/ext/carray_factorize.c +2542 -0
  98. data/ext/carray_generate.c +230 -559
  99. data/ext/carray_histogram.c +490 -0
  100. data/ext/carray_hold.c +228 -0
  101. data/ext/carray_index_classifier.c +1021 -0
  102. data/ext/carray_index_classifier.h +27 -0
  103. data/ext/carray_internal.h +136 -0
  104. data/ext/carray_kernels_bincmp.c +4446 -0
  105. data/ext/carray_kernels_binop.c +11001 -0
  106. data/ext/carray_kernels_init.c +1131 -0
  107. data/ext/carray_kernels_map.c +3467 -0
  108. data/ext/carray_kernels_moncmp.c +2097 -0
  109. data/ext/carray_kernels_monop.c +18313 -0
  110. data/ext/carray_kernels_reduce_aggregate.c +25837 -0
  111. data/ext/carray_kernels_reduce_boolean.c +330 -0
  112. data/ext/carray_kernels_reduce_cumulative.c +14593 -0
  113. data/ext/carray_kernels_reduce_extreme.c +16948 -0
  114. data/ext/carray_kernels_reduce_variance.c +3910 -0
  115. data/ext/carray_kernels_scan.c +3693 -0
  116. data/ext/carray_kernels_search.c +32138 -0
  117. data/ext/carray_kernels_sort.c +10626 -0
  118. data/ext/carray_kernels_triop.c +1392 -0
  119. data/ext/carray_lazy.c +737 -0
  120. data/ext/carray_loop.c +88 -200
  121. data/ext/carray_mask.c +853 -158
  122. data/ext/carray_math_kernel.h +120 -0
  123. data/ext/carray_mathfunc.c +10 -241
  124. data/ext/carray_median_percentile.c +1257 -0
  125. data/ext/carray_memory_view.c +1650 -0
  126. data/ext/carray_operator.c +1525 -320
  127. data/ext/carray_order.c +664 -1394
  128. data/ext/carray_partition.c +416 -0
  129. data/ext/carray_random.c +518 -0
  130. data/ext/carray_scatter.c +357 -0
  131. data/ext/carray_slab.c +1219 -0
  132. data/ext/carray_slab.h +84 -0
  133. data/ext/carray_sort.c +829 -0
  134. data/ext/carray_sort_kernel.c +620 -0
  135. data/ext/carray_struct.c +695 -0
  136. data/ext/carray_test.c +343 -229
  137. data/ext/carray_undef.c +34 -17
  138. data/ext/carray_utils.c +175 -74
  139. data/ext/extconf.rb +234 -55
  140. data/ext/mk_call_cfunc.rb +671 -0
  141. data/ext/mkkernel.rb +9096 -0
  142. data/ext/ruby_carray.c +211 -108
  143. data/ext/version.h +4 -14
  144. data/ext/version.rb +5 -13
  145. data/lib/carray/arrow_tensor.rb +401 -0
  146. data/lib/carray/attribute.rb +166 -0
  147. data/lib/carray/autoload_carray.rb +239 -0
  148. data/lib/carray/autoload_method_extension.rb +45 -0
  149. data/lib/carray/axis_group.rb +711 -0
  150. data/lib/carray/basics.rb +481 -0
  151. data/lib/carray/bincount_nd.rb +358 -0
  152. data/lib/carray/block_iterator.rb +614 -0
  153. data/lib/carray/boolean_reduce.rb +109 -0
  154. data/lib/carray/categorical.rb +561 -0
  155. data/lib/carray/categorical_iterator.rb +1084 -0
  156. data/lib/carray/complex.rb +150 -0
  157. data/lib/carray/conditional.rb +216 -0
  158. data/lib/carray/const_string.rb +228 -0
  159. data/lib/carray/construct.rb +160 -328
  160. data/lib/carray/core_extensions.rb +297 -0
  161. data/lib/carray/data_type_extension.rb +250 -0
  162. data/lib/carray/fixlen_string.rb +95 -0
  163. data/lib/carray/frame/concat.rb +132 -0
  164. data/lib/carray/frame/convert.rb +95 -0
  165. data/lib/carray/frame/csv_parser.rb +211 -0
  166. data/lib/carray/frame/frame.rb +642 -0
  167. data/lib/carray/frame/group.rb +186 -0
  168. data/lib/carray/frame/io.rb +321 -0
  169. data/lib/carray/frame/join.rb +248 -0
  170. data/lib/carray/frame/records.rb +99 -0
  171. data/lib/carray/frame/sort.rb +113 -0
  172. data/lib/carray/frame/verbs.rb +316 -0
  173. data/lib/carray/frame.rb +16 -0
  174. data/lib/carray/fuse_source.rb +123 -0
  175. data/lib/carray/fusion.rb +218 -0
  176. data/lib/carray/histogram.rb +512 -0
  177. data/lib/carray/inspect.rb +37 -26
  178. data/lib/carray/iterator.rb +58 -349
  179. data/lib/carray/lazy.rb +941 -0
  180. data/lib/carray/mask_gap_fill.rb +200 -0
  181. data/lib/carray/math.rb +78 -342
  182. data/lib/carray/meld_reduce.rb +289 -0
  183. data/lib/carray/methods/align_addr.rb +116 -0
  184. data/lib/carray/methods/bin.rb +128 -0
  185. data/lib/carray/methods/bincount.rb +87 -0
  186. data/lib/carray/methods/bit_string.rb +92 -0
  187. data/lib/carray/methods/broadcast.rb +63 -0
  188. data/lib/carray/methods/choose.rb +39 -0
  189. data/lib/carray/methods/composition.rb +280 -0
  190. data/lib/carray/methods/gather_nd.rb +206 -0
  191. data/lib/carray/methods/index.rb +39 -0
  192. data/lib/carray/methods/insert_block.rb +99 -0
  193. data/lib/carray/methods/is_in.rb +141 -0
  194. data/lib/carray/methods/join.rb +90 -0
  195. data/lib/carray/methods/locate_addr.rb +52 -0
  196. data/lib/carray/methods/mask_duplicates.rb +41 -0
  197. data/lib/carray/methods/meshgrid.rb +90 -0
  198. data/lib/carray/methods/mode.rb +126 -0
  199. data/lib/carray/methods/nunique.rb +46 -0
  200. data/lib/carray/methods/resize.rb +56 -0
  201. data/lib/carray/methods/snap.rb +161 -0
  202. data/lib/carray/methods/string_format.rb +57 -0
  203. data/lib/carray/methods/unique.rb +47 -0
  204. data/lib/carray/methods/value_counts.rb +71 -0
  205. data/lib/carray/mkmf.rb +124 -101
  206. data/lib/carray/runtime.rb +89 -0
  207. data/lib/carray/serialize.rb +478 -167
  208. data/lib/carray/slab_iterator.rb +305 -0
  209. data/lib/carray/stack.rb +291 -0
  210. data/lib/carray/string.rb +56 -180
  211. data/lib/carray/string_operation_extension.rb +289 -0
  212. data/lib/carray/struct.rb +335 -323
  213. data/lib/carray/struct_builder.rb +697 -0
  214. data/lib/carray/table.rb +41 -2
  215. data/lib/carray/time.rb +2654 -38
  216. data/lib/carray/window_iterator.rb +927 -0
  217. data/lib/carray.rb +55 -57
  218. data/yard-stubs/ca_obj_array.rb +385 -0
  219. data/yard-stubs/ca_obj_bitarray.rb +38 -0
  220. data/yard-stubs/ca_obj_bitfield.rb +43 -0
  221. data/yard-stubs/ca_obj_block.rb +73 -0
  222. data/yard-stubs/ca_obj_byte_swap.rb +56 -0
  223. data/yard-stubs/ca_obj_fake.rb +31 -0
  224. data/yard-stubs/ca_obj_farray.rb +32 -0
  225. data/yard-stubs/ca_obj_field.rb +45 -0
  226. data/yard-stubs/ca_obj_grid.rb +35 -0
  227. data/yard-stubs/ca_obj_refer.rb +72 -0
  228. data/yard-stubs/ca_obj_roll.rb +45 -0
  229. data/yard-stubs/ca_obj_shift.rb +43 -0
  230. data/yard-stubs/ca_obj_stride.rb +181 -0
  231. data/yard-stubs/ca_obj_tile.rb +29 -0
  232. data/yard-stubs/ca_obj_transpose.rb +40 -0
  233. data/yard-stubs/ca_obj_window.rb +49 -0
  234. data/yard-stubs/carray_access.rb +131 -0
  235. data/yard-stubs/carray_attribute.rb +246 -0
  236. data/yard-stubs/carray_broadcast.rb +37 -0
  237. data/yard-stubs/carray_cast.rb +489 -0
  238. data/yard-stubs/carray_class.rb +65 -0
  239. data/yard-stubs/carray_conversion.rb +76 -0
  240. data/yard-stubs/carray_copy.rb +79 -0
  241. data/yard-stubs/carray_core.rb +114 -0
  242. data/yard-stubs/carray_count.rb +79 -0
  243. data/yard-stubs/carray_element.rb +108 -0
  244. data/yard-stubs/carray_generate.rb +66 -0
  245. data/yard-stubs/carray_lazy.rb +23 -0
  246. data/yard-stubs/carray_loop.rb +140 -0
  247. data/yard-stubs/carray_mask.rb +259 -0
  248. data/yard-stubs/carray_math.rb +132 -0
  249. data/yard-stubs/carray_mathfunc.rb +45 -0
  250. data/yard-stubs/carray_median_percentile.rb +89 -0
  251. data/yard-stubs/carray_memory_view.rb +163 -0
  252. data/yard-stubs/carray_order.rb +312 -0
  253. data/yard-stubs/carray_random.rb +89 -0
  254. data/yard-stubs/carray_scatter.rb +106 -0
  255. data/yard-stubs/carray_slab.rb +57 -0
  256. data/yard-stubs/carray_sort.rb +163 -0
  257. data/yard-stubs/carray_test.rb +85 -0
  258. data/yard-stubs/carray_undef.rb +64 -0
  259. data/yard-stubs/carray_utils.rb +97 -0
  260. data/yard-stubs/ruby_carray.rb +193 -0
  261. metadata +220 -138
  262. data/Rakefile +0 -51
  263. data/TODO.md +0 -18
  264. data/ext/ca_iter_block.c +0 -257
  265. data/ext/ca_iter_dimension.c +0 -299
  266. data/ext/ca_iter_window.c +0 -214
  267. data/ext/ca_obj_mapping.c +0 -644
  268. data/ext/ca_obj_unbound_repeat.c +0 -529
  269. data/ext/carray_iterator.c +0 -641
  270. data/ext/carray_math.rb +0 -850
  271. data/ext/carray_numeric.c +0 -259
  272. data/ext/carray_sort_addr.c +0 -254
  273. data/ext/carray_stat.c +0 -2100
  274. data/ext/carray_stat_proc.rb +0 -1999
  275. data/ext/mkmath.rb +0 -741
  276. data/ext/ruby_ccomplex.c +0 -509
  277. data/ext/ruby_float_func.c +0 -86
  278. data/lib/carray/array.rb +0 -8
  279. data/lib/carray/autoload/autoload_base.rb +0 -19
  280. data/lib/carray/autoload/autoload_gem_cairo.rb +0 -9
  281. data/lib/carray/autoload/autoload_gem_ffi.rb +0 -9
  282. data/lib/carray/autoload/autoload_gem_gnuplot.rb +0 -2
  283. data/lib/carray/autoload/autoload_gem_io_csv.rb +0 -14
  284. data/lib/carray/autoload/autoload_gem_io_pg.rb +0 -6
  285. data/lib/carray/autoload/autoload_gem_io_sqlite3.rb +0 -12
  286. data/lib/carray/autoload/autoload_gem_narray.rb +0 -10
  287. data/lib/carray/autoload/autoload_gem_numo_narray.rb +0 -15
  288. data/lib/carray/autoload/autoload_gem_opencv.rb +0 -16
  289. data/lib/carray/autoload/autoload_gem_random.rb +0 -8
  290. data/lib/carray/autoload/autoload_gem_rmagick.rb +0 -23
  291. data/lib/carray/autoload/autoload_gem_zimg.rb +0 -3
  292. data/lib/carray/autoload/autoload_io_imagemagick.rb +0 -6
  293. data/lib/carray/autoload/autoload_math_histogram.rb +0 -5
  294. data/lib/carray/autoload/autoload_math_recurrence.rb +0 -6
  295. data/lib/carray/autoload/autoload_object_iterator.rb +0 -1
  296. data/lib/carray/autoload/autoload_object_link.rb +0 -1
  297. data/lib/carray/autoload/autoload_object_pack.rb +0 -2
  298. data/lib/carray/autoload.rb +0 -141
  299. data/lib/carray/basic.rb +0 -191
  300. data/lib/carray/broadcast.rb +0 -101
  301. data/lib/carray/compose.rb +0 -315
  302. data/lib/carray/convert.rb +0 -115
  303. data/lib/carray/info.rb +0 -110
  304. data/lib/carray/io/imagemagick.rb +0 -235
  305. data/lib/carray/mask.rb +0 -102
  306. data/lib/carray/math/histogram.rb +0 -177
  307. data/lib/carray/math/recurrence.rb +0 -93
  308. data/lib/carray/object/ca_obj_iterator.rb +0 -50
  309. data/lib/carray/object/ca_obj_link.rb +0 -50
  310. data/lib/carray/object/ca_obj_pack.rb +0 -99
  311. data/lib/carray/obsolete.rb +0 -256
  312. data/lib/carray/ordering.rb +0 -181
  313. data/lib/carray/testing.rb +0 -51
  314. data/lib/carray/transform.rb +0 -109
  315. data/mailmap +0 -1
  316. data/misc/Methods.ja.md +0 -182
  317. data/misc/NOTE +0 -51
  318. data/spec/Classes/CABitfield_spec.rb +0 -58
  319. data/spec/Classes/CABlockIterator_spec.rb +0 -114
  320. data/spec/Classes/CABlock_spec.rb +0 -205
  321. data/spec/Classes/CAField_spec.rb +0 -39
  322. data/spec/Classes/CAGrid_spec.rb +0 -75
  323. data/spec/Classes/CAMap_spec.rb +0 -0
  324. data/spec/Classes/CAMapping_spec.rb +0 -105
  325. data/spec/Classes/CAObject_attribute_spec.rb +0 -33
  326. data/spec/Classes/CAObject_spec.rb +0 -33
  327. data/spec/Classes/CARefer_spec.rb +0 -93
  328. data/spec/Classes/CARepeat_spec.rb +0 -65
  329. data/spec/Classes/CASelect_spec.rb +0 -22
  330. data/spec/Classes/CAShift_spec.rb +0 -16
  331. data/spec/Classes/CAStruct_spec.rb +0 -71
  332. data/spec/Classes/CATranspose_spec.rb +0 -60
  333. data/spec/Classes/CAUnboudRepeat_spec.rb +0 -102
  334. data/spec/Classes/CAWindow_spec.rb +0 -54
  335. data/spec/Classes/CAWrap_spec.rb +0 -8
  336. data/spec/Classes/CArray_spec.rb +0 -184
  337. data/spec/Classes/CScalar_spec.rb +0 -55
  338. data/spec/Features/feature_130_spec.rb +0 -19
  339. data/spec/Features/feature_attributes_spec.rb +0 -280
  340. data/spec/Features/feature_boolean_spec.rb +0 -98
  341. data/spec/Features/feature_broadcast.rb +0 -116
  342. data/spec/Features/feature_cast_function.rb +0 -19
  343. data/spec/Features/feature_cast_spec.rb +0 -33
  344. data/spec/Features/feature_class_spec.rb +0 -84
  345. data/spec/Features/feature_complex_spec.rb +0 -42
  346. data/spec/Features/feature_composite_spec.rb +0 -124
  347. data/spec/Features/feature_convert_spec.rb +0 -46
  348. data/spec/Features/feature_copy_spec.rb +0 -123
  349. data/spec/Features/feature_creation_spec.rb +0 -84
  350. data/spec/Features/feature_element_spec.rb +0 -144
  351. data/spec/Features/feature_extream_spec.rb +0 -54
  352. data/spec/Features/feature_generate_spec.rb +0 -74
  353. data/spec/Features/feature_index_spec.rb +0 -69
  354. data/spec/Features/feature_mask_spec.rb +0 -580
  355. data/spec/Features/feature_math_spec.rb +0 -97
  356. data/spec/Features/feature_order_spec.rb +0 -146
  357. data/spec/Features/feature_ref_store_spec.rb +0 -209
  358. data/spec/Features/feature_serialization_spec.rb +0 -125
  359. data/spec/Features/feature_stat_spec.rb +0 -397
  360. data/spec/Features/feature_virtual_spec.rb +0 -48
  361. data/spec/Features/method_eq_spec.rb +0 -81
  362. data/spec/Features/method_is_nan_spec.rb +0 -12
  363. data/spec/Features/method_map_spec.rb +0 -54
  364. data/spec/Features/method_max_with.rb +0 -20
  365. data/spec/Features/method_min_with.rb +0 -19
  366. data/spec/Features/method_ne_spec.rb +0 -18
  367. data/spec/Features/method_project_spec.rb +0 -188
  368. data/spec/Features/method_ref_spec.rb +0 -27
  369. data/spec/Features/method_round_spec.rb +0 -11
  370. data/spec/Features/method_s_linspace_spec.rb +0 -48
  371. data/spec/Features/method_s_span_spec.rb +0 -14
  372. data/spec/Features/method_seq_spec.rb +0 -47
  373. data/spec/Features/method_sort_with.rb +0 -43
  374. data/spec/Features/method_sorted_with.rb +0 -29
  375. data/spec/Features/method_span_spec.rb +0 -42
  376. data/spec/Features/method_wrap_readonly_spec.rb +0 -43
  377. data/spec/UnitTest/test_CAVirtual.rb +0 -214
  378. data/spec/spec_all.rb +0 -10
  379. data/utils/ca_ase.rb +0 -21
  380. data/utils/ca_methods.rb +0 -15
  381. data/utils/cast_checker.rb +0 -30
  382. data/utils/convert_test.rb +0 -73
  383. data/utils/extract_yard.rb +0 -22
  384. data/utils/guess_shape.rb +0 -76
  385. data/utils/monkey_patch_methods.rb +0 -62
  386. data/utils/remove_resource_fork.sh +0 -5
data/ext/carray_access.c CHANGED
@@ -1,22 +1,25 @@
1
1
  /* ---------------------------------------------------------------------------
2
2
 
3
- carray_access.c
4
-
5
- This file is part of Ruby/CArray extension library.
6
-
7
- Copyright (C) 2005-2025 Hiroki Motoyoshi
3
+ The indexer: CArray#[] and #[]= plus their helpers (fill, addr2index,
4
+ index2addr, normalize_index, scan_index). Classifies an index spec via
5
+ rb_ca_scan_index (forwarded to ext/carray_index_classifier.c) and
6
+ dispatches each CA_REG_* case to the matching view constructor
7
+ (CABlock / CASelect / CAGrid / CARemap / CASlabIterator / ...).
8
8
 
9
9
  ---------------------------------------------------------------------------- */
10
10
 
11
11
  #include "carray.h"
12
+ #include "carray_internal.h" /* per-obj_type view constructors */
13
+ #include "ca_obj_face.h"
14
+ #include "carray_index_classifier.h" /* rb_ca_scan_index_v2 forward decl */
12
15
 
13
16
  static ID id_begin, id_end, id_excl_end;
14
17
  #define RANGE_BEG(r) (rb_funcall(r, id_begin, 0))
15
18
  #define RANGE_END(r) (rb_funcall(r, id_end, 0))
16
19
  #define RANGE_EXCL(r) (rb_funcall(r, id_excl_end, 0))
17
20
 
18
- static ID id_ca, id_to_ca;
19
- static VALUE sym_star, sym_perc;
21
+ static ID id_to_ca;
22
+ static VALUE sym_star, sym_perc, sym_under, sym_gt, sym_tilde;
20
23
  static VALUE S_CAInfo;
21
24
 
22
25
  VALUE
@@ -54,10 +57,10 @@ rb_ca_store_index (VALUE self, ca_size_t *idx, VALUE rval)
54
57
  ca_store_index(ca, idx, v);
55
58
  }
56
59
  else {
57
- char *v = malloc_with_check(ca->bytes);
60
+ char *v = xmalloc(ca->bytes);
58
61
  rb_ca_obj2ptr(self, rval, v);
59
62
  ca_store_index(ca, idx, v);
60
- free(v);
63
+ xfree(v);
61
64
  }
62
65
  }
63
66
 
@@ -82,10 +85,10 @@ rb_ca_fetch_index (VALUE self, ca_size_t *idx)
82
85
  out = rb_ca_ptr2obj(self, v);
83
86
  }
84
87
  else {
85
- char *v = malloc_with_check(ca->bytes);
88
+ char *v = xmalloc(ca->bytes);
86
89
  ca_fetch_index(ca, idx, v);
87
90
  out = rb_ca_ptr2obj(self, v);
88
- free(v);
91
+ xfree(v);
89
92
  }
90
93
 
91
94
  /* check if the element is masked */
@@ -98,6 +101,19 @@ rb_ca_fetch_index (VALUE self, ca_size_t *idx)
98
101
  }
99
102
  }
100
103
 
104
+ /* Face scalar decode (invoked if the subclass defines storage_to_scalar(raw)) */
105
+ CA_FACE_STORAGE_TO_SCALAR_IF_FACE(out, self, ca);
106
+
107
+ /* Boolean scalar access yields true/false, not Integer 0/1. Bulk paths
108
+ (to_a / cast / serialize via rb_ca_ptr2obj) keep 0/1. Gate on the raw
109
+ INT2FIX(0/1) so a boolean-storage Face that decoded to its own object is
110
+ left untouched; note INT2FIX(0) is truthy in Ruby, so map by value, not
111
+ by RTEST. */
112
+ if ( ca->data_type == CA_BOOLEAN &&
113
+ ( out == INT2FIX(0) || out == INT2FIX(1) ) ) {
114
+ out = ( out == INT2FIX(1) ) ? Qtrue : Qfalse;
115
+ }
116
+
101
117
  return out;
102
118
  }
103
119
 
@@ -134,10 +150,10 @@ rb_ca_store_addr (VALUE self, ca_size_t addr, VALUE rval)
134
150
  ca_store_addr(ca, addr, v);
135
151
  }
136
152
  else {
137
- char *v = malloc_with_check(ca->bytes);
153
+ char *v = xmalloc(ca->bytes);
138
154
  rb_ca_obj2ptr(self, rval, v);
139
155
  ca_store_addr(ca, addr, v);
140
- free(v);
156
+ xfree(v);
141
157
  }
142
158
  }
143
159
 
@@ -162,10 +178,10 @@ rb_ca_fetch_addr (VALUE self, ca_size_t addr)
162
178
  out = rb_ca_ptr2obj(self, v);
163
179
  }
164
180
  else {
165
- char *v = malloc_with_check(ca->bytes);
181
+ char *v = xmalloc(ca->bytes);
166
182
  ca_fetch_addr(ca, addr, v);
167
183
  out = rb_ca_ptr2obj(self, v);
168
- free(v);
184
+ xfree(v);
169
185
  }
170
186
 
171
187
  /* check if the element is masked */
@@ -178,18 +194,18 @@ rb_ca_fetch_addr (VALUE self, ca_size_t addr)
178
194
  }
179
195
  }
180
196
 
197
+ /* Face scalar decode */
198
+ CA_FACE_STORAGE_TO_SCALAR_IF_FACE(out, self, ca);
199
+
200
+ /* Boolean scalar access yields true/false (see rb_ca_fetch_index). */
201
+ if ( ca->data_type == CA_BOOLEAN &&
202
+ ( out == INT2FIX(0) || out == INT2FIX(1) ) ) {
203
+ out = ( out == INT2FIX(1) ) ? Qtrue : Qfalse;
204
+ }
205
+
181
206
  return out;
182
207
  }
183
208
 
184
- /* yard:
185
- class CArray
186
- def fill
187
- end
188
- def fill_copy
189
- end
190
- end
191
- */
192
-
193
209
  VALUE
194
210
  rb_ca_fill (VALUE self, VALUE rval)
195
211
  {
@@ -199,7 +215,7 @@ rb_ca_fill (VALUE self, VALUE rval)
199
215
  TypedData_Get_Struct(self, CArray, &carray_data_type, ca);
200
216
 
201
217
  if ( ca_is_empty(ca) ) {
202
- return rval;
218
+ return self; /* empty = no-op fill; return self for chaining */
203
219
  }
204
220
 
205
221
  if ( rval == CA_UNDEF ) {
@@ -211,14 +227,14 @@ rb_ca_fill (VALUE self, VALUE rval)
211
227
  ca_fill(ca->mask, &one);
212
228
  }
213
229
  else {
214
- char *fval = malloc_with_check(ca->bytes);
230
+ char *fval = xmalloc(ca->bytes);
215
231
  boolean8_t zero = 0;
216
232
  rb_ca_obj2ptr(self, rval, fval);
217
233
  if ( ca_has_mask(ca) ) {
218
234
  ca_fill(ca->mask, &zero);
219
235
  }
220
236
  ca_fill(ca, fval);
221
- free(fval);
237
+ xfree(fval);
222
238
  }
223
239
 
224
240
  return self;
@@ -266,11 +282,6 @@ ary_guess_shape (VALUE ary, int level, int *max_level, ca_size_t *dim)
266
282
  }
267
283
  }
268
284
 
269
- /* yard:
270
- def CArray.guess_array_shape (arg)
271
- end
272
- */
273
-
274
285
  static VALUE
275
286
  rb_ca_s_guess_array_shape (VALUE self, VALUE ary)
276
287
  {
@@ -340,7 +351,7 @@ rb_ary_flatten_for_elements (VALUE ary, ca_size_t elements, void *ap)
340
351
  rb_raise(rb_eRuntimeError, "invalid shape array for conversion to carray");
341
352
  }
342
353
  else {
343
- VALUE out = rb_ary_new2(0);
354
+ volatile VALUE out = rb_ary_new2(0);
344
355
  int len = 0;
345
356
  ary_flatten_upto_level(ary, max_level, 0, out, &len);
346
357
  if ( len != elements ) {
@@ -366,7 +377,7 @@ rb_ary_flatten_for_elements (VALUE ary, ca_size_t elements, void *ap)
366
377
  }
367
378
 
368
379
  if ( same_shape ) {
369
- VALUE out = rb_ary_new2(0);
380
+ volatile VALUE out = rb_ary_new2(0);
370
381
  int len = 0;
371
382
  ary_flatten_upto_level(ary, ca->ndim-1, 0, out, &len);
372
383
 
@@ -388,7 +399,7 @@ rb_ary_flatten_for_elements (VALUE ary, ca_size_t elements, void *ap)
388
399
  }
389
400
 
390
401
  if ( level >= 0 ) {
391
- VALUE out = rb_ary_new2(0);
402
+ volatile VALUE out = rb_ary_new2(0);
392
403
  int len = 0;
393
404
  ary_flatten_upto_level(ary, level, 0, out, &len);
394
405
  if ( len != elements ) {
@@ -411,7 +422,7 @@ rb_ary_flatten_for_elements (VALUE ary, ca_size_t elements, void *ap)
411
422
  } \
412
423
  if ( index < 0 || index >= (dim) ) { \
413
424
  rb_raise(rb_eIndexError, \
414
- "index out of range at %i-dim ( %lld <=> 0..%lld )", \
425
+ "index out of range at %i-dim ( %" PRId64 " <=> 0..%" PRId64 " )", \
415
426
  i, (ca_size_t) index, (ca_size_t) (dim-1)); \
416
427
  }
417
428
 
@@ -419,522 +430,8 @@ void
419
430
  rb_ca_scan_index (int ca_ndim, ca_size_t *ca_dim, ca_size_t ca_elements,
420
431
  long argc, VALUE *argv, CAIndexInfo *info)
421
432
  {
422
- int32_t i;
423
-
424
- info->ndim = 0;
425
- info->select = NULL;
426
-
427
- if ( argc == 0 ) { /* ca[] -> CA_REG_ALL */
428
- info->type = CA_REG_ALL;
429
- return;
430
- }
431
-
432
- /* ca[:method, ...] -> CA_REG_METHOD_CALL
433
- ca[:i, ...] -> CA_REG_ITERATOR */
434
- if ( TYPE(argv[0]) == T_SYMBOL ) {
435
- const char *symstr = rb_id2name(SYM2ID(argv[0]));
436
- if ( strlen(symstr) > 1 ) { /* ca[:method, ...] -> CA_REG_METHOD_CALL */
437
- info->type = CA_REG_METHOD_CALL;
438
- info->symbol = argv[0];
439
- return;
440
- }
441
- }
442
-
443
- if ( argc == 1 ) {
444
-
445
- volatile VALUE arg = argv[0];
446
-
447
- if ( arg == Qfalse ) { /* ca[false] -> CA_REG_ALL */
448
- info->type = CA_REG_ALL;
449
- return;
450
- }
451
-
452
- if ( rb_obj_is_carray(arg) ) {
453
- CArray *cs;
454
- TypedData_Get_Struct(arg, CArray, &carray_data_type, cs);
455
- if ( ca_is_integer_type(cs) ) {
456
- #if 0
457
- if ( ca_ndim == 1 && cs->ndim == 1 ) { /* ca[g] -> CA_REG_GRID (1d) */
458
- info->type = CA_REG_GRID;
459
- }
460
- else { /* ca[m] -> CA_REG_MAPPER (2d...) */
461
- info->type = CA_REG_MAPPING;
462
- }
463
- #endif
464
- info->type = CA_REG_MAPPING;
465
- return;
466
- }
467
- else if ( ca_is_boolean_type(cs) ) {
468
- /* ca[selector] -> CA_REG_SELECT */
469
- if ( ca_elements != cs->elements ) {
470
- rb_raise(rb_eRuntimeError,
471
- "mismatch of # of elements ( %lld <=> %lld ) in reference by selection",
472
- (ca_size_t) cs->elements, (ca_size_t) ca_elements);
473
- }
474
- info->type = CA_REG_SELECT;
475
- info->select = cs;
476
- return;
477
- }
478
- else {
479
- rb_raise(rb_eIndexError,
480
- "data_type %s is invalid for reference by selection/mapping" \
481
- "(should be boolean or integer)",
482
- ca_type_name[cs->data_type]);
483
- }
484
- }
485
-
486
- if ( TYPE(arg) == T_STRING ) {
487
- if ( StringValuePtr(arg)[0] == '@' ) { /* ca["@name"] -> CA_REG_ATTRIBUTE */
488
- info->type = CA_REG_ATTRIBUTE;
489
- info->symbol = rb_str_new2(StringValuePtr(arg)+1);
490
- }
491
- else { /* ca["field"] -> CA_REG_MEMBER */
492
- info->type = CA_REG_MEMBER;
493
- info->symbol = ID2SYM(rb_intern(StringValuePtr(arg)));
494
- }
495
- return;
496
- }
497
-
498
- if ( arg == sym_star ) { /* ca[:*] -> CA_REG_UNBOUND_REPEAT */
499
- info->type = CA_REG_UNBOUND_REPEAT;
500
- return;
501
- }
502
-
503
- if ( ca_ndim > 1 ) { /* ca.ndim > 1 */
504
- if ( rb_obj_is_kind_of(arg, rb_cInteger) ) { /* ca[n] -> CA_REG_ADDRESS */
505
- ca_size_t addr;
506
- info->type = CA_REG_ADDRESS;
507
- info->ndim = 1;
508
- addr = NUM2SIZE(arg);
509
- if ( info->range_check ) {
510
- CA_CHECK_INDEX(addr, ca_elements);
511
- }
512
- info->index[0].scalar = addr;
513
- return;
514
- }
515
- else if ( arg == Qnil ) {
516
- info->type = CA_REG_FLATTEN;
517
- return;
518
- }
519
- else { /* ca[i..j] -> CA_REG_ADDRESS_COMPLEX */
520
- info->type = CA_REG_ADDRESS_COMPLEX;
521
- return;
522
- }
523
- }
524
- }
525
-
526
- /* continue to next section */
527
-
528
- if ( argc >= 1 ) {
529
- int8_t is_point = 0, is_all = 0, is_iterator = 0, is_repeat=0, is_grid=0;
530
- int8_t has_rubber = 0;
531
- int32_t *index_type = info->index_type;
532
- CAIndex *index = info->index;
533
-
534
- for (i=0; i<argc; i++) {
535
- if ( argv[i] == sym_perc ) {
536
- is_repeat = 1;
537
- goto loop_exit; /* ca[--,:%,--] -> CA_REG_REPEAT */
538
- }
539
-
540
- if ( argv[i] == sym_star ) {
541
- is_repeat = 2;
542
- goto loop_exit; /* ca[--,:*,--] -> CA_REG_UNBOUND_REPEAT */
543
- }
544
-
545
- if ( argv[i] == Qfalse ) { /* ca[--,false,--] (rubber dimension) */
546
- has_rubber = 1;
547
- if ( argc > ca_ndim + 1 ) {
548
- rb_raise(rb_eIndexError,
549
- "index specification exceeds the ndim of carray (%i)",
550
- ca_ndim);
551
- }
552
- }
553
- }
554
-
555
- if ( ! has_rubber && ca_ndim != argc ) {
556
- rb_raise(rb_eIndexError,
557
- "number of indices exceeds the ndim of carray (%i > %i)",
558
- (int) argc, ca_ndim);
559
- }
560
-
561
- info->ndim = argc;
562
-
563
- for (i=0; i<argc; i++) {
564
- volatile VALUE arg = argv[i];
565
-
566
- retry:
567
-
568
- if ( rb_obj_is_kind_of(arg, rb_cInteger) ) { /* ca[--,i,--] */
569
- ca_size_t scalar;
570
- index_type[i] = CA_IDX_SCALAR;
571
- scalar = NUM2SIZE(arg);
572
- if ( info->range_check ) {
573
- CA_CHECK_INDEX_AT(scalar, ca_dim[i], i);
574
- }
575
- index[i].scalar = scalar;
576
- }
577
- else if ( NIL_P(arg) ) { /* ca[--,nil,--] */
578
- index_type[i] = CA_IDX_ALL;
579
- }
580
- else if ( arg == Qfalse ) { /* ca[--,false,--] */
581
- int8_t rndim = ca_ndim - argc + 1;
582
- int8_t j;
583
- for (j=0; j<rndim; j++) {
584
- index_type[i+j] = CA_IDX_ALL;
585
- }
586
- i += rndim-1;
587
- argv -= rndim-1;
588
- argc = ca_ndim;
589
- info->ndim = ca_ndim;
590
- }
591
- else if ( rb_obj_is_kind_of(arg, rb_cRange) ) { /* ca[--,i..j,--] */
592
- ca_size_t start, last, excl, count, step;
593
- volatile VALUE iv_beg, iv_end, iv_excl;
594
- iv_beg = RANGE_BEG(arg);
595
- iv_end = RANGE_END(arg);
596
- iv_excl = RANGE_EXCL(arg);
597
- index_type[i] = CA_IDX_BLOCK; /* convert to block */
598
- if ( NIL_P(iv_beg) ) {
599
- start = 0;
600
- }
601
- else {
602
- start = NUM2SIZE(iv_beg);
603
- }
604
- if ( NIL_P(iv_end) ) {
605
- last = -1;
606
- }
607
- else {
608
- last = NUM2SIZE(iv_end);
609
- }
610
- excl = RTEST(iv_excl);
611
-
612
- if ( info->range_check ) {
613
- CA_CHECK_INDEX_AT(start, ca_dim[i], i);
614
- }
615
-
616
- if ( last < 0 ) { /* don't use CA_CHECK_INDEX for excl */
617
- last += ca_dim[i];
618
- }
619
- if ( excl && ( start == last ) ) {
620
- index[i].block.start = start;
621
- index[i].block.count = 0;
622
- index[i].block.step = 1;
623
- }
624
- else {
625
- if ( excl ) {
626
- last += ( (last>=start) ? -1 : 1 );
627
- }
628
- if ( info->range_check ) {
629
- if ( last < 0 || last >= ca_dim[i] ) {
630
- rb_raise(rb_eIndexError,
631
- "index %lld is out of range (0..%lld) at %i-dim",
632
- (ca_size_t) last, (ca_size_t) (ca_dim[i]-1), i);
633
- }
634
- }
635
- index[i].block.start = start;
636
- index[i].block.count = count = llabs(last - start) + 1;
637
- index[i].block.step = step = ( last >= start ) ? 1 : -1;
638
- }
639
- }
640
- #ifdef HAVE_RB_ARITHMETIC_SEQUENCE_EXTRACT
641
- else if ( rb_obj_is_kind_of(arg, rb_cArithSeq) ) { /* ca[--,ArithSeq,--]*/
642
- ca_size_t start, last, excl, count, step, bound;
643
- volatile VALUE iv_beg, iv_end, iv_excl;
644
- rb_arithmetic_sequence_components_t x;
645
- rb_arithmetic_sequence_extract(arg, &x);
646
- iv_beg = x.begin;
647
- iv_end = x.end;
648
- iv_excl = x.exclude_end;
649
- step = NUM2SIZE(x.step);
650
- if ( NIL_P(iv_beg) ) {
651
- start = 0;
652
- }
653
- else {
654
- start = NUM2SIZE(iv_beg);
655
- }
656
- if ( NIL_P(iv_end) ) {
657
- last = -1;
658
- }
659
- else {
660
- last = NUM2SIZE(iv_end);
661
- }
662
- excl = RTEST(iv_excl);
663
- if ( step == 0 ) {
664
- rb_raise(rb_eRuntimeError,
665
- "step in index equals to 0 in block reference");
666
- }
667
- index_type[i] = CA_IDX_BLOCK;
668
- CA_CHECK_INDEX_AT(start, ca_dim[i], i);
669
- if ( last < 0 ) {
670
- last += ca_dim[i];
671
- }
672
- if ( excl && ( start == last ) ) {
673
- index[i].block.start = start;
674
- index[i].block.count = 0;
675
- index[i].block.step = 1;
676
- }
677
- else {
678
- if ( excl ) {
679
- last += ( (last>=start) ? -1 : 1 );
680
- }
681
- if ( last < 0 || last >= ca_dim[i] ) {
682
- rb_raise(rb_eIndexError,
683
- "index %lld is out of range (0..%lld) at %i-dim",
684
- (ca_size_t) last, (ca_size_t) (ca_dim[i]-1), i);
685
- }
686
- if ( (last - start) * step < 0 ) {
687
- count = 1;
688
- }
689
- else {
690
- count = llabs(last - start)/llabs(step) + 1;
691
- }
692
- bound = start + (count - 1)*step;
693
- CA_CHECK_INDEX_AT(bound, ca_dim[i], i);
694
- index[i].block.start = start;
695
- index[i].block.count = count;
696
- index[i].block.step = step;
697
- }
698
- }
699
- #endif
700
- else if ( TYPE(arg) == T_ARRAY ) { /* ca[--,[array],--] */
701
- if ( RARRAY_LEN(arg) == 1 ) {
702
- VALUE arg0 = rb_ary_entry(arg, 0);
703
- if ( NIL_P(arg0) ) { /* ca[--,[nil],--]*/
704
- index_type[i] = CA_IDX_ALL;
705
- }
706
- else if ( rb_obj_is_kind_of(arg0, rb_cRange) ) {
707
- /* ca[--,[i..j],--] */
708
- arg = arg0;
709
- goto retry;
710
- }
711
- else { /* ca[--,[i],--]*/
712
- ca_size_t start;
713
- start = NUM2SIZE(arg0);
714
- CA_CHECK_INDEX_AT(start, ca_dim[i], i);
715
- index_type[i] = CA_IDX_BLOCK;
716
- index[i].block.start = start;
717
- index[i].block.count = 1;
718
- index[i].block.step = 1;
719
- }
720
- }
721
- else if ( RARRAY_LEN(arg) == 2 ) {
722
- VALUE arg0 = rb_ary_entry(arg, 0);
723
- VALUE arg1 = rb_ary_entry(arg, 1);
724
- if ( NIL_P(arg0) ) { /* ca[--,[nil,k],--]*/
725
- ca_size_t start, last, count, step, bound;
726
- step = NUM2SIZE(arg1);
727
- if ( step == 0 ) {
728
- rb_raise(rb_eRuntimeError,
729
- "step in index equals to 0 in block reference");
730
- }
731
- start = 0;
732
- last = ca_dim[i]-1;
733
- if ( step < 0 ) {
734
- count = 1;
735
- }
736
- else {
737
- count = last/step + 1;
738
- }
739
- bound = start + (count - 1)*step;
740
- CA_CHECK_INDEX_AT(bound, ca_dim[i], i);
741
- index_type[i] = CA_IDX_BLOCK;
742
- index[i].block.start = start;
743
- index[i].block.count = count;
744
- index[i].block.step = step;
745
- }
746
- else if ( rb_obj_is_kind_of(arg0, rb_cRange) ) { /* ca[--,[i..j,k],--]*/
747
- ca_size_t start, last, excl, count, step, bound;
748
- volatile VALUE iv_beg, iv_end, iv_excl;
749
- iv_beg = RANGE_BEG(arg0);
750
- iv_end = RANGE_END(arg0);
751
- iv_excl = RANGE_EXCL(arg0);
752
- if ( NIL_P(iv_beg) ) {
753
- start = 0;
754
- }
755
- else {
756
- start = NUM2SIZE(iv_beg);
757
- }
758
- if ( NIL_P(iv_end) ) {
759
- last = -1;
760
- }
761
- else {
762
- last = NUM2SIZE(iv_end);
763
- }
764
- excl = RTEST(iv_excl);
765
- step = NUM2SIZE(arg1);
766
- if ( step == 0 ) {
767
- rb_raise(rb_eRuntimeError,
768
- "step in index equals to 0 in block reference");
769
- }
770
- index_type[i] = CA_IDX_BLOCK;
771
- CA_CHECK_INDEX_AT(start, ca_dim[i], i);
772
- if ( last < 0 ) {
773
- last += ca_dim[i];
774
- }
775
- if ( excl && ( start == last ) ) {
776
- index[i].block.start = start;
777
- index[i].block.count = 0;
778
- index[i].block.step = 1;
779
- }
780
- else {
781
- if ( excl ) {
782
- last += ( (last>=start) ? -1 : 1 );
783
- }
784
- if ( last < 0 || last >= ca_dim[i] ) {
785
- rb_raise(rb_eIndexError,
786
- "index %lld is out of range (0..%lld) at %i-dim",
787
- (ca_size_t) last, (ca_size_t) (ca_dim[i]-1), i);
788
- }
789
- if ( (last - start) * step < 0 ) {
790
- count = 1;
791
- }
792
- else {
793
- count = llabs(last - start)/llabs(step) + 1;
794
- }
795
- bound = start + (count - 1)*step;
796
- CA_CHECK_INDEX_AT(bound, ca_dim[i], i);
797
- index[i].block.start = start;
798
- index[i].block.count = count;
799
- index[i].block.step = step;
800
- }
801
- }
802
- else { /* ca[--,[i,j],--]*/
803
- ca_size_t start, count, bound;
804
- start = NUM2SIZE(arg0);
805
- count = NUM2SIZE(arg1);
806
- bound = start + (count - 1);
807
- CA_CHECK_INDEX_AT(start, ca_dim[i], i);
808
- CA_CHECK_INDEX_AT(bound, ca_dim[i], i);
809
- index_type[i] = CA_IDX_BLOCK;
810
- index[i].block.start = start;
811
- index[i].block.count = count;
812
- index[i].block.step = 1;
813
- }
814
- }
815
- else if ( RARRAY_LEN(arg) == 3 ) { /* ca[--,[i,j,k],--]*/
816
- ca_size_t start, count, step, bound;
817
- start = NUM2SIZE(rb_ary_entry(arg, 0));
818
- count = NUM2SIZE(rb_ary_entry(arg, 1));
819
- step = NUM2SIZE(rb_ary_entry(arg, 2));
820
- if ( step == 0 ) {
821
- rb_raise(rb_eRuntimeError,
822
- "step in index equals to 0 in block reference");
823
- }
824
- bound = start + (count - 1)*step;
825
- CA_CHECK_INDEX_AT(start, ca_dim[i], i);
826
- CA_CHECK_INDEX_AT(bound, ca_dim[i], i);
827
- index_type[i] = CA_IDX_BLOCK;
828
- index[i].block.start = start;
829
- index[i].block.count = count;
830
- index[i].block.step = step;
831
- }
832
- else {
833
- rb_raise(rb_eIndexError,
834
- "invalid form of index range at %i-dim "\
835
- "(should be [start[,count[,step]]], [range, step])",
836
- i);
837
- }
838
- }
839
- else if ( rb_obj_is_kind_of(arg, rb_cSymbol) ) { /* ca[--,:i,--] */
840
- if ( strlen(rb_id2name(SYM2ID(arg))) != 1 ) {
841
- rb_raise(rb_eIndexError,
842
- "symbol :%s is invalid as the index for dimension iterator "\
843
- "(should be :a, :b, ... :z)",
844
- rb_id2name(SYM2ID(arg)));
845
- }
846
- index_type[i] = CA_IDX_SYMBOL;
847
- index[i].symbol.id = SYM2ID(arg);
848
- index[i].symbol.spec = Qnil;
849
- }
850
- else if ( rb_obj_is_carray(arg) ) { /* ca[--,ca,--] */
851
- CArray *ci;
852
- TypedData_Get_Struct(arg, CArray, &carray_data_type, ci);
853
- if ( ca_is_boolean_type(ci) || ca_is_integer_type(ci) ) {
854
- is_grid = 1;
855
- goto loop_exit;
856
- }
857
- else {
858
- rb_raise(rb_eIndexError,
859
- "data_type %s is invalid for reference by gridding at %i-dim "\
860
- "(should be boolean or integer)",
861
- ca_type_name[ci->data_type], i);
862
- }
863
- }
864
- else {
865
- VALUE inspect = rb_inspect(arg);
866
- rb_raise(rb_eIndexError,
867
- "object '%s' is invalid for the index for reference at %i-dim",
868
- StringValuePtr(inspect), i);
869
- }
870
- }
871
-
872
- if ( ca_ndim != info->ndim ) {
873
- rb_raise(rb_eIndexError,
874
- "number of indices does not equal to the ndim (%i != %i)",
875
- info->ndim, ca_ndim);
876
- }
877
-
878
- is_point = 1;
879
- is_all = 1;
880
- is_iterator = 0;
881
- is_grid = 0;
882
-
883
- for (i=0; i<info->ndim; i++) {
884
- switch ( info->index_type[i] ) {
885
- case CA_IDX_SCALAR:
886
- is_all = 0;
887
- continue;
888
- case CA_IDX_ALL:
889
- is_point = 0;
890
- continue;
891
- case CA_IDX_SYMBOL:
892
- is_iterator = 1;
893
- goto loop_exit;
894
- default:
895
- is_point = 0;
896
- is_all = 0;
897
- }
898
- }
899
-
900
- loop_exit:
901
-
902
- if ( is_repeat == 1 ) {
903
- info->type = CA_REG_REPEAT;
904
- }
905
- else if ( is_repeat == 2 ) {
906
- info->type = CA_REG_UNBOUND_REPEAT;
907
- }
908
- else if ( is_grid ) {
909
- info->type = CA_REG_GRID;
910
- }
911
- else if ( is_iterator ) {
912
- info->type = CA_REG_ITERATOR;
913
- }
914
- else if ( is_point ) {
915
- info->type = CA_REG_POINT;
916
- }
917
- else if ( is_all ) {
918
- info->type = CA_REG_BLOCK;
919
- }
920
- else {
921
- info->type = CA_REG_BLOCK;
922
- }
923
-
924
- if ( info->type == CA_REG_ITERATOR ) {
925
- for (i=0; i<info->ndim; i++) {
926
- if ( info->index_type[i] == CA_IDX_SCALAR ) {
927
- ca_size_t start = info->index[i].scalar;
928
- info->index_type[i] = CA_IDX_BLOCK;
929
- info->index[i].block.start = start;
930
- info->index[i].block.step = 1;
931
- info->index[i].block.count = 1;
932
- }
933
- }
934
- }
935
-
936
- return;
937
- }
433
+ /* Forwards to the v2 classifier in ext/carray_index_classifier.c. */
434
+ rb_ca_scan_index_v2(ca_ndim, ca_dim, ca_elements, argc, argv, info);
938
435
  }
939
436
 
940
437
  /* ----------------------------------------------------------------------- */
@@ -942,9 +439,7 @@ rb_ca_scan_index (int ca_ndim, ca_size_t *ca_dim, ca_size_t ca_elements,
942
439
  static VALUE
943
440
  rb_ca_ref_address (VALUE self, CAIndexInfo *info)
944
441
  {
945
- CArray *ca;
946
442
  ca_size_t addr;
947
- TypedData_Get_Struct(self, CArray, &carray_data_type, ca);
948
443
  addr = info->index[0].scalar;
949
444
  return rb_ca_fetch_addr(self, addr);
950
445
  }
@@ -952,9 +447,7 @@ rb_ca_ref_address (VALUE self, CAIndexInfo *info)
952
447
  static VALUE
953
448
  rb_ca_store_address (VALUE self, CAIndexInfo *info, volatile VALUE rval)
954
449
  {
955
- CArray *ca;
956
450
  ca_size_t addr;
957
- TypedData_Get_Struct(self, CArray, &carray_data_type, ca);
958
451
  addr = info->index[0].scalar;
959
452
  if ( rb_obj_is_cscalar(rval) ) {
960
453
  rval = rb_ca_fetch_addr(rval, 0);
@@ -999,6 +492,14 @@ rb_ca_ref_all (VALUE self, CAIndexInfo *info)
999
492
  return rb_funcall(self, rb_intern("refer"), 0);
1000
493
  }
1001
494
 
495
+ static ID
496
+ ca_id_expression_evaluator (void)
497
+ {
498
+ static ID id = 0;
499
+ if ( ! id ) id = rb_intern("@expression_evaluator");
500
+ return id;
501
+ }
502
+
1002
503
  VALUE
1003
504
  rb_ca_store_all (VALUE self, VALUE rval)
1004
505
  {
@@ -1022,36 +523,72 @@ rb_ca_store_all (VALUE self, VALUE rval)
1022
523
  CArray *cv;
1023
524
  TypedData_Get_Struct(rval, CArray, &carray_data_type, cv);
1024
525
 
1025
- if ( cv->obj_type == CA_OBJ_UNBOUND_REPEAT ) {
1026
- rval = ca_ubrep_bind_with(rval, self);
1027
- TypedData_Get_Struct(rval, CArray, &carray_data_type, cv);
526
+ /* Where something is registered to compute an expression, and this is
527
+ one, it is asked before the walk below -- it fills the destination
528
+ directly, which is the whole of the saving; making an array and
529
+ copying it over is most of the work once the expression itself is
530
+ fast. It may decline, and then the walk does it. Narrow on purpose:
531
+ the same shape and data type, so nothing here has to broadcast or
532
+ cast on the way. */
533
+ if ( ca_is_lazy_view(cv)
534
+ && ca->data_type == cv->data_type
535
+ && ca->ndim == cv->ndim
536
+ && ca->elements == cv->elements
537
+ && RTEST(rb_attr_get(rb_cCArray, ca_id_expression_evaluator())) ) {
538
+ int8_t k;
539
+ int same = 1;
540
+ for ( k = 0; k < ca->ndim; k++ ) {
541
+ if ( ca->dim[k] != cv->dim[k] ) { same = 0; break; }
542
+ }
543
+ if ( same ) {
544
+ VALUE fusion = rb_const_get(rb_cCArray, rb_intern("Fusion"));
545
+ if ( RTEST(rb_funcall(fusion, rb_intern("evaluate_into"), 2, rval, self)) ) {
546
+ return rval;
547
+ }
548
+ }
1028
549
  }
1029
550
 
1030
- if ( ca->elements != cv->elements ) {
1031
- rb_raise(rb_eRuntimeError,
1032
- "mismatch in data size (%lld <-> %lld) for storing to carray",
1033
- (ca_size_t) ca->elements, (ca_size_t) cv->elements);
1034
- }
551
+ /* The destination owns the shape; see ca_broadcast_to_destination. */
552
+ ca_broadcast_to_destination(self, &rval);
553
+ TypedData_Get_Struct(rval, CArray, &carray_data_type, cv);
1035
554
 
1036
- ca_attach(cv);
1037
- if ( ca->data_type != cv->data_type ) {
1038
- ca_allocate(ca);
1039
- ca_copy_mask_overwrite(ca, ca->elements, 1, cv);
1040
- if ( ca->mask ) {
1041
- ca_cast_block_with_mask(ca->elements, cv, cv->ptr, ca, ca->ptr,
1042
- (boolean8_t*)ca->mask->ptr);
555
+ /* Source delivery via ca_xfer_all into a local scratch instead of
556
+ ca_attach(cv). This routes through ca_*_func_xfer_all's per-region
557
+ partial-materialise path, avoiding a ca_attach(root) silent
558
+ transitive attach. When cv composes through a view root (CAFake /
559
+ CAByteSwap / CAGrid / etc.), attaching the root would materialise
560
+ the whole root (size = root->elements * root->bytes), producing a
561
+ catastrophic slowdown for size-gap cases like
562
+ big_virtual[100..200] = big_virtual[100..200].flip(0). The scratch
563
+ here is sized for cv only (cv->elements * cv->bytes), so the cost
564
+ scales with the view, not the root. (The dst-side ca_sync_data is
565
+ already an xfer_all PUT.) */
566
+ {
567
+ volatile VALUE scratch_holder;
568
+ ca_size_t cv_bytes = cv->bytes * cv->elements;
569
+ char *scratch = ALLOCV_N(char, scratch_holder, cv_bytes);
570
+ ca_xfer_all(cv, scratch, CA_XFER_GET);
571
+
572
+ if ( ca->data_type != cv->data_type ) {
573
+ ca_allocate(ca);
574
+ ca_copy_mask_overwrite(ca, ca->elements, 1, cv);
575
+ if ( ca->mask ) {
576
+ ca_cast_block_with_mask(ca->elements, cv, scratch, ca, ca->ptr,
577
+ (boolean8_t*)ca->mask->ptr);
578
+ }
579
+ else {
580
+ ca_cast_block(ca->elements, cv, scratch, ca, ca->ptr);
581
+ }
582
+ ca_sync(ca);
583
+ ca_detach(ca);
1043
584
  }
1044
585
  else {
1045
- ca_cast_block(ca->elements, cv, cv->ptr, ca, ca->ptr);
586
+ ca_copy_mask_overwrite(ca, ca->elements, 1, cv);
587
+ ca_sync_data(ca, scratch);
1046
588
  }
1047
- ca_sync(ca);
1048
- ca_detach(ca);
1049
- }
1050
- else {
1051
- ca_copy_mask_overwrite(ca, ca->elements, 1, cv);
1052
- ca_sync_data(ca, cv->ptr);
589
+
590
+ ALLOCV_END(scratch_holder);
1053
591
  }
1054
- ca_detach(cv);
1055
592
  }
1056
593
  else if ( TYPE(rval) == T_ARRAY ) {
1057
594
  volatile VALUE list =
@@ -1064,6 +601,8 @@ rb_ca_store_all (VALUE self, VALUE rval)
1064
601
  else {
1065
602
  int has_mask = 0;
1066
603
  CArray ico;
604
+ /* ca_cast_block reads ca_is_face(ca1), so flags must be 0-initialised. */
605
+ memset(&ico, 0, sizeof(CArray));
1067
606
  ico.data_type = CA_OBJECT;
1068
607
  ico.bytes = ca_sizeof[CA_OBJECT];
1069
608
  for (i=0; i<ca->elements; i++) {
@@ -1073,34 +612,56 @@ rb_ca_store_all (VALUE self, VALUE rval)
1073
612
  break;
1074
613
  }
1075
614
  }
1076
- ca_allocate(ca);
1077
- if ( has_mask ) {
1078
- boolean8_t *m;
1079
- m = (boolean8_t *)ca->mask->ptr;
1080
- for (i=0; i<ca->elements; i++) {
1081
- if ( rb_ary_entry(list,i) == CA_UNDEF ) {
1082
- *m = 1;
615
+ /* Use ca_allocate() instead of ca_attach() here because rb_ca_store_all()
616
+ overwrites ALL elements of the array from a Ruby Array. There is no need
617
+ to copy parent data (which ca_attach would do), since every element will
618
+ be replaced. Mask handling is also safe: ca_create_mask() above and
619
+ ca_copy_mask_overwrite() handle mask allocation and propagation internally,
620
+ so ca_allocate()'s simpler path (no parent data copy) is sufficient. */
621
+ ca_allocate(ca);
622
+ {
623
+ /* Face has surface != storage; the cast runs in the storage
624
+ data_type. Passing ca directly makes the cast table see FIXLEN
625
+ and report non-implemented, so pass a shadow CArray (storage
626
+ data_type, ptr aliased to ca). */
627
+ CArray shadow;
628
+ CArray *cast_target = ca;
629
+ memset(&shadow, 0, sizeof(CArray));
630
+ if ( ca_is_face(ca) ) {
631
+ CArray *root = ca;
632
+ while (root && ca_is_face(root)) root = ((CAView *) root)->parent;
633
+ if (root) {
634
+ shadow.data_type = root->data_type;
635
+ shadow.bytes = ca->bytes;
636
+ shadow.elements = ca->elements;
637
+ shadow.ptr = ca->ptr;
638
+ cast_target = &shadow;
1083
639
  }
1084
- else {
1085
- *m = 0;
640
+ }
641
+ if ( has_mask ) {
642
+ boolean8_t *m;
643
+ m = (boolean8_t *)ca->mask->ptr;
644
+ for (i=0; i<ca->elements; i++) {
645
+ if ( rb_ary_entry(list,i) == CA_UNDEF ) {
646
+ *m = 1;
647
+ }
648
+ else {
649
+ *m = 0;
650
+ }
651
+ m++;
1086
652
  }
1087
- m++;
653
+ ca_cast_block_with_mask(ca->elements, &ico, (VALUE *)RARRAY_CONST_PTR(list),
654
+ cast_target, ca->ptr,
655
+ (boolean8_t*)ca->mask->ptr);
656
+ }
657
+ else {
658
+ ca_cast_block(ca->elements, &ico, (VALUE *)RARRAY_CONST_PTR(list), cast_target, ca->ptr);
1088
659
  }
1089
- ca_cast_block_with_mask(ca->elements, &ico, RARRAY_PTR(list),
1090
- ca, ca->ptr,
1091
- (boolean8_t*)ca->mask->ptr);
1092
- }
1093
- else {
1094
- ca_cast_block(ca->elements, &ico, RARRAY_PTR(list), ca, ca->ptr);
1095
660
  }
1096
661
  ca_sync(ca);
1097
662
  ca_detach(ca);
1098
663
  }
1099
664
  }
1100
- else if ( rb_respond_to(rval, id_ca) ) {
1101
- rval = rb_funcall(rval, id_ca, 0);
1102
- goto retry;
1103
- }
1104
665
  else if ( rb_respond_to(rval, id_to_ca) ) {
1105
666
  rval = rb_funcall(rval, id_to_ca, 0);
1106
667
  goto retry;
@@ -1267,7 +828,22 @@ rb_ca_ref_block (VALUE self, CAIndexInfo *info)
1267
828
  dim, start, step, count, &offset);
1268
829
  }
1269
830
 
1270
- return rb_ca_block_new(refer, ndim, dim, start, step, count, offset);
831
+ {
832
+ volatile VALUE blk;
833
+ blk = rb_ca_block_new(refer, ndim, dim, start, step, count, offset);
834
+ /* The block inherits the Face source's FIXLEN surface; rewrite its
835
+ data_type to the storage data_type (chain bottom) so the write path
836
+ casts in the storage type. */
837
+ if ( ca_is_face(ca) ) {
838
+ CArray *blk_ca, *root = ca;
839
+ TypedData_Get_Struct(blk, CArray, &carray_data_type, blk_ca);
840
+ while (root && ca_is_face(root)) root = ((CAView *) root)->parent;
841
+ if (root && root->data_type != blk_ca->data_type) {
842
+ blk_ca->data_type = root->data_type;
843
+ }
844
+ }
845
+ return blk;
846
+ }
1271
847
  }
1272
848
 
1273
849
  static VALUE
@@ -1281,12 +857,120 @@ rb_ca_refer_new_flatten (VALUE self)
1281
857
  return rb_ca_refer_new(self, ca->data_type, 1, &dim0, ca->bytes, 0);
1282
858
  }
1283
859
 
1284
- /* yard:
1285
- class CArray
1286
- def [] (*spec)
1287
- end
1288
- end
1289
- */
860
+ /* CARemap routing: when self and mapper have identical shape AND
861
+ mapper's data_type is CA_SIZE, take the same-shape per-element gather
862
+ fast path. Defined in ca_obj_remap.c. */
863
+ extern VALUE rb_ca_remap_new (VALUE cary, VALUE rmapper);
864
+
865
+ /* a[mapper] is semantically equivalent to
866
+ a.flatten[mapper.flatten].reshape(*mapper.dim).
867
+
868
+ Two routes:
869
+ (a) Same-shape + CA_SIZE mapper -> CARemap view (fast path, avoids
870
+ building the flatten/gather/reshape chain).
871
+ (b) Otherwise -> normalize chain (a.flatten[mapper.flatten]
872
+ .reshape). A 1-D mapper is already routed to CAGrid upstream.
873
+
874
+ Both routes raise on a masked mapper. */
875
+ static VALUE
876
+ rb_ca_fancy_index_chain (VALUE self, VALUE rmapper)
877
+ {
878
+ CArray *ca, *cm;
879
+ volatile VALUE flat_self, flat_mapper, gridded, result;
880
+ VALUE grid_argv[1];
881
+ int i;
882
+
883
+ TypedData_Get_Struct(self, CArray, &carray_data_type, ca);
884
+ TypedData_Get_Struct(rmapper, CArray, &carray_data_type, cm);
885
+
886
+ if ( ca_is_any_masked(cm) ) {
887
+ rb_raise(rb_eArgError, "mapper in ca[mapper] should not be masked");
888
+ }
889
+
890
+ /* (a) same-shape fast path: ndim + per-axis dim + CA_SIZE data_type. */
891
+ if ( cm->data_type == CA_SIZE
892
+ && cm->ndim == ca->ndim
893
+ && memcmp(cm->dim, ca->dim, ca->ndim * sizeof(ca_size_t)) == 0 ) {
894
+ return rb_ca_remap_new(self, rmapper);
895
+ }
896
+
897
+ /* (b) Normalize chain fallback. */
898
+ flat_self = rb_ca_refer_new_flatten(self);
899
+ flat_mapper = (cm->ndim == 1) ? rmapper : rb_ca_flatten(rmapper);
900
+ grid_argv[0] = flat_mapper;
901
+ gridded = rb_ca_grid(1, grid_argv, flat_self);
902
+
903
+ if ( cm->ndim == 1 ) {
904
+ result = gridded;
905
+ }
906
+ else {
907
+ VALUE *dim_argv = ALLOCA_N(VALUE, cm->ndim);
908
+ for (i = 0; i < cm->ndim; i++) {
909
+ dim_argv[i] = SIZE2NUM(cm->dim[i]);
910
+ }
911
+ result = rb_ca_reshape(cm->ndim, dim_argv, gridded);
912
+ }
913
+ return result;
914
+ }
915
+
916
+ /* The CAMapping class is gone; rb_ca_mapping_new and rb_ca_mapping build
917
+ the normalize chain via rb_ca_fancy_index_chain. The public C API
918
+ signatures are preserved so external ext gems keep linking; the runtime
919
+ class of the returned VALUE is CARefer (chain outermost), not CAMapping. */
920
+
921
+ VALUE
922
+ rb_ca_mapping_new (VALUE cary, CArray *mapper)
923
+ {
924
+ CArray *m_copy = ca_copy(mapper);
925
+ volatile VALUE rmapper = ca_wrap_struct(m_copy);
926
+ return rb_ca_fancy_index_chain(cary, rmapper);
927
+ }
928
+
929
+ VALUE
930
+ rb_ca_mapping (int argc, VALUE *argv, VALUE self)
931
+ {
932
+ volatile VALUE rmapper;
933
+ rb_scan_args(argc, argv, "1", (VALUE *) &rmapper);
934
+ rb_check_carray_object(rmapper);
935
+ return rb_ca_fancy_index_chain(self, rmapper);
936
+ }
937
+
938
+ /* ------------------------------------------------------------------- */
939
+ /* newaxis (:_) sugar. `:_` inserts a size-1 axis at its position.
940
+ Strategy: strip `:_`, fetch the remaining per-axis indices into a base
941
+ view, then reshape the base inserting size-1 axes. reshape inherits
942
+ the optimal view class (CAStride for stride bases, CARefer for fancy
943
+ bases). */
944
+
945
+ static int
946
+ ca_argv_has_newaxis (int argc, VALUE *argv)
947
+ {
948
+ int i;
949
+ for (i = 0; i < argc; i++) {
950
+ if ( argv[i] == sym_under ) return 1;
951
+ }
952
+ return 0;
953
+ }
954
+
955
+ static int
956
+ ca_argv_has_slab_sigil (int argc, VALUE *argv)
957
+ {
958
+ int i;
959
+ for (i = 0; i < argc; i++) {
960
+ if ( argv[i] == sym_gt ) return 1;
961
+ }
962
+ return 0;
963
+ }
964
+
965
+ static VALUE rb_ca_fetch_newaxis (int argc, VALUE *argv, VALUE self,
966
+ CArray *ca);
967
+ static VALUE rb_ca_slab_iter_new (VALUE self, CAIndexInfo *info);
968
+ static VALUE rb_ca_fetch_slab_sigil (int argc, VALUE *argv, VALUE self);
969
+
970
+ /* axis-group apply (ext/ca_group_iter.c): type gate that routes a
971
+ CACategorical/AxisGroup index to the group path. */
972
+ int ca_argv_has_group (int argc, VALUE *argv);
973
+ VALUE rb_ca_fetch_group (int argc, VALUE *argv, VALUE self);
1290
974
 
1291
975
  static VALUE
1292
976
  rb_ca_fetch_method (int argc, VALUE *argv, VALUE self)
@@ -1299,12 +983,44 @@ rb_ca_fetch_method (int argc, VALUE *argv, VALUE self)
1299
983
 
1300
984
  TypedData_Get_Struct(self, CArray, &carray_data_type, ca);
1301
985
 
986
+ /* newaxis (:_) interception (cheap pointer scan; zero overhead beyond
987
+ the loop when no :_ is present). */
988
+ if ( ca_argv_has_newaxis(argc, argv) ) {
989
+ return rb_ca_fetch_newaxis(argc, argv, self, ca);
990
+ }
991
+
992
+ /* slab sigil (:>) interception. Pre-strip :> -> nil and Integer ->
993
+ length-1 Range so mask / fancy outer slots route through the regular
994
+ dispatch (CA_REG_SELECT / CA_REG_GRID / CA_REG_MAPPING) before being
995
+ wrapped in CASlabIterator. */
996
+ if ( ca_argv_has_slab_sigil(argc, argv) ) {
997
+ return rb_ca_fetch_slab_sigil(argc, argv, self);
998
+ }
999
+
1000
+ /* Axis-group type gate: a CACategorical/AxisGroup index routes to the
1001
+ group apply path (CAGroupIterator); plain int-array indices fall
1002
+ through to the regular selection dispatch below. The scan short-circuits
1003
+ when the surface classes have not been loaded, so a normal index pays
1004
+ nothing. */
1005
+ if ( ca_argv_has_group(argc, argv) ) {
1006
+ return rb_ca_fetch_group(argc, argv, self);
1007
+ }
1008
+
1302
1009
  info.range_check = 1;
1303
1010
  rb_ca_scan_index(ca->ndim, ca->dim, ca->elements, argc, argv, &info);
1304
1011
 
1305
1012
  switch ( info.type ) {
1306
1013
  case CA_REG_ADDRESS_COMPLEX:
1014
+ /* Re-enter against a flattened view of self. rb_ca_refer_new_flatten
1015
+ is an internal builder and hands back a bare refer, so a marker
1016
+ receiver would be dropped here and the result would never be lifted
1017
+ at the tail. Put it back on before the re-entry. (A Face survives
1018
+ on its own -- the internal builder still lifts those.) */
1307
1019
  self = rb_ca_refer_new_flatten(self);
1020
+ if ( ca_is_lazy_marker(ca) ) {
1021
+ extern VALUE rb_ca_lazy_marker_new (VALUE cary);
1022
+ self = rb_ca_lazy_marker_new(self);
1023
+ }
1308
1024
  goto retry;
1309
1025
  case CA_REG_ADDRESS:
1310
1026
  obj = rb_ca_ref_address(self, &info);
@@ -1325,16 +1041,13 @@ rb_ca_fetch_method (int argc, VALUE *argv, VALUE self)
1325
1041
  obj = rb_ca_select_new(self, argv[0]);
1326
1042
  break;
1327
1043
  case CA_REG_ITERATOR:
1328
- obj = rb_dim_iter_new(self, &info);
1044
+ obj = rb_ca_slab_iter_new(self, &info);
1329
1045
  break;
1330
1046
  case CA_REG_REPEAT:
1331
1047
  obj = rb_ca_repeat(argc, argv, self);
1332
1048
  break;
1333
- case CA_REG_UNBOUND_REPEAT:
1334
- obj = rb_funcall2(self, rb_intern("unbound_repeat"), (int) argc, argv);
1335
- break;
1336
1049
  case CA_REG_MAPPING:
1337
- obj = rb_ca_mapping(argc, argv, self);
1050
+ obj = rb_ca_fancy_index_chain(self, argv[0]);
1338
1051
  break;
1339
1052
  case CA_REG_GRID:
1340
1053
  obj = rb_ca_grid(argc, argv, self);
@@ -1348,11 +1061,14 @@ rb_ca_fetch_method (int argc, VALUE *argv, VALUE self)
1348
1061
  case CA_REG_MEMBER: {
1349
1062
  volatile VALUE data_class = rb_ca_data_class(self);
1350
1063
  if ( ! NIL_P(data_class) ) {
1351
- obj = rb_ca_field_as_member(self, info.symbol);
1352
- break;
1064
+ /* Field projection strips Face and returns a CAField on the parent
1065
+ (= a different data_type). Do not re-wrap with Face (CARecord
1066
+ wraps structs only; a field view like float64 is not a CARecord).
1067
+ Early-return to bypass the trailing ca_face_lift. */
1068
+ return rb_ca_face_field(self, info.symbol);
1353
1069
  }
1354
1070
  else {
1355
- rb_raise(rb_eIndexError,
1071
+ rb_raise(rb_eIndexError,
1356
1072
  "can't refer member of carray doesn't have data_class");
1357
1073
  }
1358
1074
  break;
@@ -1365,9 +1081,222 @@ rb_ca_fetch_method (int argc, VALUE *argv, VALUE self)
1365
1081
  default:
1366
1082
  rb_raise(rb_eIndexError, "invalid index specified");
1367
1083
  }
1084
+
1085
+ /* Wrapper lift at the read touch point: if `self` is a Face or a
1086
+ CALazyMarker, re-wrap the view result so the wrapper stays on top.
1087
+ Guarded to CArray results, which drops the scalar / CASlabIterator /
1088
+ CAGroupIterator / Hash cases the switch above can produce. The
1089
+ already-a-Face case (some builders lift their own result) is handled
1090
+ inside ca_wrapper_lift.
1091
+
1092
+ Every CA_REG_* branch converges here, so this one line covers all of
1093
+ them -- but not every index form: `a[:*, nil]` builds a CAUnboundRepeat,
1094
+ whose shape stays open until an operand binds it, and a marker copies
1095
+ shape at construction. ca_wrapper_lift refuses that one, in one place,
1096
+ because `unbound_repeat` reaches it by a second route. */
1097
+ CA_WRAPPER_LIFT(obj, self, ca);
1098
+
1368
1099
  return obj;
1369
1100
  }
1370
1101
 
1102
+ /* newaxis (:_) implementation (reshape). See the forward decl + the
1103
+ interception hook in rb_ca_fetch_method. Pre-condition: argv contains
1104
+ at least one sym_under. */
1105
+ static VALUE
1106
+ rb_ca_fetch_newaxis (int argc, VALUE *argv, VALUE self, CArray *ca)
1107
+ {
1108
+ int i;
1109
+ int nclean = 0;
1110
+ VALUE clean[CA_RANK_MAX];
1111
+ volatile VALUE base;
1112
+ ca_size_t final_dims[CA_RANK_MAX];
1113
+ int nfinal = 0;
1114
+ int base_cursor;
1115
+ CArray *cb;
1116
+ VALUE dim_argv[CA_RANK_MAX];
1117
+
1118
+ /* Build clean argv (strip :_); reject disjoint sigils + rubber dim. */
1119
+ for (i = 0; i < argc; i++) {
1120
+ VALUE a = argv[i];
1121
+ if ( a == sym_under ) continue;
1122
+ if ( a == sym_gt || a == sym_star || a == sym_perc
1123
+ || a == Qfalse || a == sym_tilde ) {
1124
+ rb_raise(rb_eIndexError,
1125
+ "newaxis (:_) cannot be combined with :>, :*, :%%, "
1126
+ "or rubber dim (false / :~)");
1127
+ }
1128
+ if ( nclean >= CA_RANK_MAX ) {
1129
+ rb_raise(rb_eIndexError, "too many indices");
1130
+ }
1131
+ clean[nclean++] = a;
1132
+ }
1133
+
1134
+ /* newaxis requires full per-axis indexing (CArray uses flat addressing
1135
+ for partial indices, which is incoherent with axis-position newaxis). */
1136
+ if ( nclean != ca->ndim ) {
1137
+ rb_raise(rb_eIndexError,
1138
+ "newaxis (:_) requires full per-axis indexing "
1139
+ "(got %d non-:_ indices for ndim %d)",
1140
+ nclean, (int) ca->ndim);
1141
+ }
1142
+
1143
+ base = rb_ca_fetch_method(nclean, clean, self);
1144
+
1145
+ /* Degenerate: all real axes scalar -> base is a scalar element, not a
1146
+ CArray. Out of scope (rare + degenerate); user indexes the element
1147
+ and reshapes explicitly. IndexError keeps it catchable + consistent
1148
+ with the other newaxis rejects. */
1149
+ if ( ! rb_obj_is_kind_of(base, rb_cCArray) ) {
1150
+ rb_raise(rb_eIndexError,
1151
+ "newaxis (:_) needs at least one non-scalar axis "
1152
+ "(all-scalar indexing yields a single element)");
1153
+ }
1154
+
1155
+ TypedData_Get_Struct(base, CArray, &carray_data_type, cb);
1156
+
1157
+ /* Reconstruct final shape: walk original argv. :_ -> size-1, scalar
1158
+ (Integer) -> dropped (skip), else -> base.dim[cursor]. */
1159
+ base_cursor = 0;
1160
+ for (i = 0; i < argc; i++) {
1161
+ VALUE a = argv[i];
1162
+ if ( a == sym_under ) {
1163
+ final_dims[nfinal++] = 1;
1164
+ }
1165
+ else if ( FIXNUM_P(a) || RB_TYPE_P(a, T_BIGNUM) ) {
1166
+ /* scalar index -> axis dropped in base; contributes no output axis */
1167
+ }
1168
+ else {
1169
+ if ( base_cursor >= cb->ndim ) {
1170
+ rb_raise(rb_eIndexError, "newaxis: base axis count mismatch");
1171
+ }
1172
+ final_dims[nfinal++] = cb->dim[base_cursor++];
1173
+ }
1174
+ }
1175
+ if ( base_cursor != cb->ndim ) {
1176
+ rb_raise(rb_eIndexError, "newaxis: base axis count mismatch");
1177
+ }
1178
+
1179
+ for (i = 0; i < nfinal; i++) {
1180
+ dim_argv[i] = SIZE2NUM(final_dims[i]);
1181
+ }
1182
+ return rb_ca_reshape(nfinal, dim_argv, base);
1183
+ }
1184
+
1185
+ /* Build a CASlabIterator from a classified ITERATOR index. The
1186
+ `:>` axes (CA_IDX_SYMBOL) become the slab axes; they are converted to
1187
+ CA_IDX_ALL so rb_ca_ref_block yields the sliced base view keeping all
1188
+ axes (scalars are already length-1 BLOCKs via the finalize_reg post-
1189
+ pass). CASlabIterator delegates to each_slab / map_slab / reduce_slab. */
1190
+ static VALUE
1191
+ rb_ca_slab_iter_new (VALUE self, CAIndexInfo *info)
1192
+ {
1193
+ volatile VALUE slab_axes = rb_ary_new();
1194
+ volatile VALUE base;
1195
+ volatile VALUE klass;
1196
+ int i;
1197
+
1198
+ for (i = 0; i < info->ndim; i++) {
1199
+ if ( info->index_type[i] == CA_IDX_SYMBOL ) {
1200
+ rb_ary_push(slab_axes, INT2NUM(i));
1201
+ info->index_type[i] = CA_IDX_ALL; /* :> axis -> full range */
1202
+ }
1203
+ }
1204
+
1205
+ base = rb_ca_ref_block(self, info);
1206
+ klass = rb_const_get(rb_cObject, rb_intern("CASlabIterator"));
1207
+ return rb_funcall(klass, rb_intern("new"), 2, base, slab_axes);
1208
+ }
1209
+
1210
+ /* Slab sigil dispatcher that supports any outer-slot index type
1211
+ (Integer / Range / nil / boolean mask / CArray / Symbol-method etc.).
1212
+ Strategy: expand rubber dim (false / :~) if present so :> positions
1213
+ align with the post-expansion ndim, replace :> with nil (full axis),
1214
+ promote Integer to length-1 Range so the base view preserves ndim,
1215
+ recurse through the regular indexer dispatch to build the base
1216
+ reference (CABlock / CASelectAxis / CAGrid / CAMapping / ...), then
1217
+ wrap in CASlabIterator with the recorded slab_axes positions. */
1218
+ static VALUE
1219
+ rb_ca_fetch_slab_sigil (int argc, VALUE *argv, VALUE self)
1220
+ {
1221
+ VALUE expanded[CA_RANK_MAX];
1222
+ VALUE clean[CA_RANK_MAX];
1223
+ volatile VALUE slab_axes = rb_ary_new();
1224
+ volatile VALUE base;
1225
+ volatile VALUE klass;
1226
+ CArray *ca;
1227
+ int i, j;
1228
+ int eargc;
1229
+ VALUE *eargv;
1230
+ int rubber_pos = -1;
1231
+
1232
+ TypedData_Get_Struct(self, CArray, &carray_data_type, ca);
1233
+
1234
+ /* Rubber-dim expansion (mirrors ca_classifier_expand_rubber_dim).
1235
+ Only the first rubber marker is honoured; later ones are left for
1236
+ the regular classifier to reject downstream. */
1237
+ for (i = 0; i < argc; i++) {
1238
+ if ( argv[i] == Qfalse || argv[i] == sym_tilde ) {
1239
+ rubber_pos = i;
1240
+ break;
1241
+ }
1242
+ }
1243
+ if ( rubber_pos < 0 ) {
1244
+ eargc = argc;
1245
+ eargv = argv;
1246
+ }
1247
+ else {
1248
+ int rndim;
1249
+ if ( argc > ca->ndim + 1 ) {
1250
+ rb_raise(rb_eIndexError,
1251
+ "index specification exceeds the ndim of carray (%i)",
1252
+ ca->ndim);
1253
+ }
1254
+ rndim = ca->ndim - argc + 1;
1255
+ if ( rndim < 0 ) rndim = 0;
1256
+ j = 0;
1257
+ for (i = 0; i < argc; i++) {
1258
+ if ( i == rubber_pos ) {
1259
+ int k;
1260
+ for (k = 0; k < rndim; k++) expanded[j++] = Qnil;
1261
+ }
1262
+ else {
1263
+ expanded[j++] = argv[i];
1264
+ }
1265
+ }
1266
+ eargc = j;
1267
+ eargv = expanded;
1268
+ }
1269
+
1270
+ if ( eargc > CA_RANK_MAX ) {
1271
+ rb_raise(rb_eIndexError, "too many indices");
1272
+ }
1273
+
1274
+ for (i = 0; i < eargc; i++) {
1275
+ VALUE a = eargv[i];
1276
+ if ( a == sym_gt ) {
1277
+ rb_ary_push(slab_axes, INT2NUM(i));
1278
+ clean[i] = Qnil;
1279
+ }
1280
+ else if ( FIXNUM_P(a) || RB_TYPE_P(a, T_BIGNUM) ) {
1281
+ /* Promote Integer to length-1 Range so the recursed view keeps
1282
+ the axis (matches the existing :> dispatcher semantics where
1283
+ scalars become length-1 BLOCKs via finalize_reg). */
1284
+ clean[i] = rb_range_new(a, a, 0);
1285
+ }
1286
+ else {
1287
+ clean[i] = a;
1288
+ }
1289
+ }
1290
+
1291
+ base = rb_ca_fetch_method(eargc, clean, self);
1292
+ if ( ! rb_obj_is_kind_of(base, rb_cCArray) ) {
1293
+ rb_raise(rb_eIndexError,
1294
+ "slab sigil (:>) requires the base view to be a CArray");
1295
+ }
1296
+ klass = rb_const_get(rb_cObject, rb_intern("CASlabIterator"));
1297
+ return rb_funcall(klass, rb_intern("new"), 2, base, slab_axes);
1298
+ }
1299
+
1371
1300
  static VALUE
1372
1301
  rb_cs_fetch_method (int argc, VALUE *argv, VALUE self)
1373
1302
  {
@@ -1403,16 +1332,13 @@ rb_cs_fetch_method (int argc, VALUE *argv, VALUE self)
1403
1332
  obj = rb_ca_select_new(self, argv[0]);
1404
1333
  break;
1405
1334
  case CA_REG_ITERATOR:
1406
- obj = rb_dim_iter_new(self, &info);
1335
+ obj = rb_ca_slab_iter_new(self, &info);
1407
1336
  break;
1408
1337
  case CA_REG_REPEAT:
1409
1338
  obj = rb_ca_repeat(argc, argv, self);
1410
1339
  break;
1411
- case CA_REG_UNBOUND_REPEAT:
1412
- obj = rb_funcall2(self, rb_intern("unbound_repeat"), (int) argc, argv);
1413
- break;
1414
1340
  case CA_REG_MAPPING:
1415
- obj = rb_ca_mapping(argc, argv, self);
1341
+ obj = rb_ca_fancy_index_chain(self, argv[0]);
1416
1342
  break;
1417
1343
  case CA_REG_GRID:
1418
1344
  obj = rb_ca_grid(argc, argv, self);
@@ -1426,7 +1352,7 @@ rb_cs_fetch_method (int argc, VALUE *argv, VALUE self)
1426
1352
  case CA_REG_MEMBER: {
1427
1353
  volatile VALUE data_class = rb_ca_data_class(self);
1428
1354
  if ( ! NIL_P(data_class) ) {
1429
- obj = rb_ca_field_as_member(self, info.symbol);
1355
+ obj = rb_ca_face_field(self, info.symbol);
1430
1356
  break;
1431
1357
  }
1432
1358
  else {
@@ -1447,12 +1373,67 @@ rb_cs_fetch_method (int argc, VALUE *argv, VALUE self)
1447
1373
  return obj;
1448
1374
  }
1449
1375
 
1450
- /* yard:
1451
- class CArray
1452
- def []= (*spec)
1453
- end
1454
- end
1455
- */
1376
+ /* Recursively convert a store rvalue's surface value objects into
1377
+ storage-domain values for a Face `self`, applied at the Ruby `[]=` entry
1378
+ before any store-side view (block / newaxis / grid / ...) is built. The
1379
+ store-side view is stripped to the storage data_type, so the Face is only
1380
+ visible here at the top of the dispatch; converting now lets every view
1381
+ path store a Face surface scalar correctly.
1382
+
1383
+ - a Face CArray rvalue is reconciled to self's storage via to_comparable
1384
+ (unit-safe: cross-group / non-exact raises), so a same-Face bulk store
1385
+ (t[0..1] = t[1..2]) becomes a storage int64 xfer. A non-Face CArray
1386
+ (e.g. datetime[0..1] = plain_int64_array) passes through unchanged --
1387
+ the documented raw-storage escape -- but it has to be raw *storage*: an
1388
+ integer-storage Face takes integer cells only. The scalar path already
1389
+ refuses a bare Float there (to_comparable does not recognise it) instead
1390
+ of letting the storage cast truncate it, and the bulk path must not be
1391
+ the looser door.
1392
+ - an Array is mapped structure-preserving (leaves converted),
1393
+ - a scalar goes through the write hook (ca_face_scalar_to_storage), which
1394
+ leaves a bare Integer / String unchanged.
1395
+
1396
+ Idempotent: an already-converted Integer passes through, so re-running on
1397
+ a `goto retry` iteration is harmless. */
1398
+ static VALUE
1399
+ ca_face_convert_store_rval (VALUE self, CArray *ca, VALUE val)
1400
+ {
1401
+ if ( rb_obj_is_carray(val) ) {
1402
+ CArray *cval;
1403
+ GetCArray(val, cval);
1404
+ /* Face RHS: reconcile to self's unit and descend to storage. Mirror of
1405
+ the scalar path (ca_face_scalar_to_storage) for a bulk CArray source.
1406
+ Guarded on self having to_comparable so a Face without a reconcile
1407
+ algebra (e.g. a fixlen-storage Face, whose same-storage store already
1408
+ takes the storage xfer branch) is left to the existing store path. */
1409
+ if ( ca_is_face(cval) && rb_respond_to(self, rb_intern("to_comparable")) ) {
1410
+ volatile VALUE reconciled = rb_funcall(self, rb_intern("to_comparable"),
1411
+ 1, val);
1412
+ return rb_funcall(reconciled, rb_intern("parent"), 0);
1413
+ }
1414
+ /* Bare storage escape, integer storage: integer cells only (see above). */
1415
+ {
1416
+ CArray *storage = ca_strip_face(ca);
1417
+ if ( storage && ca_is_integer_type(storage) && ! ca_is_integer_type(cval) ) {
1418
+ rb_raise(rb_eTypeError,
1419
+ "%s cannot store a bare %s array as raw storage "
1420
+ "(use ca.parent to write the %s storage directly)",
1421
+ rb_obj_classname(self), ca_type_name[cval->data_type],
1422
+ ca_type_name[storage->data_type]);
1423
+ }
1424
+ }
1425
+ return val;
1426
+ }
1427
+ if ( TYPE(val) == T_ARRAY ) {
1428
+ long i, n = RARRAY_LEN(val);
1429
+ volatile VALUE out = rb_ary_new2(n);
1430
+ for (i = 0; i < n; i++) {
1431
+ rb_ary_push(out, ca_face_convert_store_rval(self, ca, rb_ary_entry(val, i)));
1432
+ }
1433
+ return out;
1434
+ }
1435
+ return ca_face_scalar_to_storage(self, ca, val);
1436
+ }
1456
1437
 
1457
1438
  static VALUE
1458
1439
  rb_ca_store_method (int argc, VALUE *argv, VALUE self)
@@ -1470,6 +1451,31 @@ rb_ca_store_method (int argc, VALUE *argv, VALUE self)
1470
1451
 
1471
1452
  TypedData_Get_Struct(self, CArray, &carray_data_type, ca);
1472
1453
 
1454
+ /* Face store: bring a surface value object (Scalar / Time / DateTime) into
1455
+ the storage domain while self is still the Face. Store-side views strip
1456
+ the Face to storage, so this is the only point that sees it. */
1457
+ if ( ca_is_face(ca) ) {
1458
+ rval = ca_face_convert_store_rval(self, ca, rval);
1459
+ }
1460
+
1461
+ /* newaxis (:_) interception on store. Build the newaxis view (a
1462
+ writable reshape aliasing self), then assign rval into the whole view
1463
+ (writes propagate to self). */
1464
+ if ( ca_argv_has_newaxis(argc, argv) ) {
1465
+ volatile VALUE view = rb_ca_fetch_newaxis(argc, argv, self, ca);
1466
+ rb_ca_store_all(view, rval);
1467
+ return rval;
1468
+ }
1469
+
1470
+ /* slab sigil on store is rejected regardless of outer-slot type. Catch
1471
+ :> before scan_index so mask/fancy outer slots get the same "not
1472
+ supported" message instead of a misleading TypeError. */
1473
+ if ( ca_argv_has_slab_sigil(argc, argv) ) {
1474
+ rb_raise(rb_eIndexError,
1475
+ "assignment through a slab iterator (:>) is not supported "
1476
+ "(use a block index, e.g. ca[range, nil] = val, or each_slab)");
1477
+ }
1478
+
1473
1479
  info.range_check = 1;
1474
1480
  rb_ca_scan_index(ca->ndim, ca->dim, ca->elements, argc, argv, &info);
1475
1481
 
@@ -1502,8 +1508,11 @@ rb_ca_store_method (int argc, VALUE *argv, VALUE self)
1502
1508
  break;
1503
1509
  }
1504
1510
  case CA_REG_ITERATOR: {
1505
- obj = rb_dim_iter_new(self, &info);
1506
- obj = rb_funcall(obj, rb_intern("asign!"), 1, rval);
1511
+ /* assignment through a slab iterator (:>) is not supported.
1512
+ Use a block index (ca[range, nil] = val) or each_slab/map_slab. */
1513
+ rb_raise(rb_eIndexError,
1514
+ "assignment through a slab iterator (:>) is not supported "
1515
+ "(use a block index, e.g. ca[range, nil] = val, or each_slab)");
1507
1516
  break;
1508
1517
  }
1509
1518
  case CA_REG_REPEAT: {
@@ -1511,12 +1520,8 @@ rb_ca_store_method (int argc, VALUE *argv, VALUE self)
1511
1520
  obj = rb_ca_store_all(obj, rval);
1512
1521
  break;
1513
1522
  }
1514
- case CA_REG_UNBOUND_REPEAT:
1515
- obj = rb_funcall2(self, rb_intern("unbound_repeat"), (int) argc, argv);
1516
- obj = rb_ca_store_all(obj, rval);
1517
- break;
1518
1523
  case CA_REG_MAPPING: {
1519
- obj = rb_ca_mapping(argc, argv, self);
1524
+ obj = rb_ca_fancy_index_chain(self, argv[0]);
1520
1525
  obj = rb_ca_store_all(obj, rval);
1521
1526
  break;
1522
1527
  }
@@ -1537,7 +1542,7 @@ rb_ca_store_method (int argc, VALUE *argv, VALUE self)
1537
1542
  case CA_REG_MEMBER: {
1538
1543
  volatile VALUE data_class = rb_ca_data_class(self);
1539
1544
  if ( ! NIL_P(data_class) ) {
1540
- obj = rb_ca_field_as_member(self, info.symbol);
1545
+ obj = rb_ca_face_field(self, info.symbol);
1541
1546
  obj = rb_ca_store_all(obj, rval);
1542
1547
  }
1543
1548
  else {
@@ -1552,6 +1557,7 @@ rb_ca_store_method (int argc, VALUE *argv, VALUE self)
1552
1557
  break;
1553
1558
  }
1554
1559
  }
1560
+
1555
1561
  return obj;
1556
1562
  }
1557
1563
 
@@ -1560,7 +1566,7 @@ rb_ca_fetch (VALUE self, VALUE index)
1560
1566
  {
1561
1567
  switch ( TYPE(index) ) {
1562
1568
  case T_ARRAY:
1563
- return rb_ca_fetch_method((int) RARRAY_LEN(index), RARRAY_PTR(index), self);
1569
+ return rb_ca_fetch_method((int) RARRAY_LEN(index), (VALUE *)RARRAY_CONST_PTR(index), self);
1564
1570
  default:
1565
1571
  return rb_ca_fetch_method(1, &index, self);
1566
1572
  }
@@ -1579,7 +1585,7 @@ rb_ca_store (VALUE self, VALUE index, VALUE rval)
1579
1585
  case T_ARRAY:
1580
1586
  index = rb_obj_clone(index);
1581
1587
  rb_ary_push(index, rval);
1582
- return rb_ca_store_method((int)RARRAY_LEN(index), RARRAY_PTR(index), self);
1588
+ return rb_ca_store_method((int)RARRAY_LEN(index), (VALUE *)RARRAY_CONST_PTR(index), self);
1583
1589
  default: {
1584
1590
  VALUE rindex[2] = { index, rval };
1585
1591
  return rb_ca_store_method(2, rindex, self);
@@ -1590,16 +1596,11 @@ rb_ca_store (VALUE self, VALUE index, VALUE rval)
1590
1596
  VALUE
1591
1597
  rb_ca_store2 (VALUE self, int n, VALUE *rindex, VALUE rval)
1592
1598
  {
1593
- VALUE index = rb_ary_new4(n, rindex);
1599
+ volatile VALUE index = rb_ary_new4(n, rindex);
1594
1600
  rb_ary_push(index, rval);
1595
- return rb_ca_store_method((int)RARRAY_LEN(index), RARRAY_PTR(index), self);
1601
+ return rb_ca_store_method((int)RARRAY_LEN(index), (VALUE *)RARRAY_CONST_PTR(index), self);
1596
1602
  }
1597
1603
 
1598
- /* yard:
1599
- def CArray.scan_index(dim, idx)
1600
- end
1601
- */
1602
-
1603
1604
  static VALUE
1604
1605
  rb_ca_s_scan_index (VALUE self, VALUE rdim, VALUE ridx)
1605
1606
  {
@@ -1625,7 +1626,7 @@ rb_ca_s_scan_index (VALUE self, VALUE rdim, VALUE ridx)
1625
1626
 
1626
1627
  info.range_check = 1;
1627
1628
  rb_ca_scan_index(ndim, dim, elements,
1628
- RARRAY_LEN(ridx), RARRAY_PTR(ridx), &info);
1629
+ RARRAY_LEN(ridx), (VALUE *)RARRAY_CONST_PTR(ridx), &info);
1629
1630
 
1630
1631
  rtype = INT2NUM(info.type);
1631
1632
  rindex = rb_ary_new2(info.ndim);
@@ -1693,7 +1694,6 @@ rb_ca_s_scan_index (VALUE self, VALUE rdim, VALUE ridx)
1693
1694
  case CA_REG_GRID:
1694
1695
  case CA_REG_MAPPING:
1695
1696
  case CA_REG_METHOD_CALL:
1696
- case CA_REG_UNBOUND_REPEAT:
1697
1697
  case CA_REG_MEMBER:
1698
1698
  case CA_REG_ATTRIBUTE:
1699
1699
  break;
@@ -1704,13 +1704,6 @@ rb_ca_s_scan_index (VALUE self, VALUE rdim, VALUE ridx)
1704
1704
  return rb_struct_new(S_CAInfo, rtype, rindex);
1705
1705
  }
1706
1706
 
1707
- /* yard:
1708
- class CArray
1709
- def normalize_index (idx)
1710
- end
1711
- end
1712
- */
1713
-
1714
1707
  static VALUE
1715
1708
  rb_ca_normalize_index (VALUE self, VALUE ridx)
1716
1709
  {
@@ -1724,7 +1717,7 @@ rb_ca_normalize_index (VALUE self, VALUE ridx)
1724
1717
 
1725
1718
  info.range_check = 1;
1726
1719
  rb_ca_scan_index(ca->ndim, ca->dim, ca->elements,
1727
- RARRAY_LEN(ridx), RARRAY_PTR(ridx), &info);
1720
+ RARRAY_LEN(ridx), (VALUE *)RARRAY_CONST_PTR(ridx), &info);
1728
1721
 
1729
1722
  switch ( info.type ) {
1730
1723
  case CA_REG_ALL:
@@ -1777,78 +1770,153 @@ rb_ca_normalize_index (VALUE self, VALUE ridx)
1777
1770
 
1778
1771
  /* ------------------------------------------------------------------- */
1779
1772
 
1780
- /* yard:
1781
- class CArray
1782
- # converts addr to index
1783
- def addr2index (addr)
1784
- end
1785
- end
1786
- */
1773
+ /* ------------------------------------------------------------------- */
1774
+ /* addr <-> index conversion primitives.
1775
+ *
1776
+ * Two dispatch axes:
1777
+ * (a) instance form (uses self.shape) vs class form (`shape:` kwarg)
1778
+ * (b) scalar input vs CArray input (per method)
1779
+ *
1780
+ * The four Ruby entry points (rb_ca_{addr2index,index2addr},
1781
+ * rb_ca_s_{addr2index,index2addr}) all funnel into the two _do helpers
1782
+ * below, which take an already-resolved (ndim, dim[], elements) triple.
1783
+ */
1787
1784
 
1788
- VALUE
1789
- rb_ca_addr2index (VALUE self, VALUE raddr)
1785
+ static void
1786
+ parse_shape_kwarg (VALUE rshape, int *out_ndim, ca_size_t *dim,
1787
+ ca_size_t *out_elements)
1788
+ {
1789
+ int i, ndim;
1790
+ ca_size_t elements = 1;
1791
+
1792
+ Check_Type(rshape, T_ARRAY);
1793
+ ndim = (int) RARRAY_LEN(rshape);
1794
+ if ( ndim < 1 || ndim > CA_RANK_MAX ) {
1795
+ rb_raise(rb_eArgError, "invalid shape ndim %d (must be 1..%d)",
1796
+ ndim, CA_RANK_MAX);
1797
+ }
1798
+ for (i = 0; i < ndim; i++) {
1799
+ dim[i] = NUM2SIZE(rb_ary_entry(rshape, i));
1800
+ if ( dim[i] < 0 ) {
1801
+ rb_raise(rb_eArgError, "negative dim %" PRId64 " at axis %d",
1802
+ (ca_size_t) dim[i], i);
1803
+ }
1804
+ elements *= dim[i];
1805
+ }
1806
+ *out_ndim = ndim;
1807
+ *out_elements = elements;
1808
+ }
1809
+
1810
+ static VALUE
1811
+ addr2index_do (int ndim, ca_size_t *dim, ca_size_t elements, VALUE raddr)
1790
1812
  {
1791
1813
  volatile VALUE out;
1792
- CArray *ca;
1793
- ca_size_t *dim;
1794
- ca_size_t addr;
1795
1814
  int i;
1796
1815
 
1797
- TypedData_Get_Struct(self, CArray, &carray_data_type, ca);
1816
+ /* Scalar path (Integer input): returns Ruby Array of N Integers.
1817
+ * Preserves legacy shape for callers like `i, j = ca.addr2index(k)`. */
1818
+ if ( rb_obj_is_kind_of(raddr, rb_cInteger) ) {
1819
+ ca_size_t addr = NUM2SIZE(raddr);
1820
+ if ( addr < 0 || addr >= elements ) {
1821
+ rb_raise(rb_eArgError,
1822
+ "address %" PRId64 " is out of range (0..%" PRId64 ")",
1823
+ (ca_size_t) addr, (ca_size_t) (elements - 1));
1824
+ }
1825
+ out = rb_ary_new2(ndim);
1826
+ for (i = ndim - 1; i >= 0; i--) {
1827
+ rb_ary_store(out, i, SIZE2NUM(addr % dim[i]));
1828
+ addr /= dim[i];
1829
+ }
1830
+ return out;
1831
+ }
1832
+
1833
+ /* Vector path (CArray input): returns Ruby Array of N CArrays,
1834
+ * each with the same shape as the input; mask propagated. */
1835
+ {
1836
+ CArray *cin, *co[CA_RANK_MAX];
1837
+ ca_size_t *p_out[CA_RANK_MAX];
1838
+ boolean8_t *m;
1839
+ ca_size_t j, n;
1840
+ volatile VALUE objs[CA_RANK_MAX];
1841
+
1842
+ cin = ca_wrap_readonly(raddr, CA_SIZE);
1843
+ ca_attach(cin);
1844
+
1845
+ out = rb_ary_new2(ndim);
1846
+ for (i = 0; i < ndim; i++) {
1847
+ objs[i] = rb_carray_new(CA_SIZE, cin->ndim, cin->dim, 0, NULL);
1848
+ TypedData_Get_Struct(objs[i], CArray, &carray_data_type, co[i]);
1849
+ p_out[i] = (ca_size_t *) co[i]->ptr;
1850
+ rb_ary_store(out, i, objs[i]);
1851
+ }
1852
+
1853
+ m = cin->mask ? (boolean8_t *) cin->mask->ptr : NULL;
1854
+ if ( m ) {
1855
+ for (i = 0; i < ndim; i++) {
1856
+ ca_create_mask(co[i]);
1857
+ memcpy(co[i]->mask->ptr, m, cin->elements);
1858
+ }
1859
+ }
1798
1860
 
1799
- addr = NUM2SIZE(raddr);
1800
- if ( addr < 0 || addr >= ca->elements ) {
1801
- rb_raise(rb_eArgError,
1802
- "address %lld is out of range (0..%lld)",
1803
- (ca_size_t) addr, (ca_size_t) (ca->elements-1));
1804
- }
1805
- dim = ca->dim;
1806
- out = rb_ary_new2(ca->ndim);
1807
- for (i=ca->ndim-1; i>=0; i--) { /* in descending order */
1808
- rb_ary_store(out, i, SIZE2NUM(addr % dim[i]));
1809
- addr /= dim[i];
1810
- }
1861
+ n = cin->elements;
1862
+ {
1863
+ ca_size_t *src = (ca_size_t *) cin->ptr;
1864
+ ca_size_t addr;
1865
+ int k;
1866
+ for (j = 0; j < n; j++) {
1867
+ if ( m && m[j] ) {
1868
+ for (k = 0; k < ndim; k++) { p_out[k][j] = 0; }
1869
+ continue;
1870
+ }
1871
+ addr = src[j];
1872
+ if ( addr < 0 || addr >= elements ) {
1873
+ ca_detach(cin);
1874
+ rb_raise(rb_eArgError,
1875
+ "address %" PRId64 " is out of range (0..%" PRId64 ")",
1876
+ (ca_size_t) addr, (ca_size_t) (elements - 1));
1877
+ }
1878
+ for (k = ndim - 1; k >= 0; k--) {
1879
+ p_out[k][j] = addr % dim[k];
1880
+ addr /= dim[k];
1881
+ }
1882
+ }
1883
+ }
1811
1884
 
1812
- return out;
1885
+ ca_detach(cin);
1886
+ return out;
1887
+ }
1813
1888
  }
1814
1889
 
1815
- /* yard:
1816
- class CArray
1817
- def index2addr (*index)
1818
- end
1819
- end
1820
- */
1821
-
1822
- VALUE
1823
- rb_ca_index2addr (int argc, VALUE *argv, VALUE self)
1890
+ static VALUE
1891
+ index2addr_do (int ndim, ca_size_t *dim, int argc, VALUE *argv)
1824
1892
  {
1825
1893
  volatile VALUE obj;
1826
- CArray *ca, *co, *cidx[CA_RANK_MAX];
1894
+ CArray *co, *cidx[CA_RANK_MAX];
1827
1895
  ca_size_t *q, *p[CA_RANK_MAX], s[CA_RANK_MAX];
1828
- ca_size_t *dim;
1829
- ca_size_t addr, elements = 0;
1830
- int8_t i;
1831
- ca_size_t k, n;
1896
+ ca_size_t addr, k, n;
1832
1897
  boolean8_t *m;
1833
- int all_number = 1;
1898
+ int i, all_number = 1;
1899
+ int out_ndim = 1;
1900
+ ca_size_t out_dim[CA_RANK_MAX];
1901
+ ca_size_t out_elements = 1;
1902
+ int shape_from = -1;
1834
1903
 
1835
- TypedData_Get_Struct(self, CArray, &carray_data_type, ca);
1836
-
1837
- if ( argc != ca->ndim ) {
1838
- rb_raise(rb_eRuntimeError, "invalid ndim of index");
1904
+ if ( argc != ndim ) {
1905
+ rb_raise(rb_eArgError,
1906
+ "wrong number of indices (%d for %d)", argc, ndim);
1839
1907
  }
1840
1908
 
1841
- for (i=0; i<ca->ndim; i++) {
1909
+ for (i = 0; i < ndim; i++) {
1842
1910
  if ( ! rb_obj_is_kind_of(argv[i], rb_cInteger) ) {
1843
1911
  all_number = 0;
1844
1912
  break;
1845
1913
  }
1846
1914
  }
1847
1915
 
1916
+ /* All-scalar path: returns single Integer (unchanged). */
1848
1917
  if ( all_number ) {
1849
- dim = ca->dim;
1850
1918
  addr = 0;
1851
- for (i=0; i<ca->ndim; i++) {
1919
+ for (i = 0; i < ndim; i++) {
1852
1920
  k = NUM2SIZE(argv[i]);
1853
1921
  CA_CHECK_INDEX(k, dim[i]);
1854
1922
  addr = dim[i] * addr + k;
@@ -1856,62 +1924,77 @@ rb_ca_index2addr (int argc, VALUE *argv, VALUE self)
1856
1924
  return SIZE2NUM(addr);
1857
1925
  }
1858
1926
 
1859
- elements = 1;
1860
- for (i=0; i<ca->ndim; i++) {
1927
+ /* Vector path: wrap each arg as CA_SIZE view. Output shape follows
1928
+ * the first non-scalar CArray input; other non-scalar inputs must
1929
+ * match that shape. */
1930
+ for (i = 0; i < ndim; i++) {
1861
1931
  cidx[i] = ca_wrap_readonly(argv[i], CA_SIZE);
1862
1932
  if ( ! ca_is_scalar(cidx[i]) ) {
1863
- if ( elements == 1 ) {
1864
- elements = cidx[i]->elements;
1933
+ if ( shape_from < 0 ) {
1934
+ shape_from = i;
1935
+ out_ndim = cidx[i]->ndim;
1936
+ memcpy(out_dim, cidx[i]->dim, sizeof(ca_size_t) * out_ndim);
1937
+ out_elements = cidx[i]->elements;
1865
1938
  }
1866
- else if ( elements != cidx[i]->elements ) {
1867
- rb_raise(rb_eRuntimeError, "mismatch in # of elements");
1939
+ else {
1940
+ int j;
1941
+ if ( cidx[i]->ndim != out_ndim
1942
+ || cidx[i]->elements != out_elements ) {
1943
+ rb_raise(rb_eArgError,
1944
+ "shape mismatch: axis %d has shape ndim=%d elements=%"
1945
+ PRId64 ", expected ndim=%d elements=%" PRId64,
1946
+ i, cidx[i]->ndim, (ca_size_t) cidx[i]->elements,
1947
+ out_ndim, (ca_size_t) out_elements);
1948
+ }
1949
+ for (j = 0; j < out_ndim; j++) {
1950
+ if ( cidx[i]->dim[j] != out_dim[j] ) {
1951
+ rb_raise(rb_eArgError,
1952
+ "shape mismatch at axis %d dim %d", i, j);
1953
+ }
1954
+ }
1868
1955
  }
1869
1956
  }
1870
1957
  }
1871
1958
 
1872
- for (i=0; i<ca->ndim; i++) {
1959
+ for (i = 0; i < ndim; i++) {
1873
1960
  ca_attach(cidx[i]);
1874
1961
  ca_set_iterator(1, cidx[i], &p[i], &s[i]);
1875
1962
  }
1876
1963
 
1877
- obj = rb_carray_new(CA_SIZE, 1, &elements, 0, NULL);
1964
+ obj = rb_carray_new(CA_SIZE, out_ndim, out_dim, 0, NULL);
1878
1965
  TypedData_Get_Struct(obj, CArray, &carray_data_type, co);
1879
1966
 
1880
1967
  q = (ca_size_t *) co->ptr;
1881
1968
 
1882
- ca_copy_mask_overwrite_n(co, elements, ca->ndim, cidx);
1969
+ ca_copy_mask_overwrite_n(co, out_elements, ndim, cidx);
1883
1970
  m = ( co->mask ) ? (boolean8_t *) co->mask->ptr : NULL;
1884
1971
 
1885
- dim = ca->dim;
1886
-
1887
1972
  if ( m ) {
1888
- n = elements;
1973
+ n = out_elements;
1889
1974
  while ( n-- ) {
1890
- if ( !*m ) {
1975
+ if ( ! *m ) {
1891
1976
  addr = 0;
1892
- for (i=0; i<ca->ndim; i++) {
1977
+ for (i = 0; i < ndim; i++) {
1893
1978
  k = *(p[i]);
1894
- p[i]+=s[i];
1979
+ p[i] += s[i];
1895
1980
  CA_CHECK_INDEX(k, dim[i]);
1896
1981
  addr = dim[i] * addr + k;
1897
1982
  }
1898
1983
  *q = addr;
1899
1984
  }
1900
1985
  else {
1901
- for (i=0; i<ca->ndim; i++) {
1902
- p[i]+=s[i];
1903
- }
1986
+ for (i = 0; i < ndim; i++) { p[i] += s[i]; }
1904
1987
  }
1905
1988
  m++; q++;
1906
1989
  }
1907
1990
  }
1908
1991
  else {
1909
- n = elements;
1992
+ n = out_elements;
1910
1993
  while ( n-- ) {
1911
1994
  addr = 0;
1912
- for (i=0; i<ca->ndim; i++) {
1995
+ for (i = 0; i < ndim; i++) {
1913
1996
  k = *(p[i]);
1914
- p[i]+=s[i];
1997
+ p[i] += s[i];
1915
1998
  CA_CHECK_INDEX(k, dim[i]);
1916
1999
  addr = dim[i] * addr + k;
1917
2000
  }
@@ -1920,26 +2003,84 @@ rb_ca_index2addr (int argc, VALUE *argv, VALUE self)
1920
2003
  }
1921
2004
  }
1922
2005
 
1923
- for (i=0; i<ca->ndim; i++) {
1924
- ca_detach(cidx[i]);
1925
- }
2006
+ for (i = 0; i < ndim; i++) { ca_detach(cidx[i]); }
1926
2007
 
1927
2008
  return obj;
1928
2009
  }
1929
2010
 
2011
+ VALUE
2012
+ rb_ca_addr2index (VALUE self, VALUE raddr)
2013
+ {
2014
+ CArray *ca;
2015
+ TypedData_Get_Struct(self, CArray, &carray_data_type, ca);
2016
+ return addr2index_do(ca->ndim, ca->dim, ca->elements, raddr);
2017
+ }
2018
+
2019
+ VALUE
2020
+ rb_ca_index2addr (int argc, VALUE *argv, VALUE self)
2021
+ {
2022
+ CArray *ca;
2023
+ TypedData_Get_Struct(self, CArray, &carray_data_type, ca);
2024
+ return index2addr_do(ca->ndim, ca->dim, argc, argv);
2025
+ }
2026
+
2027
+ /* CArray.addr2index(addr, shape: [...]) */
2028
+ static VALUE
2029
+ rb_ca_s_addr2index (int argc, VALUE *argv, VALUE klass)
2030
+ {
2031
+ VALUE raddr, opts;
2032
+ ID kw_ids[1];
2033
+ VALUE kw_vals[1];
2034
+ int ndim;
2035
+ ca_size_t dim[CA_RANK_MAX];
2036
+ ca_size_t elements;
2037
+
2038
+ kw_ids[0] = rb_intern("shape");
2039
+ rb_scan_args(argc, argv, "1:", &raddr, &opts);
2040
+ if ( NIL_P(opts) ) {
2041
+ rb_raise(rb_eArgError, "missing keyword: shape");
2042
+ }
2043
+ rb_get_kwargs(opts, kw_ids, 1, 0, kw_vals);
2044
+ parse_shape_kwarg(kw_vals[0], &ndim, dim, &elements);
2045
+ return addr2index_do(ndim, dim, elements, raddr);
2046
+ }
2047
+
2048
+ /* CArray.index2addr(*index, shape: [...]) */
2049
+ static VALUE
2050
+ rb_ca_s_index2addr (int argc, VALUE *argv, VALUE klass)
2051
+ {
2052
+ VALUE rest, opts;
2053
+ ID kw_ids[1];
2054
+ VALUE kw_vals[1];
2055
+ int ndim;
2056
+ ca_size_t dim[CA_RANK_MAX];
2057
+ ca_size_t elements;
2058
+
2059
+ kw_ids[0] = rb_intern("shape");
2060
+ rb_scan_args(argc, argv, "*:", &rest, &opts);
2061
+ if ( NIL_P(opts) ) {
2062
+ rb_raise(rb_eArgError, "missing keyword: shape");
2063
+ }
2064
+ rb_get_kwargs(opts, kw_ids, 1, 0, kw_vals);
2065
+ parse_shape_kwarg(kw_vals[0], &ndim, dim, &elements);
2066
+ return index2addr_do(ndim, dim, (int) RARRAY_LEN(rest), RARRAY_PTR(rest));
2067
+ }
2068
+
1930
2069
 
1931
2070
  void
1932
- Init_carray_access ()
2071
+ Init_carray_access (void)
1933
2072
  {
1934
2073
 
1935
2074
  id_begin = rb_intern("begin");
1936
2075
  id_end = rb_intern("end");
1937
2076
  id_excl_end = rb_intern("exclude_end?");
1938
2077
 
1939
- id_ca = rb_intern("ca");
1940
2078
  id_to_ca = rb_intern("to_ca");
1941
- sym_star = ID2SYM(rb_intern("*"));
1942
- sym_perc = ID2SYM(rb_intern("%"));
2079
+ sym_star = ID2SYM(rb_intern("*"));
2080
+ sym_perc = ID2SYM(rb_intern("%"));
2081
+ sym_under = ID2SYM(rb_intern("_"));
2082
+ sym_gt = ID2SYM(rb_intern(">"));
2083
+ sym_tilde = ID2SYM(rb_intern("~"));
1943
2084
 
1944
2085
  rb_define_method(rb_cCArray, "[]", rb_ca_fetch_method, -1);
1945
2086
  rb_define_method(rb_cCArray, "[]=", rb_ca_store_method, -1);
@@ -1959,6 +2100,9 @@ Init_carray_access ()
1959
2100
  rb_define_method(rb_cCArray, "index2addr", rb_ca_index2addr, -1);
1960
2101
  rb_define_method(rb_cCArray, "addr2index", rb_ca_addr2index, 1);
1961
2102
 
2103
+ rb_define_singleton_method(rb_cCArray, "addr2index", rb_ca_s_addr2index, -1);
2104
+ rb_define_singleton_method(rb_cCArray, "index2addr", rb_ca_s_index2addr, -1);
2105
+
1962
2106
  rb_define_const(rb_cObject, "CA_REG_NONE", INT2NUM(CA_REG_NONE));
1963
2107
  rb_define_const(rb_cObject, "CA_REG_ALL", INT2NUM(CA_REG_ALL));
1964
2108
  rb_define_const(rb_cObject, "CA_REG_ADDRESS", INT2NUM(CA_REG_ADDRESS));
@@ -1974,8 +2118,6 @@ Init_carray_access ()
1974
2118
  rb_define_const(rb_cObject, "CA_REG_MAPPING", INT2NUM(CA_REG_MAPPING));
1975
2119
  rb_define_const(rb_cObject, "CA_REG_METHOD_CALL",
1976
2120
  INT2NUM(CA_REG_METHOD_CALL));
1977
- rb_define_const(rb_cObject, "CA_REG_UNBOUND_REPEAT",
1978
- INT2NUM(CA_REG_UNBOUND_REPEAT));
1979
2121
  rb_define_const(rb_cObject, "CA_REG_MEMBER", INT2NUM(CA_REG_MEMBER));
1980
2122
  rb_define_const(rb_cObject, "CA_REG_ATTRIBUTE", INT2NUM(CA_REG_ATTRIBUTE));
1981
2123