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
data/ext/carray_element.c CHANGED
@@ -1,22 +1,82 @@
1
1
  /* ---------------------------------------------------------------------------
2
2
 
3
- carray_element.c
3
+ Per-cell (`elem_*`) hot-path family for tight scalar loops
4
+ (histogram increment, scatter writes, per-cell swap / mask).
4
5
 
5
- This file is part of Ruby/CArray extension library.
6
+ These skip the indexer front-end (`ca[i,j,k]` -> scan_index_v2 ->
7
+ ref_point) and land at the same `rb_ca_fetch_index` backend with
8
+ the front-end dispatcher removed. Inline helpers below collapse
9
+ the remaining per-call overheads (double TypedData_Get_Struct,
10
+ T_ARRAY bounds check, NUM2SIZE Bignum branch, ca_update_mask
11
+ function-call for the entity + no-mask common case). Semantics
12
+ match the indexer path.
6
13
 
7
- Copyright (C) 2005-2020 Hiroki Motoyoshi
14
+ YARD signatures live in yard-stubs/carray_element.rb.
8
15
 
9
16
  ---------------------------------------------------------------------------- */
10
17
 
11
18
  #include "ruby.h"
12
19
  #include "carray.h"
20
+ #include "ca_obj_face.h"
13
21
 
14
- /* ------------------------------------------------------------------- */
22
+ /* T_ARRAY index parser. ridx is assumed already known to be T_ARRAY.
23
+ Direct VALUE * access + FIXNUM_P fast path eliminates rb_ary_entry
24
+ bounds check and NUM2SIZE Bignum branching for the common case. */
25
+ static inline void
26
+ elem_parse_idx_array (CArray *ca, VALUE rary, ca_size_t *idx)
27
+ {
28
+ const VALUE *iarr = RARRAY_CONST_PTR(rary);
29
+ int8_t ndim = ca->ndim;
30
+ int8_t i;
31
+ for (i=0; i<ndim; i++) {
32
+ VALUE v = iarr[i];
33
+ ca_size_t k = FIXNUM_P(v) ? (ca_size_t)FIX2LONG(v) : NUM2SIZE(v);
34
+ CA_CHECK_INDEX(k, ca->dim[i]);
35
+ idx[i] = k;
36
+ }
37
+ }
38
+
39
+ /* Scalar index parser (flat addr). FIXNUM_P fast path. */
40
+ static inline ca_size_t
41
+ elem_parse_idx_scalar (CArray *ca, VALUE ridx)
42
+ {
43
+ ca_size_t k = FIXNUM_P(ridx) ? (ca_size_t)FIX2LONG(ridx) : NUM2SIZE(ridx);
44
+ CA_CHECK_INDEX(k, ca->elements);
45
+ return k;
46
+ }
15
47
 
16
- /* @overload elem_swap (idx1, idx2)
48
+ /* Mask-existence probe with inline skip for the entity+no-mask common
49
+ case. Equivalent to `ca_update_mask(ca); has_mask = (ca->mask != NULL);`
50
+ but avoids the function call when the result is statically known. */
51
+ static inline int
52
+ elem_probe_mask (CArray *ca)
53
+ {
54
+ if ( ca->mask ) return 1; /* mask already materialised */
55
+ if ( ca_is_entity(ca) ) return 0; /* entity + no mask: definitely none */
56
+ ca_update_mask(ca); /* view: may need to materialise */
57
+ return ( ca->mask != NULL );
58
+ }
17
59
 
18
- (Element) Swaps the values at the elements which are specified by arguments.
19
- */
60
+ /* Entity-array fast path: compute flat addr from idx[] and access ptr directly.
61
+ Bypasses ca_func[ARRAY].xfer_index function-pointer dispatch.
62
+ Caller must ensure ca_is_entity(ca) and ca->ptr is attached (= entity
63
+ arrays are always attached by definition). */
64
+ static inline ca_size_t
65
+ elem_entity_addr (CArray *ca, ca_size_t *idx)
66
+ {
67
+ ca_size_t n = idx[0];
68
+ int8_t i;
69
+ for (i=1; i<ca->ndim; i++) {
70
+ n = ca->dim[i] * n + idx[i];
71
+ }
72
+ return n;
73
+ }
74
+
75
+ /* ------------------------------------------------------------------- */
76
+
77
+ /* `elem_swap(idx1, idx2)` — exchanges the values (and mask states,
78
+ * if any) at cells `idx1` and `idx2` in place. `idx*` accept either
79
+ * a flat Integer address or an Array<Integer> of per-axis indices. */
20
80
 
21
81
  VALUE
22
82
  rb_ca_elem_swap (VALUE self, VALUE ridx1, VALUE ridx2)
@@ -24,10 +84,8 @@ rb_ca_elem_swap (VALUE self, VALUE ridx1, VALUE ridx2)
24
84
  CArray *ca;
25
85
  ca_size_t idx1[CA_RANK_MAX], idx2[CA_RANK_MAX];
26
86
  ca_size_t addr1 = 0, addr2 = 0;
27
- int8_t i;
28
- ca_size_t k;
29
87
  int has_mask, has_index1, has_index2;
30
- char _val1[32], _val2[32];
88
+ char _val1[64], _val2[64];
31
89
  char *val1 = _val1, *val2 = _val2;
32
90
  boolean8_t m1 = 0, m2 = 0;
33
91
 
@@ -35,20 +93,15 @@ rb_ca_elem_swap (VALUE self, VALUE ridx1, VALUE ridx2)
35
93
 
36
94
  TypedData_Get_Struct(self, CArray, &carray_data_type, ca);
37
95
 
38
- ca_update_mask(ca);
39
- has_mask = ( ca->mask ) ? 1 : 0;
96
+ has_mask = elem_probe_mask(ca);
40
97
 
41
- if ( ca->bytes > 32 ) {
42
- val1 = malloc_with_check(ca->bytes);
43
- val2 = malloc_with_check(ca->bytes);
98
+ if ( ca->bytes > 64 ) {
99
+ val1 = xmalloc(ca->bytes);
100
+ val2 = xmalloc(ca->bytes);
44
101
  }
45
102
 
46
103
  if ( TYPE(ridx1) == T_ARRAY ) {
47
- for (i=0; i<ca->ndim; i++) {
48
- k = NUM2SIZE(rb_ary_entry(ridx1, i));
49
- CA_CHECK_INDEX(k, ca->dim[i]);
50
- idx1[i] = k;
51
- }
104
+ elem_parse_idx_array(ca, ridx1, idx1);
52
105
  has_index1 = 1;
53
106
  ca_fetch_index(ca, idx1, val1);
54
107
  if ( has_mask ) {
@@ -56,9 +109,7 @@ rb_ca_elem_swap (VALUE self, VALUE ridx1, VALUE ridx2)
56
109
  }
57
110
  }
58
111
  else {
59
- k = NUM2SIZE(ridx1);
60
- CA_CHECK_INDEX(k, ca->elements);
61
- addr1 = k;
112
+ addr1 = elem_parse_idx_scalar(ca, ridx1);
62
113
  has_index1 = 0;
63
114
  ca_fetch_addr(ca, addr1, val1);
64
115
  if ( has_mask ) {
@@ -67,11 +118,7 @@ rb_ca_elem_swap (VALUE self, VALUE ridx1, VALUE ridx2)
67
118
  }
68
119
 
69
120
  if ( TYPE(ridx2) == T_ARRAY ) {
70
- for (i=0; i<ca->ndim; i++) {
71
- k = NUM2SIZE(rb_ary_entry(ridx2, i));
72
- CA_CHECK_INDEX(k, ca->dim[i]);
73
- idx2[i] = k;
74
- }
121
+ elem_parse_idx_array(ca, ridx2, idx2);
75
122
  has_index2 = 1;
76
123
  ca_fetch_index(ca, idx2, val2);
77
124
  if ( has_mask ) {
@@ -79,9 +126,7 @@ rb_ca_elem_swap (VALUE self, VALUE ridx1, VALUE ridx2)
79
126
  }
80
127
  }
81
128
  else {
82
- k = NUM2SIZE(ridx2);
83
- CA_CHECK_INDEX(k, ca->elements);
84
- addr2 = k;
129
+ addr2 = elem_parse_idx_scalar(ca, ridx2);
85
130
  has_index2 = 0;
86
131
  ca_fetch_addr(ca, addr2, val2);
87
132
  if ( has_mask ) {
@@ -115,18 +160,16 @@ rb_ca_elem_swap (VALUE self, VALUE ridx1, VALUE ridx2)
115
160
  }
116
161
  }
117
162
 
118
- if ( ca->bytes > 32 ) {
119
- free(val1);
120
- free(val2);
163
+ if ( ca->bytes > 64 ) {
164
+ xfree(val1);
165
+ xfree(val2);
121
166
  }
122
167
 
123
168
  return self;
124
169
  }
125
170
 
126
- /* @overload elem_copy (idx1, idx2)
127
-
128
- (Element) Copies the value of the element of idx1 to the element of idx2
129
- */
171
+ /* `elem_copy(idx1, idx2)` — copies the value (and mask state) at
172
+ * `idx1` into the cell at `idx2`; source cell is left unchanged. */
130
173
 
131
174
  VALUE
132
175
  rb_ca_elem_copy (VALUE self, VALUE ridx1, VALUE ridx2)
@@ -134,10 +177,8 @@ rb_ca_elem_copy (VALUE self, VALUE ridx1, VALUE ridx2)
134
177
  CArray *ca;
135
178
  ca_size_t idx1[CA_RANK_MAX], idx2[CA_RANK_MAX];
136
179
  ca_size_t addr1 = 0, addr2 = 0;
137
- int8_t i;
138
- ca_size_t k;
139
180
  int has_mask;
140
- char _val[32];
181
+ char _val[64];
141
182
  char *val = _val;
142
183
  boolean8_t m = 0;
143
184
 
@@ -145,28 +186,21 @@ rb_ca_elem_copy (VALUE self, VALUE ridx1, VALUE ridx2)
145
186
 
146
187
  TypedData_Get_Struct(self, CArray, &carray_data_type, ca);
147
188
 
148
- ca_update_mask(ca);
149
- has_mask = ( ca->mask ) ? 1 : 0;
189
+ has_mask = elem_probe_mask(ca);
150
190
 
151
- if ( ca->bytes > 32 ) {
152
- val = malloc_with_check(ca->bytes);
191
+ if ( ca->bytes > 64 ) {
192
+ val = xmalloc(ca->bytes);
153
193
  }
154
194
 
155
195
  if ( TYPE(ridx1) == T_ARRAY ) {
156
- for (i=0; i<ca->ndim; i++) {
157
- k = NUM2SIZE(rb_ary_entry(ridx1, i));
158
- CA_CHECK_INDEX(k, ca->dim[i]);
159
- idx1[i] = k;
160
- }
196
+ elem_parse_idx_array(ca, ridx1, idx1);
161
197
  ca_fetch_index(ca, idx1, val);
162
198
  if ( has_mask ) {
163
199
  ca_fetch_index(ca->mask, idx1, &m);
164
200
  }
165
201
  }
166
202
  else {
167
- k = NUM2SIZE(ridx1);
168
- CA_CHECK_INDEX(k, ca->elements);
169
- addr1 = k;
203
+ addr1 = elem_parse_idx_scalar(ca, ridx1);
170
204
  ca_fetch_addr(ca, addr1, val);
171
205
  if ( has_mask ) {
172
206
  ca_fetch_addr(ca->mask, addr1, &m);
@@ -174,105 +208,228 @@ rb_ca_elem_copy (VALUE self, VALUE ridx1, VALUE ridx2)
174
208
  }
175
209
 
176
210
  if ( TYPE(ridx2) == T_ARRAY ) {
177
- for (i=0; i<ca->ndim; i++) {
178
- k = NUM2SIZE(rb_ary_entry(ridx2, i));
179
- CA_CHECK_INDEX(k, ca->dim[i]);
180
- idx2[i] = k;
181
- }
211
+ elem_parse_idx_array(ca, ridx2, idx2);
182
212
  ca_store_index(ca, idx2, val);
183
213
  if ( has_mask ) {
184
214
  ca_store_index(ca->mask, idx2, &m);
185
215
  }
186
216
  }
187
217
  else {
188
- k = NUM2SIZE(ridx2);
189
- CA_CHECK_INDEX(k, ca->elements);
190
- addr2 = k;
218
+ addr2 = elem_parse_idx_scalar(ca, ridx2);
191
219
  ca_store_addr(ca, addr2, val);
192
220
  if ( has_mask ) {
193
221
  ca_store_addr(ca->mask, addr2, &m);
194
222
  }
195
223
  }
196
224
 
197
- if ( ca->bytes > 32 ) {
198
- free(val);
225
+ if ( ca->bytes > 64 ) {
226
+ xfree(val);
199
227
  }
200
228
 
201
229
  return self;
202
230
  }
203
231
 
204
- /* @overload elem_store (idx, obj)
205
-
206
- (Element) Stores the object value in the element of idx.
207
- */
232
+ /* `elem_store(idx, obj)` — stores `obj` (cast to `self.data_type`)
233
+ * at cell `idx`, clearing any mask state there. Passing `UNDEF`
234
+ * masks the cell. */
208
235
 
209
236
  VALUE
210
237
  rb_ca_elem_store (VALUE self, VALUE ridx, VALUE obj)
211
238
  {
212
239
  CArray *ca;
213
240
  ca_size_t idx[CA_RANK_MAX];
214
- ca_size_t addr = 0;
215
- int8_t i;
216
- ca_size_t k;
241
+ ca_size_t addr;
242
+ int from_array;
217
243
 
218
244
  rb_ca_modify(self);
219
245
 
220
246
  TypedData_Get_Struct(self, CArray, &carray_data_type, ca);
221
247
 
222
248
  if ( TYPE(ridx) == T_ARRAY ) {
223
- for (i=0; i<ca->ndim; i++) {
224
- k = NUM2SIZE(rb_ary_entry(ridx, i));
225
- CA_CHECK_INDEX(k, ca->dim[i]);
226
- idx[i] = k;
249
+ elem_parse_idx_array(ca, ridx, idx);
250
+ from_array = 1;
251
+ }
252
+ else {
253
+ addr = elem_parse_idx_scalar(ca, ridx);
254
+ from_array = 0;
255
+ }
256
+
257
+ /* Super-hot path: entity + no mask + common numeric dtype + Numeric obj.
258
+ Bypasses rb_ca_store_index/addr -> ca_update_mask + rb_ca_obj2ptr ->
259
+ cast_table dispatch. Falls through to general path for: views,
260
+ existing mask, non-numeric dtype, UNDEF, or non-Numeric obj. */
261
+ if ( ca_is_entity(ca) && ! ca->mask && obj != CA_UNDEF ) {
262
+ int8_t dt = ca->data_type;
263
+ if ( dt == CA_FLOAT64 || dt == CA_FLOAT32 ||
264
+ dt == CA_INT64 || dt == CA_INT32 ||
265
+ dt == CA_INT16 || dt == CA_INT8 ||
266
+ dt == CA_UINT64 || dt == CA_UINT32 ||
267
+ dt == CA_UINT16 || dt == CA_UINT8 ||
268
+ dt == CA_BOOLEAN ) {
269
+ /* Compute flat addr if T_ARRAY index path. */
270
+ if ( from_array ) addr = elem_entity_addr(ca, idx);
271
+ char *p = ca->ptr + ca->bytes * addr;
272
+ /* Convert obj to dtype + store. Restrict to Fixnum / Float / true
273
+ / false to avoid Bignum / Rational / Complex coercion that the
274
+ cast_table handles in the general path. */
275
+ switch ( dt ) {
276
+ case CA_FLOAT64:
277
+ if ( RB_FLOAT_TYPE_P(obj) ) { *(double *)p = RFLOAT_VALUE(obj); return obj; }
278
+ if ( FIXNUM_P(obj) ) { *(double *)p = (double)FIX2LONG(obj); return obj; }
279
+ break;
280
+ case CA_FLOAT32:
281
+ if ( RB_FLOAT_TYPE_P(obj) ) { *(float *)p = (float)RFLOAT_VALUE(obj); return obj; }
282
+ if ( FIXNUM_P(obj) ) { *(float *)p = (float)FIX2LONG(obj); return obj; }
283
+ break;
284
+ case CA_INT64:
285
+ if ( FIXNUM_P(obj) ) { *(int64_t *)p = (int64_t)FIX2LONG(obj); return obj; }
286
+ break;
287
+ case CA_INT32:
288
+ if ( FIXNUM_P(obj) ) { *(int32_t *)p = (int32_t)FIX2LONG(obj); return obj; }
289
+ break;
290
+ case CA_INT16:
291
+ if ( FIXNUM_P(obj) ) { *(int16_t *)p = (int16_t)FIX2LONG(obj); return obj; }
292
+ break;
293
+ case CA_INT8:
294
+ if ( FIXNUM_P(obj) ) { *(int8_t *)p = (int8_t)FIX2LONG(obj); return obj; }
295
+ break;
296
+ case CA_UINT64:
297
+ if ( FIXNUM_P(obj) ) { *(uint64_t *)p = (uint64_t)FIX2LONG(obj); return obj; }
298
+ break;
299
+ case CA_UINT32:
300
+ if ( FIXNUM_P(obj) ) { *(uint32_t *)p = (uint32_t)FIX2LONG(obj); return obj; }
301
+ break;
302
+ case CA_UINT16:
303
+ if ( FIXNUM_P(obj) ) { *(uint16_t *)p = (uint16_t)FIX2LONG(obj); return obj; }
304
+ break;
305
+ case CA_UINT8:
306
+ if ( FIXNUM_P(obj) ) { *(uint8_t *)p = (uint8_t)FIX2LONG(obj); return obj; }
307
+ break;
308
+ case CA_BOOLEAN:
309
+ if ( obj == Qtrue ) { *(uint8_t *)p = 1; return obj; }
310
+ if ( obj == Qfalse ) { *(uint8_t *)p = 0; return obj; }
311
+ if ( FIXNUM_P(obj) ) { *(uint8_t *)p = (FIX2LONG(obj) != 0); return obj; }
312
+ break;
313
+ }
314
+ /* Fall through: dtype matches but obj kind needs general coercion. */
227
315
  }
316
+ }
317
+
318
+ /* General path: dispatch through rb_ca_store_index/addr. */
319
+ if ( from_array ) {
228
320
  rb_ca_store_index(self, idx, obj);
229
321
  }
230
322
  else {
231
- k = NUM2SIZE(ridx);
232
- CA_CHECK_INDEX(k, ca->elements);
233
- addr = k;
234
323
  rb_ca_store_addr(self, addr, obj);
235
324
  }
236
325
 
237
326
  return obj;
238
327
  }
239
328
 
240
- /* @overload elem_fetch (idx)
241
-
242
- (Element) Fetches the object value at the element of idx.
243
- */
329
+ /* `elem_fetch(idx)` — returns the value at cell `idx` (cast to a
330
+ * Ruby object). Returns `UNDEF` if the cell is masked, `nil` if
331
+ * `self` is empty. */
244
332
 
245
333
  VALUE
246
334
  rb_ca_elem_fetch (VALUE self, VALUE ridx)
247
335
  {
248
336
  CArray *ca;
249
337
  ca_size_t idx[CA_RANK_MAX];
250
- ca_size_t addr = 0;
251
- int8_t i;
252
- ca_size_t k;
253
338
 
254
339
  TypedData_Get_Struct(self, CArray, &carray_data_type, ca);
255
340
 
341
+ if ( ca_is_empty(ca) ) return Qnil;
342
+
256
343
  if ( TYPE(ridx) == T_ARRAY ) {
257
- for (i=0; i<ca->ndim; i++) {
258
- k = NUM2SIZE(rb_ary_entry(ridx, i));
259
- CA_CHECK_INDEX(k, ca->dim[i]);
260
- idx[i] = k;
344
+ volatile VALUE out;
345
+ elem_parse_idx_array(ca, ridx, idx);
346
+
347
+ /* Super-hot path: entity + common numeric dtype + no mask.
348
+ Bypass ca_fetch_index dispatch AND rb_ca_ptr2obj cast-table dispatch.
349
+ Covers the bench's float64 / int64 / int32 cases. */
350
+ if ( ca_is_entity(ca) && ! ca->mask ) {
351
+ ca_size_t addr = elem_entity_addr(ca, idx);
352
+ char *p = ca->ptr + ca->bytes * addr;
353
+ switch ( ca->data_type ) {
354
+ case CA_FLOAT64: return rb_float_new(*(double *)p);
355
+ case CA_INT64: return LL2NUM(*(int64_t *)p);
356
+ case CA_INT32: return INT2NUM(*(int32_t *)p);
357
+ case CA_FLOAT32: return rb_float_new((double)*(float *)p);
358
+ case CA_INT16: return INT2FIX(*(int16_t *)p);
359
+ case CA_INT8: return INT2FIX(*(int8_t *)p);
360
+ case CA_UINT8: return INT2FIX(*(uint8_t *)p);
361
+ case CA_UINT16: return INT2FIX(*(uint16_t *)p);
362
+ case CA_UINT32: return UINT2NUM(*(uint32_t *)p);
363
+ case CA_UINT64: return ULL2NUM(*(uint64_t *)p);
364
+ case CA_BOOLEAN: return (*(uint8_t *)p) ? Qtrue : Qfalse;
365
+ /* default: fall through to general path below */
366
+ }
367
+ }
368
+
369
+ /* General path: inlined rb_ca_fetch_index body (avoid duplicate
370
+ TypedData_Get_Struct). Semantics identical to rb_ca_fetch_index. */
371
+ if ( ca->bytes <= 64 ) {
372
+ char v[64];
373
+ if ( ca_is_entity(ca) ) {
374
+ ca_size_t addr = elem_entity_addr(ca, idx);
375
+ memcpy(v, ca->ptr + ca->bytes * addr, ca->bytes);
376
+ }
377
+ else {
378
+ ca_fetch_index(ca, idx, v);
379
+ }
380
+ out = rb_ca_ptr2obj(self, v);
381
+ }
382
+ else {
383
+ char *v = xmalloc(ca->bytes);
384
+ ca_fetch_index(ca, idx, v);
385
+ out = rb_ca_ptr2obj(self, v);
386
+ xfree(v);
261
387
  }
262
- return rb_ca_fetch_index(self, idx);
388
+
389
+ /* Mask check with inline skip for the entity+no-mask common case. */
390
+ if ( ca->mask ) {
391
+ boolean8_t mval;
392
+ ca_fetch_index(ca->mask, idx, &mval);
393
+ if ( mval ) return CA_UNDEF;
394
+ }
395
+ else if ( ! ca_is_entity(ca) ) {
396
+ ca_update_mask(ca);
397
+ if ( ca->mask ) {
398
+ boolean8_t mval;
399
+ ca_fetch_index(ca->mask, idx, &mval);
400
+ if ( mval ) return CA_UNDEF;
401
+ }
402
+ }
403
+
404
+ CA_FACE_STORAGE_TO_SCALAR_IF_FACE(out, self, ca);
405
+ return out;
263
406
  }
264
407
  else {
265
- k = NUM2SIZE(ridx);
266
- CA_CHECK_INDEX(k, ca->elements);
267
- addr = k;
408
+ ca_size_t addr = elem_parse_idx_scalar(ca, ridx);
409
+ /* Super-hot flat-addr path: entity + numeric + no mask. */
410
+ if ( ca_is_entity(ca) && ! ca->mask ) {
411
+ char *p = ca->ptr + ca->bytes * addr;
412
+ switch ( ca->data_type ) {
413
+ case CA_FLOAT64: return rb_float_new(*(double *)p);
414
+ case CA_INT64: return LL2NUM(*(int64_t *)p);
415
+ case CA_INT32: return INT2NUM(*(int32_t *)p);
416
+ case CA_FLOAT32: return rb_float_new((double)*(float *)p);
417
+ case CA_INT16: return INT2FIX(*(int16_t *)p);
418
+ case CA_INT8: return INT2FIX(*(int8_t *)p);
419
+ case CA_UINT8: return INT2FIX(*(uint8_t *)p);
420
+ case CA_UINT16: return INT2FIX(*(uint16_t *)p);
421
+ case CA_UINT32: return UINT2NUM(*(uint32_t *)p);
422
+ case CA_UINT64: return ULL2NUM(*(uint64_t *)p);
423
+ case CA_BOOLEAN: return (*(uint8_t *)p) ? Qtrue : Qfalse;
424
+ }
425
+ }
268
426
  return rb_ca_fetch_addr(self, addr);
269
427
  }
270
428
  }
271
429
 
272
- /* @overload elem_incr (idx)
273
-
274
- (Element) Increments the value by 1 at the element of idx.
275
- */
430
+ /* `elem_incr(idx)` — increments the integer cell at `idx` by 1 in
431
+ * place, returning the new value. Masked cells are skipped (returns
432
+ * `nil`). Raises for non-integer data_type. */
276
433
 
277
434
  VALUE
278
435
  rb_ca_elem_incr (VALUE self, VALUE ridx1)
@@ -281,12 +438,9 @@ rb_ca_elem_incr (VALUE self, VALUE ridx1)
281
438
  CArray *ca;
282
439
  ca_size_t idx1[CA_RANK_MAX];
283
440
  ca_size_t addr1 = 0;
284
- int8_t i;
285
- ca_size_t k;
286
441
  int has_index1 = 0;
287
442
  int has_mask;
288
- char _val[8];
289
- char *val = _val;
443
+ char val[8];
290
444
  boolean8_t m = 0;
291
445
 
292
446
  rb_ca_modify(self);
@@ -298,39 +452,49 @@ rb_ca_elem_incr (VALUE self, VALUE ridx1)
298
452
  "incremented array should be an integer array");
299
453
  }
300
454
 
301
- ca_update_mask(ca);
302
- has_mask = ( ca->mask ) ? 1 : 0;
455
+ has_mask = elem_probe_mask(ca);
303
456
 
304
- if ( TYPE(ridx1) == T_ARRAY ) {
305
- for (i=0; i<ca->ndim; i++) {
306
- k = NUM2SIZE(rb_ary_entry(ridx1, i));
307
- CA_CHECK_INDEX(k, ca->dim[i]);
308
- idx1[i] = k;
457
+ /* Super-hot path: entity + no mask + integer dtype.
458
+ Reads p directly, modifies in place — no separate fetch/store dispatch. */
459
+ if ( ca_is_entity(ca) && ! has_mask ) {
460
+ ca_size_t addr;
461
+ if ( TYPE(ridx1) == T_ARRAY ) {
462
+ elem_parse_idx_array(ca, ridx1, idx1);
463
+ addr = elem_entity_addr(ca, idx1);
309
464
  }
310
- if ( has_mask ) {
311
- ca_fetch_index(ca->mask, idx1, &m);
465
+ else {
466
+ addr = elem_parse_idx_scalar(ca, ridx1);
312
467
  }
313
- if ( m ) {
314
- return Qnil;
468
+ void *p = ca->ptr + ca->bytes * addr;
469
+ switch ( ca->data_type ) {
470
+ case CA_INT8: return INT2FIX(++*((int8_t *)p));
471
+ case CA_UINT8: return INT2FIX(++*((uint8_t *)p));
472
+ case CA_INT16: return INT2FIX(++*((int16_t *)p));
473
+ case CA_UINT16: return INT2FIX(++*((uint16_t*)p));
474
+ case CA_INT32: return INT2NUM(++*((int32_t *)p));
475
+ case CA_UINT32: return UINT2NUM(++*((uint32_t*)p));
476
+ case CA_INT64: return LL2NUM(++*((int64_t *)p));
477
+ case CA_UINT64: return ULL2NUM(++*((uint64_t*)p));
315
478
  }
316
- else {
317
- ca_fetch_index(ca, idx1, val);
479
+ }
480
+
481
+ /* General path: view array or masked. */
482
+ if ( TYPE(ridx1) == T_ARRAY ) {
483
+ elem_parse_idx_array(ca, ridx1, idx1);
484
+ if ( has_mask ) {
485
+ ca_fetch_index(ca->mask, idx1, &m);
486
+ if ( m ) return Qnil;
318
487
  }
488
+ ca_fetch_index(ca, idx1, val);
319
489
  has_index1 = 1;
320
490
  }
321
491
  else {
322
- k = NUM2SIZE(ridx1);
323
- CA_CHECK_INDEX(k, ca->elements);
324
- addr1 = k;
492
+ addr1 = elem_parse_idx_scalar(ca, ridx1);
325
493
  if ( has_mask ) {
326
494
  ca_fetch_addr(ca->mask, addr1, &m);
495
+ if ( m ) return Qnil;
327
496
  }
328
- if ( m ) {
329
- return Qnil;
330
- }
331
- else {
332
- ca_fetch_addr(ca, addr1, val);
333
- }
497
+ ca_fetch_addr(ca, addr1, val);
334
498
  }
335
499
 
336
500
  switch ( ca->data_type ) {
@@ -354,10 +518,9 @@ rb_ca_elem_incr (VALUE self, VALUE ridx1)
354
518
  return out;
355
519
  }
356
520
 
357
- /* @overload elem_decr (idx)
358
-
359
- (Element) Decrements the value by 1 at the element of idx.
360
- */
521
+ /* `elem_decr(idx)` — decrements the integer cell at `idx` by 1 in
522
+ * place, returning the new value. Masked cells are skipped (returns
523
+ * `nil`). Raises for non-integer data_type. */
361
524
 
362
525
  VALUE
363
526
  rb_ca_elem_decr (VALUE self, VALUE ridx1)
@@ -366,12 +529,9 @@ rb_ca_elem_decr (VALUE self, VALUE ridx1)
366
529
  CArray *ca;
367
530
  ca_size_t idx1[CA_RANK_MAX];
368
531
  ca_size_t addr1 = 0;
369
- int8_t i;
370
- ca_size_t k;
371
532
  int has_index1 = 0;
372
533
  int has_mask;
373
- char _val[8];
374
- char *val = _val;
534
+ char val[8];
375
535
  boolean8_t m = 0;
376
536
 
377
537
  rb_ca_modify(self);
@@ -383,39 +543,47 @@ rb_ca_elem_decr (VALUE self, VALUE ridx1)
383
543
  "decremented array should be an integer array");
384
544
  }
385
545
 
386
- ca_update_mask(ca);
387
- has_mask = ( ca->mask ) ? 1 : 0;
546
+ has_mask = elem_probe_mask(ca);
388
547
 
389
- if ( TYPE(ridx1) == T_ARRAY ) {
390
- for (i=0; i<ca->ndim; i++) {
391
- k = NUM2SIZE(rb_ary_entry(ridx1, i));
392
- CA_CHECK_INDEX(k, ca->dim[i]);
393
- idx1[i] = k;
548
+ /* Super-hot path: mirror of elem_incr. */
549
+ if ( ca_is_entity(ca) && ! has_mask ) {
550
+ ca_size_t addr;
551
+ if ( TYPE(ridx1) == T_ARRAY ) {
552
+ elem_parse_idx_array(ca, ridx1, idx1);
553
+ addr = elem_entity_addr(ca, idx1);
394
554
  }
395
- if ( has_mask ) {
396
- ca_fetch_index(ca->mask, idx1, &m);
555
+ else {
556
+ addr = elem_parse_idx_scalar(ca, ridx1);
397
557
  }
398
- if ( m ) {
399
- return Qnil;
558
+ void *p = ca->ptr + ca->bytes * addr;
559
+ switch ( ca->data_type ) {
560
+ case CA_INT8: return INT2FIX(--*((int8_t *)p));
561
+ case CA_UINT8: return INT2FIX(--*((uint8_t *)p));
562
+ case CA_INT16: return INT2FIX(--*((int16_t *)p));
563
+ case CA_UINT16: return INT2FIX(--*((uint16_t*)p));
564
+ case CA_INT32: return INT2NUM(--*((int32_t *)p));
565
+ case CA_UINT32: return UINT2NUM(--*((uint32_t*)p));
566
+ case CA_INT64: return LL2NUM(--*((int64_t *)p));
567
+ case CA_UINT64: return ULL2NUM(--*((uint64_t*)p));
400
568
  }
401
- else {
402
- ca_fetch_index(ca, idx1, val);
569
+ }
570
+
571
+ if ( TYPE(ridx1) == T_ARRAY ) {
572
+ elem_parse_idx_array(ca, ridx1, idx1);
573
+ if ( has_mask ) {
574
+ ca_fetch_index(ca->mask, idx1, &m);
575
+ if ( m ) return Qnil;
403
576
  }
577
+ ca_fetch_index(ca, idx1, val);
404
578
  has_index1 = 1;
405
579
  }
406
580
  else {
407
- k = NUM2SIZE(ridx1);
408
- CA_CHECK_INDEX(k, ca->elements);
409
- addr1 = k;
581
+ addr1 = elem_parse_idx_scalar(ca, ridx1);
410
582
  if ( has_mask ) {
411
583
  ca_fetch_addr(ca->mask, addr1, &m);
584
+ if ( m ) return Qnil;
412
585
  }
413
- if ( m ) {
414
- return Qnil;
415
- }
416
- else {
417
- ca_fetch_addr(ca, addr1, val);
418
- }
586
+ ca_fetch_addr(ca, addr1, val);
419
587
  }
420
588
 
421
589
  switch ( ca->data_type ) {
@@ -425,8 +593,8 @@ rb_ca_elem_decr (VALUE self, VALUE ridx1)
425
593
  case CA_UINT16: out = UINT2NUM(--*((uint16_t*) val)); break;
426
594
  case CA_INT32: out = INT2NUM(--*((int32_t*) val)); break;
427
595
  case CA_UINT32: out = UINT2NUM(--*((uint32_t*) val)); break;
428
- case CA_INT64: out = INT2NUM(--*((int64_t*) val)); break;
429
- case CA_UINT64: out = UINT2NUM(--*((uint64_t*) val)); break;
596
+ case CA_INT64: out = LL2NUM(--*((int64_t*) val)); break;
597
+ case CA_UINT64: out = ULL2NUM(--*((uint64_t*) val)); break;
430
598
  }
431
599
 
432
600
  if ( has_index1 ) {
@@ -439,39 +607,46 @@ rb_ca_elem_decr (VALUE self, VALUE ridx1)
439
607
  return out;
440
608
  }
441
609
 
442
- /* @overload elem_masked? (idx)
443
-
444
- (Masking, Element) Returns true if the element at given idx is masked.
445
- */
610
+ /* `elem_masked?(idx)` — returns `true` if the cell at `idx` is
611
+ * masked. */
446
612
 
447
613
  VALUE
448
614
  rb_ca_elem_test_masked (VALUE self, VALUE ridx1)
449
615
  {
450
616
  CArray *ca;
451
617
  ca_size_t idx1[CA_RANK_MAX];
452
- ca_size_t addr1 = 0;
453
- int8_t i;
454
- ca_size_t k;
455
618
  boolean8_t m = 0;
456
619
 
457
620
  TypedData_Get_Struct(self, CArray, &carray_data_type, ca);
458
621
 
622
+ /* Super-hot path: entity. Covers both no-mask (Qfalse fast) and masked
623
+ (direct ca->mask->ptr read) cases without dispatch. Mask itself is
624
+ always a boolean8_t entity (= 1 byte / element, same shape as ca),
625
+ so flat addr from elem_entity_addr applies directly. */
626
+ if ( ca_is_entity(ca) ) {
627
+ ca_size_t addr;
628
+ if ( TYPE(ridx1) == T_ARRAY ) {
629
+ elem_parse_idx_array(ca, ridx1, idx1);
630
+ addr = elem_entity_addr(ca, idx1);
631
+ }
632
+ else {
633
+ addr = elem_parse_idx_scalar(ca, ridx1);
634
+ }
635
+ if ( ! ca->mask ) return Qfalse;
636
+ return ((boolean8_t *)ca->mask->ptr)[addr] ? Qtrue : Qfalse;
637
+ }
638
+
639
+ /* General path: view array. Walk parent chain to materialise mask. */
459
640
  ca_update_mask(ca);
460
641
 
461
642
  if ( TYPE(ridx1) == T_ARRAY ) {
462
- for (i=0; i<ca->ndim; i++) {
463
- k = NUM2SIZE(rb_ary_entry(ridx1, i));
464
- CA_CHECK_INDEX(k, ca->dim[i]);
465
- idx1[i] = k;
466
- }
643
+ elem_parse_idx_array(ca, ridx1, idx1);
467
644
  if ( ca->mask ) {
468
645
  ca_fetch_index(ca->mask, idx1, &m);
469
646
  }
470
647
  }
471
648
  else {
472
- k = NUM2SIZE(ridx1);
473
- CA_CHECK_INDEX(k, ca->elements);
474
- addr1 = k;
649
+ ca_size_t addr1 = elem_parse_idx_scalar(ca, ridx1);
475
650
  if ( ca->mask ) {
476
651
  ca_fetch_addr(ca->mask, addr1, &m);
477
652
  }
@@ -480,71 +655,265 @@ rb_ca_elem_test_masked (VALUE self, VALUE ridx1)
480
655
  return m ? Qtrue : Qfalse;
481
656
  }
482
657
 
483
- /* ----------------------------------------------------------------- */
658
+ /* `elem_mask(idx)` — sets the mask bit at cell `idx`. Unlike
659
+ * `a[idx] = UNDEF`, this preserves the underlying stored value, so a
660
+ * later `elem_unmask(idx)` restores it. */
484
661
 
485
- /* @overload incr_addr (addr)
662
+ VALUE
663
+ rb_ca_elem_mask (VALUE self, VALUE ridx)
664
+ {
665
+ CArray *ca;
666
+ ca_size_t idx[CA_RANK_MAX];
667
+
668
+ rb_ca_modify(self);
669
+
670
+ TypedData_Get_Struct(self, CArray, &carray_data_type, ca);
671
+
672
+ /* Super-hot path: entity. Materialise mask on first call (one-time per
673
+ array), then direct ptr write. */
674
+ if ( ca_is_entity(ca) ) {
675
+ ca_size_t addr;
676
+ if ( TYPE(ridx) == T_ARRAY ) {
677
+ elem_parse_idx_array(ca, ridx, idx);
678
+ addr = elem_entity_addr(ca, idx);
679
+ }
680
+ else {
681
+ addr = elem_parse_idx_scalar(ca, ridx);
682
+ }
683
+ if ( ! ca->mask ) {
684
+ ca_create_mask(ca); /* raises for value-array / mask-array */
685
+ }
686
+ ((boolean8_t *)ca->mask->ptr)[addr] = 1;
687
+ return self;
688
+ }
689
+
690
+ /* General path: view array. Walk parent chain + dispatch. */
691
+ {
692
+ boolean8_t one = 1;
693
+ ca_update_mask(ca);
694
+ if ( ! ca->mask ) {
695
+ ca_create_mask(ca);
696
+ }
697
+ if ( TYPE(ridx) == T_ARRAY ) {
698
+ elem_parse_idx_array(ca, ridx, idx);
699
+ ca_store_index(ca->mask, idx, &one);
700
+ }
701
+ else {
702
+ ca_size_t addr = elem_parse_idx_scalar(ca, ridx);
703
+ ca_store_addr(ca->mask, addr, &one);
704
+ }
705
+ }
706
+
707
+ return self;
708
+ }
486
709
 
487
- (Element) Increment the value at the element of addr.
488
- */
710
+ /* `elem_unmask(idx)` clears the mask bit at cell `idx`, leaving
711
+ * the stored data unchanged. No-op if `self` has no mask. */
489
712
 
490
- static VALUE
491
- rb_ca_incr_addr (volatile VALUE self, volatile VALUE raddr)
713
+ VALUE
714
+ rb_ca_elem_unmask (VALUE self, VALUE ridx)
492
715
  {
493
- CArray *ca, *ci;
494
- int64_t *q, *p;
495
- ca_size_t k, elements;
496
- ca_size_t i;
497
- boolean8_t *m;
716
+ CArray *ca;
717
+ ca_size_t idx[CA_RANK_MAX];
498
718
 
499
719
  rb_ca_modify(self);
500
720
 
501
- self = rb_ca_wrap_writable(self, INT2NUM(CA_INT64));
502
- raddr = rb_ca_wrap_readonly(raddr, INT2NUM(CA_INT64));
503
-
504
721
  TypedData_Get_Struct(self, CArray, &carray_data_type, ca);
505
- TypedData_Get_Struct(raddr, CArray, &carray_data_type, ci);
506
722
 
507
- ca_attach_n(2, ca, ci);
723
+ /* Super-hot path: entity. If no mask exists, nothing to clear. */
724
+ if ( ca_is_entity(ca) ) {
725
+ if ( ! ca->mask ) return self;
726
+ ca_size_t addr;
727
+ if ( TYPE(ridx) == T_ARRAY ) {
728
+ elem_parse_idx_array(ca, ridx, idx);
729
+ addr = elem_entity_addr(ca, idx);
730
+ }
731
+ else {
732
+ addr = elem_parse_idx_scalar(ca, ridx);
733
+ }
734
+ ((boolean8_t *)ca->mask->ptr)[addr] = 0;
735
+ return self;
736
+ }
737
+
738
+ /* General path: view array. */
739
+ {
740
+ boolean8_t zero = 0;
741
+ ca_update_mask(ca);
742
+ if ( ! ca->mask ) return self;
743
+ if ( TYPE(ridx) == T_ARRAY ) {
744
+ elem_parse_idx_array(ca, ridx, idx);
745
+ ca_store_index(ca->mask, idx, &zero);
746
+ }
747
+ else {
748
+ ca_size_t addr = elem_parse_idx_scalar(ca, ridx);
749
+ ca_store_addr(ca->mask, addr, &zero);
750
+ }
751
+ }
752
+
753
+ return self;
754
+ }
508
755
 
509
- q = (int64_t *) ca->ptr;
510
- p = (int64_t *) ci->ptr;
511
- m = ( ci->mask ) ? (boolean8_t *) ci->mask->ptr : NULL;
756
+ /* `elem_min(idx, v)` — updates the cell at `idx` to `min(self[idx],
757
+ * v)` in place. Masked cells are skipped. NaN follows the `fmin`
758
+ * rule (NaN is treated as missing: `min(NaN, v)` returns `v`,
759
+ * `min(x, NaN)` returns `x`). Raises for non-numeric data_type. */
760
+
761
+ #define ELEM_MINMAX_BODY(OP) do { \
762
+ if ( ca_is_entity(ca) ) { \
763
+ ca_size_t addr; \
764
+ if ( TYPE(ridx) == T_ARRAY ) { \
765
+ elem_parse_idx_array(ca, ridx, idx); \
766
+ addr = elem_entity_addr(ca, idx); \
767
+ } \
768
+ else { \
769
+ addr = elem_parse_idx_scalar(ca, ridx); \
770
+ } \
771
+ if ( ca->mask && ((boolean8_t *)ca->mask->ptr)[addr] ) return self; \
772
+ void *p = ca->ptr + ca->bytes * addr; \
773
+ if ( RB_FLOAT_TYPE_P(rval) ) { \
774
+ double v = RFLOAT_VALUE(rval); \
775
+ switch ( ca->data_type ) { \
776
+ case CA_FLOAT64: { double *q=(double *)p; if (v OP *q || *q != *q) *q = v; return self; } \
777
+ case CA_FLOAT32: { float *q=(float *)p; float vf=(float)v; if (vf OP *q || *q != *q) *q = vf; return self; } \
778
+ case CA_INT64: { int64_t *q=(int64_t *)p; if ((int64_t)v OP *q) *q = (int64_t)v; return self; } \
779
+ case CA_INT32: { int32_t *q=(int32_t *)p; if ((int32_t)v OP *q) *q = (int32_t)v; return self; } \
780
+ case CA_INT16: { int16_t *q=(int16_t *)p; if ((int16_t)v OP *q) *q = (int16_t)v; return self; } \
781
+ case CA_INT8: { int8_t *q=(int8_t *)p; if ((int8_t )v OP *q) *q = (int8_t )v; return self; } \
782
+ case CA_UINT64: { uint64_t *q=(uint64_t*)p; if ((uint64_t)v OP *q) *q = (uint64_t)v; return self; } \
783
+ case CA_UINT32: { uint32_t *q=(uint32_t*)p; if ((uint32_t)v OP *q) *q = (uint32_t)v; return self; } \
784
+ case CA_UINT16: { uint16_t *q=(uint16_t*)p; if ((uint16_t)v OP *q) *q = (uint16_t)v; return self; } \
785
+ case CA_UINT8: { uint8_t *q=(uint8_t *)p; if ((uint8_t )v OP *q) *q = (uint8_t )v; return self; } \
786
+ } \
787
+ } \
788
+ else if ( FIXNUM_P(rval) ) { \
789
+ long v = FIX2LONG(rval); \
790
+ switch ( ca->data_type ) { \
791
+ case CA_FLOAT64: { double *q=(double *)p; double vd=(double)v; if (vd OP *q || *q != *q) *q = vd; return self; } \
792
+ case CA_FLOAT32: { float *q=(float *)p; float vf=(float )v; if (vf OP *q || *q != *q) *q = vf; return self; } \
793
+ case CA_INT64: { int64_t *q=(int64_t *)p; if ((int64_t)v OP *q) *q = (int64_t)v; return self; } \
794
+ case CA_INT32: { int32_t *q=(int32_t *)p; if ((int32_t)v OP *q) *q = (int32_t)v; return self; } \
795
+ case CA_INT16: { int16_t *q=(int16_t *)p; if ((int16_t)v OP *q) *q = (int16_t)v; return self; } \
796
+ case CA_INT8: { int8_t *q=(int8_t *)p; if ((int8_t )v OP *q) *q = (int8_t )v; return self; } \
797
+ case CA_UINT64: { uint64_t *q=(uint64_t*)p; if ((uint64_t)v OP *q) *q = (uint64_t)v; return self; } \
798
+ case CA_UINT32: { uint32_t *q=(uint32_t*)p; if ((uint32_t)v OP *q) *q = (uint32_t)v; return self; } \
799
+ case CA_UINT16: { uint16_t *q=(uint16_t*)p; if ((uint16_t)v OP *q) *q = (uint16_t)v; return self; } \
800
+ case CA_UINT8: { uint8_t *q=(uint8_t *)p; if ((uint8_t )v OP *q) *q = (uint8_t )v; return self; } \
801
+ } \
802
+ } \
803
+ } \
804
+ } while (0)
512
805
 
513
- elements = ca->elements;
806
+ VALUE
807
+ rb_ca_elem_min (VALUE self, VALUE ridx, VALUE rval)
808
+ {
809
+ CArray *ca;
810
+ ca_size_t idx[CA_RANK_MAX];
514
811
 
515
- if ( m ) {
516
- #ifdef _OPENMP
517
- #pragma omp parallel for
518
- #endif
519
- for (i=0; i<ci->elements; i++) {
520
- if ( ! *(m+i) ) {
521
- k = *(p+i);
522
- CA_CHECK_INDEX(k, elements);
523
- *(q + k) += 1;
524
- }
525
- }
812
+ rb_ca_modify(self);
813
+
814
+ TypedData_Get_Struct(self, CArray, &carray_data_type, ca);
815
+
816
+ if ( ! ca_is_numeric_type(ca) ) {
817
+ rb_raise(rb_eCADataTypeError,
818
+ "elem_min requires a numeric array");
526
819
  }
527
- else {
528
- #ifdef _OPENMP
529
- #pragma omp parallel for
530
- #endif
531
- for (i=0; i<ci->elements; i++) {
532
- k = *(p+i);
533
- CA_CHECK_INDEX(k, elements);
534
- *(q + k) += 1;
820
+
821
+ /* Super-hot path: entity + numeric dtype + Fixnum/Float rval. */
822
+ ELEM_MINMAX_BODY(<);
823
+
824
+ /* General path: view / Bignum-Rational-Complex rval / fall-through.
825
+ fetch, compare via Ruby, store back. */
826
+ {
827
+ VALUE cur;
828
+ boolean8_t m = 0;
829
+ ca_size_t addr;
830
+ int from_array = (TYPE(ridx) == T_ARRAY);
831
+
832
+ if ( from_array ) {
833
+ elem_parse_idx_array(ca, ridx, idx);
834
+ }
835
+ else {
836
+ addr = elem_parse_idx_scalar(ca, ridx);
837
+ }
838
+
839
+ /* Probe mask (view may need ca_update_mask). */
840
+ if ( ! ca->mask && ! ca_is_entity(ca) ) ca_update_mask(ca);
841
+ if ( ca->mask ) {
842
+ if ( from_array ) ca_fetch_index(ca->mask, idx, &m);
843
+ else ca_fetch_addr(ca->mask, addr, &m);
844
+ if ( m ) return self;
845
+ }
846
+
847
+ cur = from_array ? rb_ca_fetch_index(self, idx) : rb_ca_fetch_addr(self, addr);
848
+ if ( cur == CA_UNDEF ) return self;
849
+ if ( RTEST(rb_funcall(rval, rb_intern("<"), 1, cur)) ) {
850
+ if ( from_array ) rb_ca_store_index(self, idx, rval);
851
+ else rb_ca_store_addr(self, addr, rval);
535
852
  }
536
853
  }
537
854
 
538
- ca_sync(ca);
539
- ca_detach_n(2, ca, ci);
855
+ return self;
856
+ }
857
+
858
+ /* `elem_max(idx, v)` — updates the cell at `idx` to `max(self[idx],
859
+ * v)` in place. Masked cells are skipped. NaN follows the `fmax`
860
+ * rule. Raises for non-numeric data_type. */
861
+
862
+ VALUE
863
+ rb_ca_elem_max (VALUE self, VALUE ridx, VALUE rval)
864
+ {
865
+ CArray *ca;
866
+ ca_size_t idx[CA_RANK_MAX];
867
+
868
+ rb_ca_modify(self);
869
+
870
+ TypedData_Get_Struct(self, CArray, &carray_data_type, ca);
871
+
872
+ if ( ! ca_is_numeric_type(ca) ) {
873
+ rb_raise(rb_eCADataTypeError,
874
+ "elem_max requires a numeric array");
875
+ }
876
+
877
+ ELEM_MINMAX_BODY(>);
878
+
879
+ /* General path: view / Bignum-Rational-Complex rval. */
880
+ {
881
+ VALUE cur;
882
+ boolean8_t m = 0;
883
+ ca_size_t addr;
884
+ int from_array = (TYPE(ridx) == T_ARRAY);
885
+
886
+ if ( from_array ) {
887
+ elem_parse_idx_array(ca, ridx, idx);
888
+ }
889
+ else {
890
+ addr = elem_parse_idx_scalar(ca, ridx);
891
+ }
892
+
893
+ if ( ! ca->mask && ! ca_is_entity(ca) ) ca_update_mask(ca);
894
+ if ( ca->mask ) {
895
+ if ( from_array ) ca_fetch_index(ca->mask, idx, &m);
896
+ else ca_fetch_addr(ca->mask, addr, &m);
897
+ if ( m ) return self;
898
+ }
540
899
 
541
- return Qnil;
900
+ cur = from_array ? rb_ca_fetch_index(self, idx) : rb_ca_fetch_addr(self, addr);
901
+ if ( cur == CA_UNDEF ) return self;
902
+ if ( RTEST(rb_funcall(rval, rb_intern(">"), 1, cur)) ) {
903
+ if ( from_array ) rb_ca_store_index(self, idx, rval);
904
+ else rb_ca_store_addr(self, addr, rval);
905
+ }
906
+ }
907
+
908
+ return self;
542
909
  }
543
910
 
911
+ #undef ELEM_MINMAX_BODY
912
+
544
913
  /* ----------------------------------------------------------------- */
545
914
 
546
915
  void
547
- Init_carray_element ()
916
+ Init_carray_element (void)
548
917
  {
549
918
  rb_define_method(rb_cCArray, "elem_swap", rb_ca_elem_swap, 2);
550
919
  rb_define_method(rb_cCArray, "elem_copy", rb_ca_elem_copy, 2);
@@ -555,7 +924,10 @@ Init_carray_element ()
555
924
  rb_define_method(rb_cCArray, "elem_decr", rb_ca_elem_decr, 1);
556
925
 
557
926
  rb_define_method(rb_cCArray, "elem_masked?", rb_ca_elem_test_masked, 1);
927
+ rb_define_method(rb_cCArray, "elem_mask", rb_ca_elem_mask, 1);
928
+ rb_define_method(rb_cCArray, "elem_unmask", rb_ca_elem_unmask, 1);
558
929
 
559
- rb_define_method(rb_cCArray, "incr_addr", rb_ca_incr_addr, 1);
930
+ rb_define_method(rb_cCArray, "elem_min", rb_ca_elem_min, 2);
931
+ rb_define_method(rb_cCArray, "elem_max", rb_ca_elem_max, 2);
560
932
  }
561
933