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_mask.c CHANGED
@@ -1,16 +1,253 @@
1
1
  /* ---------------------------------------------------------------------------
2
2
 
3
- carray_mask.c
4
-
5
- This file is part of Ruby/CArray extension library.
6
-
7
- Copyright (C) 2005-2020 Hiroki Motoyoshi
3
+ Mask machinery shared by every CArray subtype: has_mask / any-masked /
4
+ all-masked scans, mask creation and propagation across the view chain,
5
+ the mask SET family (mask_eq / mask_invalid / mask_where / strip_mask),
6
+ count_masked, and the word-bulk boolean bit-op fast paths.
8
7
 
9
8
  ---------------------------------------------------------------------------- */
10
9
 
11
10
  #include <stdarg.h>
11
+ #include <stdint.h>
12
12
  #include "ruby.h"
13
13
  #include "carray.h"
14
+ #include "carray_internal.h" /* ca_lazy_arena_* */
15
+ #include "ca_obj_face.h" /* CA_FACE_LIFT_IF_FACE for the mask-lift path */
16
+
17
+ /* ============================================================================
18
+ * Word-bulk mask substrate helpers.
19
+ *
20
+ * CAREFUL: every helper here relies on the invariant that a mask / bool
21
+ * byte is in {0, 1} throughout all production code paths (OBJ2BOOL strict,
22
+ * integer->bool cast raises on out-of-range, comparison output is
23
+ * C-semantic 0/1, the mask combiner forces *ma=1, and an external mask via
24
+ * ca_wrap_readonly normalises). Breaking it corrupts the word-level ops.
25
+ *
26
+ * Word loop: uint64_t (8 byte/op) + tail byte fallback (n % 8).
27
+ * NOT uses an LSB XOR pattern (w ^ 0x0101...01ULL) to preserve the
28
+ * invariant (byte ~ would produce 0xFE, violating {0,1}).
29
+ *
30
+ * Helpers are static inline (file-local), inlinable into callers within
31
+ * this translation unit (overlay_n, is_any_masked, is_all_masked, etc.).
32
+ * extern + header extraction can wait until a cross-file need arises.
33
+ * Placed at the top of the TU so the inline body is visible at every call
34
+ * site.
35
+ * ============================================================================ */
36
+
37
+ #define CA_MASK_WORD_BYTES 8
38
+ #define CA_MASK_WORD_LSB 0x0101010101010101ULL
39
+
40
+ /* ma[i] |= ms[i] for i in [0, n) -- used by ca_copy_mask_overlay_n */
41
+ static inline void
42
+ ca_mask_word_or (boolean8_t *ma, const boolean8_t *ms, ca_size_t n)
43
+ {
44
+ ca_size_t nw = n / CA_MASK_WORD_BYTES;
45
+ ca_size_t i;
46
+ uint64_t *wa = (uint64_t *) ma;
47
+ const uint64_t *ws = (const uint64_t *) ms;
48
+ for (i = 0; i < nw; i++) {
49
+ wa[i] |= ws[i];
50
+ }
51
+ for (i = nw * CA_MASK_WORD_BYTES; i < n; i++) {
52
+ ma[i] |= ms[i];
53
+ }
54
+ }
55
+
56
+ /* md[i] = ma[i] & mb[i] for i in [0, n) */
57
+ static inline void
58
+ ca_mask_word_and (boolean8_t *md,
59
+ const boolean8_t *ma,
60
+ const boolean8_t *mb,
61
+ ca_size_t n)
62
+ {
63
+ ca_size_t nw = n / CA_MASK_WORD_BYTES;
64
+ ca_size_t i;
65
+ uint64_t *wd = (uint64_t *) md;
66
+ const uint64_t *wa = (const uint64_t *) ma;
67
+ const uint64_t *wb = (const uint64_t *) mb;
68
+ for (i = 0; i < nw; i++) {
69
+ wd[i] = wa[i] & wb[i];
70
+ }
71
+ for (i = nw * CA_MASK_WORD_BYTES; i < n; i++) {
72
+ md[i] = ma[i] & mb[i];
73
+ }
74
+ }
75
+
76
+ /* md[i] = ma[i] ^ mb[i] for i in [0, n) */
77
+ static inline void
78
+ ca_mask_word_xor (boolean8_t *md,
79
+ const boolean8_t *ma,
80
+ const boolean8_t *mb,
81
+ ca_size_t n)
82
+ {
83
+ ca_size_t nw = n / CA_MASK_WORD_BYTES;
84
+ ca_size_t i;
85
+ uint64_t *wd = (uint64_t *) md;
86
+ const uint64_t *wa = (const uint64_t *) ma;
87
+ const uint64_t *wb = (const uint64_t *) mb;
88
+ for (i = 0; i < nw; i++) {
89
+ wd[i] = wa[i] ^ wb[i];
90
+ }
91
+ for (i = nw * CA_MASK_WORD_BYTES; i < n; i++) {
92
+ md[i] = ma[i] ^ mb[i];
93
+ }
94
+ }
95
+
96
+ /* md[i] = 1 - ms[i] (LSB XOR pattern; preserves byte ∈ {0,1} invariant) */
97
+ static inline void
98
+ ca_mask_word_not (boolean8_t *md, const boolean8_t *ms, ca_size_t n)
99
+ {
100
+ ca_size_t nw = n / CA_MASK_WORD_BYTES;
101
+ ca_size_t i;
102
+ uint64_t *wd = (uint64_t *) md;
103
+ const uint64_t *ws = (const uint64_t *) ms;
104
+ for (i = 0; i < nw; i++) {
105
+ wd[i] = ws[i] ^ CA_MASK_WORD_LSB;
106
+ }
107
+ for (i = nw * CA_MASK_WORD_BYTES; i < n; i++) {
108
+ md[i] = ms[i] ^ (boolean8_t) 1;
109
+ }
110
+ }
111
+
112
+ /* 1 if any byte in [0, n) is non-zero, 0 otherwise (with early exit).
113
+ Consumer: ca_is_any_masked / any_masked? scan. */
114
+ static inline int
115
+ ca_mask_word_any (const boolean8_t *m, ca_size_t n)
116
+ {
117
+ ca_size_t nw = n / CA_MASK_WORD_BYTES;
118
+ ca_size_t i;
119
+ const uint64_t *w = (const uint64_t *) m;
120
+ for (i = 0; i < nw; i++) {
121
+ if ( w[i] != 0 ) return 1;
122
+ }
123
+ for (i = nw * CA_MASK_WORD_BYTES; i < n; i++) {
124
+ if ( m[i] ) return 1;
125
+ }
126
+ return 0;
127
+ }
128
+
129
+ /* 1 if every byte in [0, n) is non-zero, 0 otherwise (with early exit).
130
+ The {0,1} invariant makes a word check (w == LSB pattern) sufficient.
131
+ Consumer: ca_is_all_masked / all_masked? scan. */
132
+ static inline int
133
+ ca_mask_word_all_set (const boolean8_t *m, ca_size_t n)
134
+ {
135
+ ca_size_t nw = n / CA_MASK_WORD_BYTES;
136
+ ca_size_t i;
137
+ const uint64_t *w = (const uint64_t *) m;
138
+ for (i = 0; i < nw; i++) {
139
+ if ( w[i] != CA_MASK_WORD_LSB ) return 0;
140
+ }
141
+ for (i = nw * CA_MASK_WORD_BYTES; i < n; i++) {
142
+ if ( ! m[i] ) return 0;
143
+ }
144
+ return 1;
145
+ }
146
+
147
+ /* Count of set bytes in [0, n). Under the {0,1} invariant, a plain accumulate of
148
+ uint8 lanes (no popcount needed); compilers lower this to a horizontal
149
+ byte-sum (psadbw / uaddlv). Consumer: ca_count_masked. */
150
+ static inline ca_size_t
151
+ ca_mask_word_count (const boolean8_t *m, ca_size_t n)
152
+ {
153
+ ca_size_t i, count = 0;
154
+ for (i = 0; i < n; i++) {
155
+ count += m[i];
156
+ }
157
+ return count;
158
+ }
159
+
160
+ /* ============================================================================
161
+ * (End of the word-bulk helpers. Smoke surface for these helpers is at
162
+ * the bottom of this file, just before Init_carray_mask.)
163
+ * ============================================================================ */
164
+
165
+ /* ----------------------------------------------------------------------------
166
+ * Virtual-mask block scan (non-materialise reduction)
167
+ *
168
+ * When a mask is a virtual view (e.g. CAStackMask composed by
169
+ * ca_stack_func_create_mask), ca_attach
170
+ * materialises the WHOLE composed mask before the word scan — which defeats
171
+ * the early-exit of any/all and forces peak O(n) memory. Instead, drive
172
+ * ca_xfer_stride over the mask in row-slab blocks pulled into one arena
173
+ * scratch (the same mechanism CAMonOp uses to consume a virtual source
174
+ * without materialising it). Peak memory = one block; ANY/ALL short-circuit
175
+ * on the first decisive block (a masked cell in block 0 of a K-deep CAStack
176
+ * returns after pulling 1/K of the data, never touching the rest).
177
+ *
178
+ * Only used for NON-alias masks (cheap/entity masks keep the O(1)-alias
179
+ * attach + direct word scan above). Blocking is along axis 0; for arrays
180
+ * with a very wide inner product (> CA_MASK_BLOCK_BYTES) the block degrades
181
+ * to a single row, which is still O(row) peak rather than O(n).
182
+ * ---------------------------------------------------------------------------- */
183
+
184
+ #define CA_MASK_BLOCK_BYTES (32 * 1024) /* L1-resident scratch budget */
185
+
186
+ enum { CA_MASK_SCAN_ANY, CA_MASK_SCAN_ALL, CA_MASK_SCAN_COUNT };
187
+
188
+ static ca_size_t
189
+ ca_mask_scan_virtual (CArray *mask, int op)
190
+ {
191
+ int8_t ndim = mask->ndim;
192
+ ca_size_t outer = mask->dim[0];
193
+ ca_size_t inner = 1;
194
+ ca_size_t native[CA_RANK_MAX];
195
+ ca_size_t rows, off, result = 0;
196
+ boolean8_t *scratch;
197
+ int8_t k;
198
+
199
+ for (k = 1; k < ndim; k++) {
200
+ inner *= mask->dim[k];
201
+ }
202
+
203
+ /* compact row-major byte strides (boolean = 1 byte/cell); native[0] == inner
204
+ so passing these as the destination layout writes each block contiguously
205
+ into scratch as [nrow * inner] cells. */
206
+ {
207
+ ca_size_t s = 1;
208
+ for (k = ndim - 1; k >= 0; k--) { native[k] = s; s *= mask->dim[k]; }
209
+ }
210
+
211
+ rows = (inner > 0) ? (CA_MASK_BLOCK_BYTES / inner) : CA_MASK_BLOCK_BYTES;
212
+ if ( rows < 1 ) rows = 1;
213
+ if ( rows > outer ) rows = outer;
214
+
215
+ if ( op == CA_MASK_SCAN_ALL ) {
216
+ result = 1; /* vacuously all-set until a non-set cell is found */
217
+ }
218
+
219
+ ca_lazy_arena_enter();
220
+ scratch = (boolean8_t *) ca_lazy_arena_acquire(rows * inner);
221
+
222
+ for (off = 0; off < outer; off += rows) {
223
+ ca_size_t nrow = (outer - off < rows) ? (outer - off) : rows;
224
+ ca_size_t n = nrow * inner;
225
+ ca_size_t starts[CA_RANK_MAX] = {0};
226
+ ca_size_t counts[CA_RANK_MAX];
227
+
228
+ starts[0] = off;
229
+ counts[0] = nrow;
230
+ for (k = 1; k < ndim; k++) {
231
+ counts[k] = mask->dim[k];
232
+ }
233
+
234
+ ca_xfer_stride(mask, starts, counts, native, scratch, CA_XFER_GET);
235
+
236
+ if ( op == CA_MASK_SCAN_ANY ) {
237
+ if ( ca_mask_word_any(scratch, n) ) { result = 1; break; }
238
+ }
239
+ else if ( op == CA_MASK_SCAN_ALL ) {
240
+ if ( ! ca_mask_word_all_set(scratch, n) ) { result = 0; break; }
241
+ }
242
+ else { /* CA_MASK_SCAN_COUNT — no early exit */
243
+ result += ca_mask_word_count(scratch, n);
244
+ }
245
+ }
246
+
247
+ ca_lazy_arena_release(scratch);
248
+ ca_lazy_arena_exit();
249
+ return result;
250
+ }
14
251
 
15
252
  boolean8_t *
16
253
  ca_mask_ptr (void *ap)
@@ -36,13 +273,32 @@ ca_has_mask (void *ap)
36
273
  }
37
274
  else if ( ca_is_caobject(ca) ) { /* CAObject */
38
275
  CAObject *co = (CAObject *) ca;
276
+ if ( ca_is_face(ca) ) { /* Face: the mask follows the parent */
277
+ if ( co->parent && ca_has_mask(co->parent) ) {
278
+ ca_create_mask(ca);
279
+ return 1;
280
+ }
281
+ return 0;
282
+ }
39
283
  if ( rb_obj_respond_to(co->self, rb_intern("mask_created?"), Qtrue) ) {
40
284
  return RTEST(rb_funcall(co->self, rb_intern("mask_created?"), 0));
41
285
  }
42
286
  return ( ca->mask != NULL ) ? 1 : 0;
43
287
  }
288
+ else if ( ca_test_flag(ca, CA_FLAG_MULTI_PARENTS) ) {
289
+ /* multi-parent view: masked iff ANY parent is (union of masked cells). */
290
+ CAMultiParent *mp = (CAMultiParent *) ca;
291
+ int32_t k;
292
+ for ( k = 0; k < mp->n_parents; k++ ) {
293
+ if ( ca_has_mask(mp->parents[k]) ) {
294
+ ca_create_mask(ca);
295
+ return 1;
296
+ }
297
+ }
298
+ return 0;
299
+ }
44
300
  else {
45
- CAVirtual *cr = (CAVirtual *) ca; /* virtual array, check parent array */
301
+ CAView *cr = (CAView *) ca; /* view array, check parent array */
46
302
  if ( ca_has_mask(cr->parent) ) {
47
303
  ca_create_mask(ca);
48
304
  return 1;
@@ -57,21 +313,26 @@ int
57
313
  ca_is_any_masked (void *ap)
58
314
  {
59
315
  CArray *ca = (CArray *) ap;
60
- boolean8_t *m;
61
- ca_size_t i;
62
316
  int flag = 0;
63
317
 
64
318
  ca_update_mask(ca);
65
319
  if ( ca->mask ) {
66
- ca_attach(ca->mask);
67
- m = (boolean8_t *) ca->mask->ptr;
68
- for (i=0; i<ca->elements; i++) {
69
- if ( *m++ ) {
70
- flag = 1;
71
- break;
72
- }
320
+ if ( ca_attach_is_alias(ca->mask) ) {
321
+ /* cheap (entity / contiguous-alias) mask: O(1) alias attach +
322
+ word-level any with early exit. A sparse mask returns on the first
323
+ non-zero word (the typical "all zero, no mask actually set" hot
324
+ pattern). An O(n) scan cache is not viable here: the mask is
325
+ dynamic and a view chain can introduce a mask mid-chain. */
326
+ ca_attach(ca->mask);
327
+ flag = ca_mask_word_any((boolean8_t *) ca->mask->ptr, ca->elements);
328
+ ca_detach(ca->mask);
329
+ }
330
+ else {
331
+ /* virtual mask (e.g. CAStackMask): block-loop the composed mask so it
332
+ is never materialised whole, and short-circuit on the first masked
333
+ block. See ca_mask_scan_virtual. */
334
+ flag = (int) ca_mask_scan_virtual(ca->mask, CA_MASK_SCAN_ANY);
73
335
  }
74
- ca_detach(ca->mask);
75
336
  }
76
337
 
77
338
  return flag;
@@ -81,26 +342,25 @@ int
81
342
  ca_is_all_masked (void *ap)
82
343
  {
83
344
  CArray *ca = (CArray *) ap;
84
- boolean8_t *m;
85
- ca_size_t i;
86
- int flag;
345
+ int flag = 0;
87
346
 
88
347
  ca_update_mask(ca);
89
348
  if ( ca->mask ) {
90
- flag = 1;
91
- ca_attach(ca->mask);
92
- m = (boolean8_t *) ca->mask->ptr;
93
- for (i=0; i<ca->elements; i++) {
94
- if ( ! *m++ ) {
95
- flag = 0;
96
- break;
97
- }
349
+ if ( ca_attach_is_alias(ca->mask) ) {
350
+ /* cheap mask: O(1) alias attach + word-level all_set with early exit.
351
+ The word check uses (w == 0x0101...01ULL) under the {0,1}
352
+ invariant; returns on the first non-fully-set word. */
353
+ ca_attach(ca->mask);
354
+ flag = ca_mask_word_all_set((boolean8_t *) ca->mask->ptr, ca->elements);
355
+ ca_detach(ca->mask);
356
+ }
357
+ else {
358
+ /* virtual mask: block-loop + short-circuit on first non-fully-set block. */
359
+ flag = (int) ca_mask_scan_virtual(ca->mask, CA_MASK_SCAN_ALL);
98
360
  }
99
- ca_detach(ca->mask);
100
- return flag;
101
- } else {
102
- return 0;
103
361
  }
362
+
363
+ return flag;
104
364
  }
105
365
 
106
366
  /* create mask array if array has mask but has not mask array */
@@ -132,11 +392,11 @@ ca_create_mask (void *ap)
132
392
  if ( ! ca->mask ) {
133
393
  ca_func[ca->obj_type].create_mask(ca);
134
394
  ca_set_flag(ca->mask, CA_FLAG_MASK_ARRAY); /* set array as mask array */
135
- if ( ca_is_virtual(ca) ) {
136
- if ( CAVIRTUAL(ca)->attach ) {
395
+ if ( ca_is_view(ca) ) {
396
+ if ( CAVIEW(ca)->attach ) {
137
397
  ca_attach(ca->mask);
138
- if ( ca_is_virtual(ca->mask) ) {
139
- CAVIRTUAL(ca->mask)->attach = CAVIRTUAL(ca)->attach;
398
+ if ( ca_is_view(ca->mask) ) {
399
+ CAVIEW(ca->mask)->attach = CAVIEW(ca)->attach;
140
400
  }
141
401
  }
142
402
  }
@@ -190,7 +450,6 @@ ca_copy_mask_overlay_n (void *ap, ca_size_t elements, int n, CArray **slist)
190
450
  CArray *cs;
191
451
  boolean8_t *ma, *ms;
192
452
  int i, some_has_mask = 0;
193
- ca_size_t j;
194
453
 
195
454
  for (i=0; i<n; i++) {
196
455
  if ( slist[i] && ca_has_mask(slist[i]) ) {
@@ -223,21 +482,17 @@ ca_copy_mask_overlay_n (void *ap, ca_size_t elements, int n, CArray **slist)
223
482
  ca_attach(cs->mask);
224
483
  ma = (boolean8_t *) ca->mask->ptr;
225
484
  ms = (boolean8_t *) cs->mask->ptr;
485
+ /* scalar masked source -> broadcast to all (memset); array source
486
+ -> pure word OR (ca_mask_word_or). A defensive `if (*ms)` is
487
+ unnecessary: under the {0,1} invariant, `*ma |= *ms` equals
488
+ `if (*ms) *ma = 1`. Post-condition: the mask byte stays in {0,1}. */
226
489
  if ( ca_is_scalar(cs) ) {
227
490
  if ( *ms ) {
228
- for (j=0; j<elements; j++) {
229
- *ma = 1;
230
- ma++;
231
- }
491
+ memset(ma, 1, elements);
232
492
  }
233
493
  }
234
494
  else {
235
- for (j=0; j<elements; j++) {
236
- if ( *ms ) {
237
- *ma = 1;
238
- }
239
- ma++; ms++;
240
- }
495
+ ca_mask_word_or(ma, ms, elements);
241
496
  }
242
497
  ca_detach(cs->mask);
243
498
  }
@@ -254,7 +509,7 @@ ca_copy_mask_overlay (void *ap, ca_size_t elements, int n, ...)
254
509
  va_list args;
255
510
  int i;
256
511
 
257
- slist = malloc_with_check(sizeof(CArray *)*n);
512
+ slist = xmalloc(sizeof(CArray *)*n);
258
513
  va_start(args, n);
259
514
  for (i=0; i<n; i++) {
260
515
  slist[i] = va_arg(args, CArray *);
@@ -263,7 +518,7 @@ ca_copy_mask_overlay (void *ap, ca_size_t elements, int n, ...)
263
518
 
264
519
  ca_copy_mask_overlay_n(ca, elements, n, slist);
265
520
 
266
- free(slist);
521
+ xfree(slist);
267
522
  }
268
523
 
269
524
  void
@@ -288,7 +543,7 @@ ca_copy_mask_overwrite (void *ap, ca_size_t elements, int n, ...)
288
543
  va_list args;
289
544
  int i;
290
545
 
291
- slist = malloc_with_check(sizeof(CArray*)*n);
546
+ slist = xmalloc(sizeof(CArray*)*n);
292
547
  va_start(args, n);
293
548
  for (i=0; i<n; i++) {
294
549
  slist[i] = va_arg(args, CArray*);
@@ -303,7 +558,7 @@ ca_copy_mask_overwrite (void *ap, ca_size_t elements, int n, ...)
303
558
 
304
559
  ca_copy_mask_overlay_n(ca, elements, n, slist);
305
560
 
306
- free(slist);
561
+ xfree(slist);
307
562
  }
308
563
 
309
564
  void
@@ -320,19 +575,23 @@ ca_count_masked (void *ap)
320
575
  {
321
576
  CArray *ca = (CArray *) ap;
322
577
  boolean8_t *m;
323
- ca_size_t i, count = 0;
578
+ ca_size_t count = 0;
324
579
 
325
580
  ca_update_mask(ca);
326
581
 
327
582
  if ( ca->mask ) {
328
- ca_attach(ca->mask);
329
- m = (boolean8_t *) ca->mask->ptr;
330
- for (i=0; i<ca->elements; i++) {
331
- if ( *m++ ) {
332
- count++;
333
- }
583
+ if ( ca_attach_is_alias(ca->mask) ) {
584
+ /* cheap mask: O(1) alias attach + word-bulk count. */
585
+ ca_attach(ca->mask);
586
+ m = (boolean8_t *) ca->mask->ptr;
587
+ count = ca_mask_word_count(m, ca->elements);
588
+ ca_detach(ca->mask);
589
+ }
590
+ else {
591
+ /* virtual mask: block-loop the composed mask (peak O(block), no full
592
+ materialise). COUNT has no early exit but still avoids O(n) peak. */
593
+ count = ca_mask_scan_virtual(ca->mask, CA_MASK_SCAN_COUNT);
334
594
  }
335
- ca_detach(ca->mask);
336
595
  }
337
596
 
338
597
  return count;
@@ -345,31 +604,30 @@ ca_count_not_masked (void *ap)
345
604
  return ca->elements - ca_count_masked(ca);
346
605
  }
347
606
 
348
- #define proc_fill_bang(type) \
607
+ /* Scalar-fill every masked cell with fill_value, then clear its mask bit.
608
+ This depends only on the cell BYTE WIDTH, not the data type (the work is
609
+ a width-sized copy), so a single width switch covers every numeric /
610
+ complex / object data type; the default arm handles fixlen and any other
611
+ width via memcpy. The typed arms let the compiler emit a plain store. */
612
+ #define proc_fill_bang_width(T) \
349
613
  { \
350
- ca_size_t i; \
351
- type *p = (type *)ca->ptr; \
352
- type v = *(type *)fill_value; \
614
+ ca_size_t i; \
615
+ T *p = (T *)ca->ptr; \
616
+ T v = *(T *)fill_value; \
353
617
  boolean8_t *m = (boolean8_t *) ca->mask->ptr; \
354
618
  for (i=0; i<ca->elements; i++) { \
355
- if ( *m ) { \
356
- *p = v; \
357
- *m = 0; \
358
- } \
619
+ if ( *m ) { *p = v; *m = 0; } \
359
620
  m++; p++; \
360
621
  } \
361
622
  }
362
623
 
363
- #define proc_fill_bang_fixlen() \
624
+ #define proc_fill_bang_bytes() \
364
625
  { \
365
- ca_size_t i; \
626
+ ca_size_t i; \
366
627
  char *p = ca->ptr; \
367
628
  boolean8_t *m = (boolean8_t *) ca->mask->ptr; \
368
629
  for (i=0; i<ca->elements; i++) { \
369
- if ( *m ) { \
370
- memcpy(p, fill_value, ca->bytes); \
371
- *m = 0; \
372
- } \
630
+ if ( *m ) { memcpy(p, fill_value, ca->bytes); *m = 0; } \
373
631
  m++; p+=ca->bytes; \
374
632
  } \
375
633
  }
@@ -388,27 +646,12 @@ ca_unmask (void *ap, char *fill_value)
388
646
  else {
389
647
  ca_attach(ca);
390
648
 
391
- switch ( ca->data_type ) {
392
- case CA_FIXLEN: proc_fill_bang_fixlen(); break;
393
- case CA_BOOLEAN:
394
- case CA_INT8:
395
- case CA_UINT8: proc_fill_bang(int8_t); break;
396
- case CA_INT16:
397
- case CA_UINT16: proc_fill_bang(int16_t); break;
398
- case CA_INT32:
399
- case CA_UINT32:
400
- case CA_FLOAT32: proc_fill_bang(int32_t); break;
401
- case CA_INT64:
402
- case CA_UINT64:
403
- case CA_FLOAT64: proc_fill_bang(float64_t); break;
404
- case CA_FLOAT128: proc_fill_bang(float128_t); break;
405
- #ifdef HAVE_COMPLEX_H
406
- case CA_CMPLX64: proc_fill_bang(cmplx64_t); break;
407
- case CA_CMPLX128: proc_fill_bang(cmplx128_t); break;
408
- case CA_CMPLX256: proc_fill_bang(cmplx256_t); break;
409
- #endif
410
- case CA_OBJECT: proc_fill_bang(VALUE); break;
411
- default: rb_bug("array has an unknown data type");
649
+ switch ( ca->bytes ) {
650
+ case 1: proc_fill_bang_width(uint8_t); break;
651
+ case 2: proc_fill_bang_width(uint16_t); break;
652
+ case 4: proc_fill_bang_width(uint32_t); break;
653
+ case 8: proc_fill_bang_width(uint64_t); break;
654
+ default: proc_fill_bang_bytes(); break; /* cmplx128 (16) + fixlen */
412
655
  }
413
656
 
414
657
  ca_sync(ca);
@@ -450,7 +693,6 @@ ca_invert_mask (void *ap)
450
693
  {
451
694
  CArray *ca = (CArray *) ap;
452
695
  boolean8_t *m;
453
- ca_size_t i;
454
696
 
455
697
  ca_update_mask(ca);
456
698
 
@@ -460,10 +702,7 @@ ca_invert_mask (void *ap)
460
702
 
461
703
  ca_attach(ca->mask);
462
704
  m = (boolean8_t *) ca->mask->ptr;
463
- for (i=0; i<ca->elements; i++) {
464
- *m ^= 1;
465
- m++;
466
- }
705
+ ca_mask_word_not(m, m, ca->elements); /* in-place 1 - m, LSB-XOR word loop */
467
706
  ca_detach(ca->mask);
468
707
 
469
708
  return;
@@ -500,7 +739,11 @@ ca_allocate_mask_iterator_n (int n, CArray **slist)
500
739
  }
501
740
  }
502
741
 
503
- m = malloc(sizeof(boolean8_t)*elements);
742
+ /* elements stays at its -1 sentinel only when no non-NULL array was given
743
+ (degenerate call); guard so the alloc/memset never see a negative size
744
+ (which would wrap to a huge bound). Also silences GCC's VRP warnings. */
745
+ if ( elements < 0 ) elements = 0;
746
+ m = xmalloc(sizeof(boolean8_t)*elements);
504
747
  memset(m, 0, elements);
505
748
 
506
749
  if ( ! some_has_mask ) {
@@ -528,10 +771,7 @@ ca_allocate_mask_iterator_n (int n, CArray **slist)
528
771
  }
529
772
  }
530
773
  else {
531
- for (j=0; j<elements; j++) {
532
- *mp = ( *mp || *ms );
533
- mp++; ms++;
534
- }
774
+ ca_mask_word_or(mp, ms, elements); /* m[j] |= ms[j], word-bulk OR */
535
775
  }
536
776
  ca_detach(cs->mask);
537
777
  }
@@ -546,7 +786,7 @@ ca_allocate_mask_iterator (int n, ...)
546
786
  va_list args;
547
787
  int i;
548
788
 
549
- slist = malloc_with_check(sizeof(CArray *)*n);
789
+ slist = xmalloc(sizeof(CArray *)*n);
550
790
  va_start(args, n);
551
791
  for (i=0; i<n; i++) {
552
792
  slist[i] = va_arg(args, CArray *);
@@ -555,7 +795,7 @@ ca_allocate_mask_iterator (int n, ...)
555
795
 
556
796
  m = ca_allocate_mask_iterator_n(n, slist);
557
797
 
558
- free(slist);
798
+ xfree(slist);
559
799
 
560
800
  return m;
561
801
  }
@@ -654,10 +894,41 @@ rb_ca_value_array (VALUE self)
654
894
  TypedData_Get_Struct(self, CArray, &carray_data_type, ca);
655
895
 
656
896
  obj = rb_ca_refer_new(self, ca->data_type, ca->ndim, ca->dim, ca->bytes, 0);
897
+
657
898
  TypedData_Get_Struct(obj, CArray, &carray_data_type, co);
658
899
 
900
+ /* Value arrays ignore the mask by definition. ca_stride_setup
901
+ auto-propagates the parent's mask into newly-built CAStride
902
+ subclasses; strip it here so ca_has_mask(value_array) stays
903
+ false. */
904
+ if (co->mask) {
905
+ ca_free(co->mask);
906
+ co->mask = NULL;
907
+ }
659
908
  ca_set_flag(co, CA_FLAG_VALUE_ARRAY);
660
909
 
910
+ /* CAREFUL: when self is a Face, rb_ca_refer_new has already lifted its
911
+ result, so `co` above is the Face wrapper and the refer that actually
912
+ carries the data sits underneath it. Marking only the wrapper is not
913
+ enough: kernels strip Faces at entry (ca_strip_face) and would go on
914
+ to ask that refer for a mask, which -- having none of its own -- it
915
+ answers from its parent. The values would read back raw while a
916
+ reduction over them still skipped the masked cells.
917
+
918
+ Marking the storage level is enough for anything stacked in between,
919
+ since ca_is_value_array inherits from parent to child. */
920
+ if ( ca_is_face(co) ) {
921
+ CArray *storage = ca_strip_face(co);
922
+ if ( storage ) {
923
+ if ( storage->mask ) {
924
+ ca_free(storage->mask);
925
+ storage->mask = NULL;
926
+ }
927
+ ca_set_flag(storage, CA_FLAG_VALUE_ARRAY);
928
+ }
929
+ }
930
+
931
+ CA_FACE_LIFT_IF_FACE(obj, self, ca);
661
932
  return obj;
662
933
  }
663
934
 
@@ -671,7 +942,7 @@ The mask array can't be set mask.
671
942
  VALUE
672
943
  rb_ca_mask_array (VALUE self)
673
944
  {
674
- VALUE obj;
945
+ volatile VALUE obj;
675
946
  CArray *ca;
676
947
 
677
948
  TypedData_Get_Struct(self, CArray, &carray_data_type, ca);
@@ -830,36 +1101,6 @@ rb_ca_is_not_masked (VALUE self)
830
1101
  return ca_wrap_struct(co);
831
1102
  }
832
1103
 
833
- /* @overload count_masked
834
-
835
- (Masking, Statistics)
836
- Returns the number of masked elements.
837
- */
838
-
839
- VALUE
840
- rb_ca_count_masked (VALUE self)
841
- {
842
- CArray *ca;
843
- printf("hello\n");
844
- TypedData_Get_Struct(self, CArray, &carray_data_type, ca);
845
- printf("world\n");
846
- return SIZE2NUM(ca_count_masked(ca));
847
- }
848
-
849
- /* @overload count_not_masked
850
-
851
- (Masking, Statistics)
852
- Returns the number of not-masked elements.
853
- */
854
-
855
- VALUE
856
- rb_ca_count_not_masked (VALUE self)
857
- {
858
- CArray *ca;
859
- TypedData_Get_Struct(self, CArray, &carray_data_type, ca);
860
- return SIZE2NUM(ca_count_not_masked(ca));
861
- }
862
-
863
1104
  /* @overload unmask (fill_value = nil)
864
1105
 
865
1106
  (Masking, Destructive)
@@ -872,7 +1113,7 @@ The returned array doesn't have the mask array.
872
1113
  static VALUE
873
1114
  rb_ca_unmask_method (int argc, VALUE *argv, VALUE self)
874
1115
  {
875
- volatile VALUE rfval = CA_NIL, rcs;
1116
+ volatile VALUE rfval = CA_UNSPECIFIED, rcs;
876
1117
  CArray *ca;
877
1118
  CScalar *cv;
878
1119
  char *fval = NULL;
@@ -885,8 +1126,15 @@ rb_ca_unmask_method (int argc, VALUE *argv, VALUE self)
885
1126
 
886
1127
  TypedData_Get_Struct(self, CArray, &carray_data_type, ca);
887
1128
 
888
- if ( rfval != CA_NIL ) {
889
- rcs = rb_cscalar_new_with_value(ca->data_type, ca->bytes, rfval);
1129
+ if ( rfval != CA_UNSPECIFIED ) {
1130
+ /* Face has surface != storage; the fill value is cast in the storage data_type. */
1131
+ int8_t conv_type = ca->data_type;
1132
+ if ( ca_is_face(ca) ) {
1133
+ CArray *root = ca;
1134
+ while (root && ca_is_face(root)) root = ((CAView *) root)->parent;
1135
+ if (root) conv_type = root->data_type;
1136
+ }
1137
+ rcs = rb_cscalar_new_with_value(conv_type, ca->bytes, rfval);
890
1138
  TypedData_Get_Struct(rcs, CScalar, &cscalar_data_type, cv);
891
1139
  fval = cv->ptr;
892
1140
  }
@@ -924,7 +1172,7 @@ The returned array doesn't have the mask array.
924
1172
  static VALUE
925
1173
  rb_ca_unmask_copy_method (int argc, VALUE *argv, VALUE self)
926
1174
  {
927
- volatile VALUE obj, rfval = CA_NIL, rcs;
1175
+ volatile VALUE obj, rfval = CA_UNSPECIFIED, rcs;
928
1176
  CArray *ca, *co;
929
1177
  CScalar *cv;
930
1178
  char *fval = NULL;
@@ -935,15 +1183,22 @@ rb_ca_unmask_copy_method (int argc, VALUE *argv, VALUE self)
935
1183
 
936
1184
  TypedData_Get_Struct(self, CArray, &carray_data_type, ca);
937
1185
 
938
- if ( rfval != CA_NIL ) {
939
- rcs = rb_cscalar_new_with_value(ca->data_type, ca->bytes, rfval);
1186
+ if ( rfval != CA_UNSPECIFIED ) {
1187
+ /* Face has surface != storage; the fill value is cast in the storage data_type. */
1188
+ int8_t conv_type = ca->data_type;
1189
+ if ( ca_is_face(ca) ) {
1190
+ CArray *root = ca;
1191
+ while (root && ca_is_face(root)) root = ((CAView *) root)->parent;
1192
+ if (root) conv_type = root->data_type;
1193
+ }
1194
+ rcs = rb_cscalar_new_with_value(conv_type, ca->bytes, rfval);
940
1195
  TypedData_Get_Struct(rcs, CScalar, &cscalar_data_type, cv);
941
1196
  fval = cv->ptr;
942
1197
  }
943
1198
 
944
1199
  co = ca_unmask_copy(ca, fval);
945
1200
  obj = ca_wrap_struct(co);
946
- rb_ca_data_type_inherit(obj, self);
1201
+ CA_FACE_LIFT_IF_FACE(obj, self, ca);
947
1202
  return obj;
948
1203
  }
949
1204
 
@@ -978,6 +1233,71 @@ rb_ca_invert_mask (VALUE self)
978
1233
  return self;
979
1234
  }
980
1235
 
1236
+ /* guard_undef(*values, fill_value: UNDEF, &block) — class method.
1237
+ Returns fill_value if any of values is UNDEF, otherwise yields values
1238
+ to the block and returns its result.
1239
+ See lib/carray/mask.rb (removed in CIFY). */
1240
+ static VALUE
1241
+ rb_ca_s_guard_undef(int argc, VALUE *argv, VALUE klass)
1242
+ {
1243
+ VALUE values, opts;
1244
+ VALUE fill_value = CA_UNDEF;
1245
+ long i, n;
1246
+
1247
+ rb_scan_args(argc, argv, "*:", &values, &opts);
1248
+ rb_scan_options(opts, "fill_value", &fill_value);
1249
+
1250
+ n = RARRAY_LEN(values);
1251
+ for (i = 0; i < n; i++) {
1252
+ if (RARRAY_AREF(values, i) == CA_UNDEF) {
1253
+ return fill_value;
1254
+ }
1255
+ }
1256
+
1257
+ return rb_yield_splat(values);
1258
+ }
1259
+
1260
+ /* mask_eq(v) — return form of "mask cells equal to v".
1261
+ Equivalent to: obj = copy; obj[:eq, v] = UNDEF; obj */
1262
+ static VALUE
1263
+ rb_ca_mask_eq (VALUE self, VALUE v)
1264
+ {
1265
+ volatile VALUE obj = rb_ca_copy(self);
1266
+ rb_funcall(obj, rb_intern("[]="), 3,
1267
+ ID2SYM(rb_intern("eq")), v, CA_UNDEF);
1268
+ return obj;
1269
+ }
1270
+
1271
+ /* mask_invalid — return form of "mask NaN/Inf cells".
1272
+ Equivalent to: obj = copy; obj[:is_invalid] = UNDEF; obj */
1273
+ static VALUE
1274
+ rb_ca_mask_invalid (VALUE self)
1275
+ {
1276
+ volatile VALUE obj = rb_ca_copy(self);
1277
+ rb_funcall(obj, rb_intern("[]="), 2,
1278
+ ID2SYM(rb_intern("is_invalid")), CA_UNDEF);
1279
+ return obj;
1280
+ }
1281
+
1282
+ /* mask_where(*args) — generic return form predicate masking.
1283
+ Equivalent to: obj = copy; obj[*args] = UNDEF; obj
1284
+ Requires at least 1 argument. */
1285
+ static VALUE
1286
+ rb_ca_mask_where (int argc, VALUE *argv, VALUE self)
1287
+ {
1288
+ volatile VALUE obj;
1289
+ VALUE *all_args;
1290
+ if (argc == 0) {
1291
+ rb_raise(rb_eArgError, "mask_where requires at least 1 argument");
1292
+ }
1293
+ obj = rb_ca_copy(self);
1294
+ all_args = ALLOCA_N(VALUE, argc + 1);
1295
+ memcpy(all_args, argv, argc * sizeof(VALUE));
1296
+ all_args[argc] = CA_UNDEF;
1297
+ rb_funcallv(obj, rb_intern("[]="), argc + 1, all_args);
1298
+ return obj;
1299
+ }
1300
+
981
1301
  /* @overload inherit_mask (*others):
982
1302
 
983
1303
  (Masking, Destructive)
@@ -996,7 +1316,7 @@ rb_ca_inherit_mask_method (int argc, VALUE *argv, VALUE self)
996
1316
 
997
1317
  TypedData_Get_Struct(self, CArray, &carray_data_type, ca);
998
1318
 
999
- slist = malloc_with_check(sizeof(CArray *)*argc);
1319
+ slist = xmalloc(sizeof(CArray *)*argc);
1000
1320
  for (i=0; i<argc; i++) {
1001
1321
  if ( rb_obj_is_carray(argv[i]) ) {
1002
1322
  TypedData_Get_Struct(argv[i], CArray, &carray_data_type, cs);
@@ -1008,7 +1328,7 @@ rb_ca_inherit_mask_method (int argc, VALUE *argv, VALUE self)
1008
1328
  }
1009
1329
  ca_copy_mask_overlay_n(ca, ca->elements, argc, slist);
1010
1330
 
1011
- free(slist);
1331
+ xfree(slist);
1012
1332
 
1013
1333
  return self;
1014
1334
  }
@@ -1037,7 +1357,7 @@ rb_ca_inherit_mask (VALUE self, int n, ...)
1037
1357
  TypedData_Get_Struct(self, CArray, &carray_data_type, ca);
1038
1358
 
1039
1359
  va_start(rothers, n);
1040
- slist = malloc_with_check(sizeof(CArray *)*n);
1360
+ slist = xmalloc(sizeof(CArray *)*n);
1041
1361
  for (i=0; i<n; i++) {
1042
1362
  other = va_arg(rothers, VALUE);
1043
1363
  if ( rb_obj_is_carray(other) ) {
@@ -1052,7 +1372,7 @@ rb_ca_inherit_mask (VALUE self, int n, ...)
1052
1372
 
1053
1373
  ca_copy_mask_overlay_n(ca, ca->elements, n, slist);
1054
1374
 
1055
- free(slist);
1375
+ xfree(slist);
1056
1376
 
1057
1377
  return self;
1058
1378
  }
@@ -1075,7 +1395,7 @@ rb_ca_inherit_mask_replace_method (int argc, VALUE *argv, VALUE self)
1075
1395
 
1076
1396
  TypedData_Get_Struct(self, CArray, &carray_data_type, ca);
1077
1397
 
1078
- slist = malloc_with_check(sizeof(CArray *)*argc);
1398
+ slist = xmalloc(sizeof(CArray *)*argc);
1079
1399
  for (i=0; i<argc; i++) {
1080
1400
  if ( rb_obj_is_carray(argv[i]) ) {
1081
1401
  TypedData_Get_Struct(argv[i], CArray, &carray_data_type, cs);
@@ -1087,7 +1407,7 @@ rb_ca_inherit_mask_replace_method (int argc, VALUE *argv, VALUE self)
1087
1407
  }
1088
1408
  ca_copy_mask_overwrite_n(ca, ca->elements, argc, slist);
1089
1409
 
1090
- free(slist);
1410
+ xfree(slist);
1091
1411
 
1092
1412
  return self;
1093
1413
  }
@@ -1116,7 +1436,7 @@ rb_ca_inherit_mask_replace (VALUE self, int n, ...)
1116
1436
  TypedData_Get_Struct(self, CArray, &carray_data_type, ca);
1117
1437
 
1118
1438
  va_start(rothers, n);
1119
- slist = malloc_with_check(sizeof(CArray *)*n);
1439
+ slist = xmalloc(sizeof(CArray *)*n);
1120
1440
  for (i=0; i<n; i++) {
1121
1441
  other = va_arg(rothers, VALUE);
1122
1442
  if ( rb_obj_is_carray(other) ) {
@@ -1131,13 +1451,375 @@ rb_ca_inherit_mask_replace (VALUE self, int n, ...)
1131
1451
 
1132
1452
  ca_copy_mask_overwrite_n(ca, ca->elements, n, slist);
1133
1453
 
1134
- free(slist);
1454
+ xfree(slist);
1135
1455
 
1136
1456
  return self;
1137
1457
  }
1138
1458
 
1459
+ /* Helper: build a fill-valued int64 CArray whose shape is ca's shape
1460
+ with the listed axes removed. Used by count_masked/count_not_masked
1461
+ when there is no mask. */
1462
+ static VALUE
1463
+ ca_make_reduced_int64 (CArray *ca, int argc, VALUE *argv, ca_size_t fill_val)
1464
+ {
1465
+ ca_size_t dim[CA_RANK_MAX];
1466
+ int8_t ndim = 0;
1467
+ int i, j;
1468
+ int64_t v = (int64_t) fill_val;
1469
+ VALUE result;
1470
+ CArray *co;
1471
+
1472
+ for (i = 0; i < ca->ndim; i++) {
1473
+ int is_axis = 0;
1474
+ for (j = 0; j < argc; j++) {
1475
+ int ax = NUM2INT(argv[j]);
1476
+ if (ax < 0) ax += ca->ndim;
1477
+ if (ax == i) { is_axis = 1; break; }
1478
+ }
1479
+ if (!is_axis) dim[ndim++] = ca->dim[i];
1480
+ }
1481
+
1482
+ result = rb_carray_new(CA_INT64, ndim, dim, 0, NULL);
1483
+ TypedData_Get_Struct(result, CArray, &carray_data_type, co);
1484
+ ca_fill(co, &v);
1485
+ return result;
1486
+ }
1487
+
1488
+ /* count_true_ki / count_false_ki -- mkkernel-generated count kernels
1489
+ (carray_kernels.c, bind_ruby: false; output CA_INT64). Used to reduce
1490
+ the boolean mask along axes in C for count_masked / count_not_masked,
1491
+ so the axis path needs no Ruby accumulate trampoline. */
1492
+ extern VALUE rb_ca_count_true_ki (int argc, VALUE *argv, VALUE self);
1493
+ extern VALUE rb_ca_count_false_ki (int argc, VALUE *argv, VALUE self);
1494
+
1495
+ /* count_masked(*axis) -- number of masked elements, optionally reduced
1496
+ along axes (no args -> Integer; axis args -> int64 CArray with those
1497
+ axes collapsed).
1498
+
1499
+ Public (non-static) linkage so rb_ca_count in carray_count.c can forward
1500
+ count(UNDEF) here directly without going through Ruby method dispatch. */
1501
+ VALUE
1502
+ rb_ca_count_masked (int argc, VALUE *argv, VALUE self)
1503
+ {
1504
+ CArray *ca;
1505
+ TypedData_Get_Struct(self, CArray, &carray_data_type, ca);
1506
+
1507
+ /* Accepts the axis: kwarg only (positional axis is not accepted),
1508
+ matching the aggregate _ki family. */
1509
+ VALUE kw_hash, axis_val = Qnil;
1510
+ rb_scan_args(argc, argv, "0:", &kw_hash);
1511
+ rb_scan_options(kw_hash, "axis", &axis_val);
1512
+
1513
+ /* No mask -> nothing is masked. Short-circuit without materialising a
1514
+ zero mask (axis form returns a zero-filled reduced int64 array). */
1515
+ if (!ca_has_mask(ca)) {
1516
+ if (NIL_P(axis_val)) {
1517
+ return SIZE2NUM(0);
1518
+ }
1519
+ VALUE axis_ary = (TYPE(axis_val) == T_ARRAY) ? axis_val : rb_ary_new3(1, axis_val);
1520
+ return ca_make_reduced_int64(ca, (int) RARRAY_LEN(axis_ary),
1521
+ (VALUE *) RARRAY_CONST_PTR(axis_ary), 0);
1522
+ }
1523
+
1524
+ /* Masked count = count of `true` in the mask, routed through the
1525
+ count_true_ki kernel for both the flat (no axis -> scalar Integer) and
1526
+ per-axis (-> reduced CArray) forms. The scalar C primitive
1527
+ ca_count_masked stays separately for internal C callers (grid /
1528
+ median / order) that need a raw ca_size_t without Ruby allocation. */
1529
+ VALUE mask_obj = rb_ca_mask_array(self);
1530
+ if (NIL_P(axis_val)) {
1531
+ return rb_ca_count_true_ki(0, NULL, mask_obj);
1532
+ }
1533
+ VALUE kw = rb_hash_new();
1534
+ rb_hash_aset(kw, ID2SYM(rb_intern("axis")), axis_val);
1535
+ VALUE kargv[1] = { kw };
1536
+ return rb_ca_count_true_ki(1, kargv, mask_obj);
1537
+ }
1538
+
1539
+ /* @overload count_not_masked(*axis)
1540
+
1541
+ (Masking, Statistics)
1542
+ Returns the number of not-masked elements, optionally reduced along axes.
1543
+ With no arguments returns an Integer. With axis arguments returns a
1544
+ CArray of int64 with the specified axes collapsed.
1545
+ */
1546
+ /* C-callable entry: skip rb_scan_args (call-frame state dependency).
1547
+ * axis_val = Qnil -> scalar Integer count over whole array
1548
+ * axis_val = Integer/Array -> reduced CArray int64
1549
+ * For ext authors who need to call count_not_masked from C. */
1550
+ VALUE
1551
+ rb_ca_count_not_masked_c (VALUE self, VALUE axis_val)
1552
+ {
1553
+ CArray *ca;
1554
+ TypedData_Get_Struct(self, CArray, &carray_data_type, ca);
1555
+
1556
+ /* No mask -> every element is not-masked. Short-circuit without
1557
+ materialising a mask (no axis -> elements; axis form -> reduced int64
1558
+ filled with the product of the reduced-axis extents). */
1559
+ if (!ca_has_mask(ca)) {
1560
+ if (NIL_P(axis_val)) {
1561
+ return SIZE2NUM(ca->elements);
1562
+ }
1563
+ VALUE axis_ary = (TYPE(axis_val) == T_ARRAY) ? axis_val : rb_ary_new3(1, axis_val);
1564
+ int n_axes = (int) RARRAY_LEN(axis_ary);
1565
+ VALUE *axes_argv = (VALUE *) RARRAY_CONST_PTR(axis_ary);
1566
+ ca_size_t axis_vol = 1;
1567
+ int j;
1568
+ for (j = 0; j < n_axes; j++) {
1569
+ int ax = NUM2INT(axes_argv[j]);
1570
+ if (ax < 0) ax += ca->ndim;
1571
+ axis_vol *= ca->dim[ax];
1572
+ }
1573
+ return ca_make_reduced_int64(ca, n_axes, axes_argv, axis_vol);
1574
+ }
1575
+
1576
+ /* Not-masked count = count of `false` in the mask, routed through the
1577
+ count_false_ki kernel for both the flat (no axis -> scalar Integer) and
1578
+ per-axis (-> reduced CArray) forms. The scalar C primitive
1579
+ ca_count_not_masked (= elements - ca_count_masked) stays separately for
1580
+ internal C callers (median / order) that need a raw ca_size_t. */
1581
+ VALUE mask_obj = rb_ca_mask_array(self);
1582
+ if (NIL_P(axis_val)) {
1583
+ return rb_ca_count_false_ki(0, NULL, mask_obj);
1584
+ }
1585
+ VALUE kw = rb_hash_new();
1586
+ rb_hash_aset(kw, ID2SYM(rb_intern("axis")), axis_val);
1587
+ VALUE kargv[1] = { kw };
1588
+ return rb_ca_count_false_ki(1, kargv, mask_obj);
1589
+ }
1590
+
1591
+ /* Ruby binding entry: parses (axis: kwarg) -> forwards to _c. */
1592
+ VALUE
1593
+ rb_ca_count_not_masked (int argc, VALUE *argv, VALUE self)
1594
+ {
1595
+ /* Accepts the axis: kwarg only. */
1596
+ VALUE kw_hash, axis_val = Qnil;
1597
+ rb_scan_args(argc, argv, "0:", &kw_hash);
1598
+ rb_scan_options(kw_hash, "axis", &axis_val);
1599
+ return rb_ca_count_not_masked_c(self, axis_val);
1600
+ }
1601
+
1602
+ /* ============================================================================
1603
+ * boolean8_t bit-op fast paths (table slot patching)
1604
+ *
1605
+ * Strategy: ext/carray_math.c is GENERATED (= .gitignored, regen'd from
1606
+ * ext/carray_math.rb). Hand-editing the generated functions would be
1607
+ * overwritten by `ruby carray_math.rb`. Instead we:
1608
+ *
1609
+ * 1. Define standalone boolean8_t-only fast functions here in
1610
+ * carray_mask.c (= hand-written, regen-safe).
1611
+ * 2. Patch the generated dispatch tables at Init time:
1612
+ * ca_binop_bit_and_i[CA_BOOLEAN] = ca_binop_bit_and_i_boolean8_fast;
1613
+ * (and similarly for _or_i, _xor_i, and ca_monop_bit_neg)
1614
+ * 3. Init_carray_mask_fast_paths() is called AFTER Init_carray_math()
1615
+ * from ruby_carray.c so the patch sticks.
1616
+ *
1617
+ * Each fast function handles BOTH paths internally:
1618
+ * fast: m == NULL && i1 == i2 == i3 == 1 (contig + no mask)
1619
+ * → word loop on uint64_t + tail byte fallback
1620
+ * slow: anything else (mask present, or non-contig view, etc.)
1621
+ * → duplicate of the original generated byte loop
1622
+ *
1623
+ * The slow path is bit-identical to the generated function's body so
1624
+ * hooking is transparent — no semantic change, only fast-path addition.
1625
+ * ============================================================================ */
1626
+
1627
+ /* Generated tables to be patched (defined non-static in carray_math.c). */
1628
+ extern ca_binop_func_t ca_binop_bit_and_i[];
1629
+ extern ca_binop_func_t ca_binop_bit_or_i[];
1630
+ extern ca_binop_func_t ca_binop_bit_xor_i[];
1631
+ extern ca_monop_func_t ca_monop_bit_neg[];
1632
+ /* Note: Ruby `~` is registered as an alias of `bit_neg` (carray_math.c:
1633
+ rb_define_alias(rb_cCArray, "~", "bit_neg")); the table is
1634
+ ca_monop_bit_neg. */
1635
+
1636
+ /* ----- bit_and_i (boolean8_t fast) ----- */
1637
+ static void
1638
+ ca_binop_bit_and_i_boolean8_fast (ca_size_t n, boolean8_t *m,
1639
+ char *ptr1, ca_size_t i1,
1640
+ char *ptr2, ca_size_t i2,
1641
+ char *ptr3, ca_size_t i3)
1642
+ {
1643
+ boolean8_t *q1 = (boolean8_t *) ptr1;
1644
+ boolean8_t *q2 = (boolean8_t *) ptr2;
1645
+ boolean8_t *q3 = (boolean8_t *) ptr3;
1646
+ boolean8_t *p1, *p2, *p3;
1647
+ ca_size_t k;
1648
+
1649
+ if ( m ) {
1650
+ /* mask path: existing byte loop (skip masked positions) */
1651
+ boolean8_t *pm;
1652
+ for (k = 0; k < n; k++) {
1653
+ pm = m + k;
1654
+ if ( ! *pm ) {
1655
+ p1 = q1 + k*i1;
1656
+ p2 = q2 + k*i2;
1657
+ p3 = q3 + k*i3;
1658
+ (*p3) = (*p1) & (*p2);
1659
+ }
1660
+ }
1661
+ }
1662
+ else if ( i1 == 1 && i2 == 1 && i3 == 1 ) {
1663
+ /* fast path: word-bulk AND */
1664
+ ca_mask_word_and(q3, q1, q2, n);
1665
+ }
1666
+ else {
1667
+ /* non-contig view: existing byte loop */
1668
+ for (k = 0; k < n; k++) {
1669
+ p1 = q1 + k*i1;
1670
+ p2 = q2 + k*i2;
1671
+ p3 = q3 + k*i3;
1672
+ (*p3) = (*p1) & (*p2);
1673
+ }
1674
+ }
1675
+ }
1676
+
1677
+ /* ----- bit_or_i (boolean8_t fast) ----- */
1678
+ static void
1679
+ ca_binop_bit_or_i_boolean8_fast (ca_size_t n, boolean8_t *m,
1680
+ char *ptr1, ca_size_t i1,
1681
+ char *ptr2, ca_size_t i2,
1682
+ char *ptr3, ca_size_t i3)
1683
+ {
1684
+ boolean8_t *q1 = (boolean8_t *) ptr1;
1685
+ boolean8_t *q2 = (boolean8_t *) ptr2;
1686
+ boolean8_t *q3 = (boolean8_t *) ptr3;
1687
+ boolean8_t *p1, *p2, *p3;
1688
+ ca_size_t k;
1689
+
1690
+ if ( m ) {
1691
+ boolean8_t *pm;
1692
+ for (k = 0; k < n; k++) {
1693
+ pm = m + k;
1694
+ if ( ! *pm ) {
1695
+ p1 = q1 + k*i1;
1696
+ p2 = q2 + k*i2;
1697
+ p3 = q3 + k*i3;
1698
+ (*p3) = (*p1) | (*p2);
1699
+ }
1700
+ }
1701
+ }
1702
+ else if ( i1 == 1 && i2 == 1 && i3 == 1 ) {
1703
+ /* fast path: inline 3-way word OR (ca_mask_word_or is 2-arg
1704
+ accumulating; here we need 3-arg). */
1705
+ ca_size_t nw = n / CA_MASK_WORD_BYTES;
1706
+ ca_size_t j;
1707
+ uint64_t *wd = (uint64_t *) q3;
1708
+ const uint64_t *wa = (const uint64_t *) q1;
1709
+ const uint64_t *wb = (const uint64_t *) q2;
1710
+ for (j = 0; j < nw; j++) {
1711
+ wd[j] = wa[j] | wb[j];
1712
+ }
1713
+ for (j = nw * CA_MASK_WORD_BYTES; j < n; j++) {
1714
+ q3[j] = q1[j] | q2[j];
1715
+ }
1716
+ }
1717
+ else {
1718
+ for (k = 0; k < n; k++) {
1719
+ p1 = q1 + k*i1;
1720
+ p2 = q2 + k*i2;
1721
+ p3 = q3 + k*i3;
1722
+ (*p3) = (*p1) | (*p2);
1723
+ }
1724
+ }
1725
+ }
1726
+
1727
+ /* ----- bit_xor_i (boolean8_t fast) -----
1728
+ Original generated form for boolean8_t uses `(*p3) = ((*p1) != (*p2)) ? 1 : 0`
1729
+ (logical XOR for {0,1}). Word XOR is equivalent under invariant {0,1}:
1730
+ bytewise XOR of {0,1} stays in {0,1}. */
1731
+ static void
1732
+ ca_binop_bit_xor_i_boolean8_fast (ca_size_t n, boolean8_t *m,
1733
+ char *ptr1, ca_size_t i1,
1734
+ char *ptr2, ca_size_t i2,
1735
+ char *ptr3, ca_size_t i3)
1736
+ {
1737
+ boolean8_t *q1 = (boolean8_t *) ptr1;
1738
+ boolean8_t *q2 = (boolean8_t *) ptr2;
1739
+ boolean8_t *q3 = (boolean8_t *) ptr3;
1740
+ boolean8_t *p1, *p2, *p3;
1741
+ ca_size_t k;
1742
+
1743
+ if ( m ) {
1744
+ boolean8_t *pm;
1745
+ for (k = 0; k < n; k++) {
1746
+ pm = m + k;
1747
+ if ( ! *pm ) {
1748
+ p1 = q1 + k*i1;
1749
+ p2 = q2 + k*i2;
1750
+ p3 = q3 + k*i3;
1751
+ (*p3) = ((*p1) != (*p2)) ? 1 : 0;
1752
+ }
1753
+ }
1754
+ }
1755
+ else if ( i1 == 1 && i2 == 1 && i3 == 1 ) {
1756
+ /* fast path: word XOR */
1757
+ ca_mask_word_xor(q3, q1, q2, n);
1758
+ }
1759
+ else {
1760
+ for (k = 0; k < n; k++) {
1761
+ p1 = q1 + k*i1;
1762
+ p2 = q2 + k*i2;
1763
+ p3 = q3 + k*i3;
1764
+ (*p3) = ((*p1) != (*p2)) ? 1 : 0;
1765
+ }
1766
+ }
1767
+ }
1768
+
1769
+ /* ----- bit_neg (= Ruby `~`, monop) (boolean8_t fast) -----
1770
+ The generated boolean8_t variant uses `(*p2) = (*p1) ? 0 : 1` (logical
1771
+ inversion via ternary), NOT bitwise `~`. No caller expects byte `~`
1772
+ semantics for a boolean array, and the LSB XOR pattern
1773
+ (= ca_mask_word_not) is fully equivalent while preserving {0,1}. */
1774
+ static void
1775
+ ca_monop_bit_neg_boolean8_fast (ca_size_t n, boolean8_t *m,
1776
+ char *ptr1, ca_size_t i1,
1777
+ char *ptr2, ca_size_t i2)
1778
+ {
1779
+ boolean8_t *q1 = (boolean8_t *) ptr1;
1780
+ boolean8_t *q2 = (boolean8_t *) ptr2;
1781
+ boolean8_t *p1, *p2;
1782
+ ca_size_t k;
1783
+
1784
+ if ( m ) {
1785
+ boolean8_t *pm;
1786
+ for (k = 0; k < n; k++) {
1787
+ pm = m + k;
1788
+ if ( ! *pm ) {
1789
+ p1 = q1 + k*i1;
1790
+ p2 = q2 + k*i2;
1791
+ (*p2) = (*p1) ? 0 : 1;
1792
+ }
1793
+ }
1794
+ }
1795
+ else if ( i1 == 1 && i2 == 1 ) {
1796
+ /* fast path: word NOT (LSB XOR) */
1797
+ ca_mask_word_not(q2, q1, n);
1798
+ }
1799
+ else {
1800
+ for (k = 0; k < n; k++) {
1801
+ p1 = q1 + k*i1;
1802
+ p2 = q2 + k*i2;
1803
+ (*p2) = (*p1) ? 0 : 1;
1804
+ }
1805
+ }
1806
+ }
1807
+
1808
+ /* Init_carray_mask_fast_paths — patch generated dispatch tables.
1809
+ Must be called AFTER Init_carray_math (table is set up there).
1810
+ Wired in ruby_carray.c. */
1139
1811
  void
1140
- Init_carray_mask ()
1812
+ Init_carray_mask_fast_paths (void)
1813
+ {
1814
+ ca_binop_bit_and_i[CA_BOOLEAN] = ca_binop_bit_and_i_boolean8_fast;
1815
+ ca_binop_bit_or_i [CA_BOOLEAN] = ca_binop_bit_or_i_boolean8_fast;
1816
+ ca_binop_bit_xor_i[CA_BOOLEAN] = ca_binop_bit_xor_i_boolean8_fast;
1817
+ ca_monop_bit_neg [CA_BOOLEAN] = ca_monop_bit_neg_boolean8_fast;
1818
+ }
1819
+
1820
+
1821
+ void
1822
+ Init_carray_mask (void)
1141
1823
  {
1142
1824
  rb_define_private_method(rb_cCArray, "__create_mask__", rb_ca_create_mask, 0);
1143
1825
  /*
@@ -1154,16 +1836,29 @@ Init_carray_mask ()
1154
1836
  rb_define_method(rb_cCArray, "is_masked", rb_ca_is_masked, 0);
1155
1837
  rb_define_method(rb_cCArray, "is_not_masked", rb_ca_is_not_masked, 0);
1156
1838
  rb_define_method(rb_cCArray, "unmask", rb_ca_unmask_method, -1);
1157
- rb_define_method(rb_cCArray, "unmask_copy", rb_ca_unmask_copy_method, -1);
1839
+
1840
+ /* unmask_copy is not offered (removed in 3.0), no alias. Migration:
1841
+ ca.unmask_copy() -> ca.value.to_ca (mask structure dissociated)
1842
+ ca.unmask_copy(fill) -> ca.strip_mask(fill)
1843
+ The internal C function ca_unmask_copy / rb_ca_mask_fill_copy stays
1844
+ since strip_mask wraps it. */
1845
+
1846
+ /* strip_mask(fill) is the return form of "produce a mask-free copy
1847
+ filled with fill at previously-masked positions". Fill is mandatory
1848
+ (arity 1, no default), matching the "all arguments mandatory"
1849
+ regularity of the mask SET family. */
1850
+ rb_define_method(rb_cCArray, "strip_mask", rb_ca_mask_fill_copy, 1);
1158
1851
  rb_define_method(rb_cCArray, "invert_mask", rb_ca_invert_mask, 0);
1852
+ rb_define_singleton_method(rb_cCArray, "guard_undef", rb_ca_s_guard_undef, -1);
1853
+ rb_define_method(rb_cCArray, "mask_eq", rb_ca_mask_eq, 1);
1854
+ rb_define_method(rb_cCArray, "mask_invalid", rb_ca_mask_invalid, 0);
1855
+ rb_define_method(rb_cCArray, "mask_where", rb_ca_mask_where, -1);
1159
1856
 
1160
1857
  rb_define_method(rb_cCArray, "inherit_mask", rb_ca_inherit_mask_method, -1);
1161
- rb_define_method(rb_cCArray, "inherit_mask_replace",
1858
+ rb_define_method(rb_cCArray, "inherit_mask_replace",
1162
1859
  rb_ca_inherit_mask_replace_method, -1);
1163
1860
 
1164
- /* These methods go to lib/carray/mask.rb. */
1165
- /* def count_masked (*axis); end */
1166
- /* def count_not_masked (*axis); end */
1167
-
1861
+ rb_define_method(rb_cCArray, "count_masked", rb_ca_count_masked, -1);
1862
+ rb_define_method(rb_cCArray, "count_not_masked", rb_ca_count_not_masked, -1);
1168
1863
  }
1169
1864