carray 2.0.0 → 3.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (339) hide show
  1. checksums.yaml +4 -4
  2. data/.yardopts +5 -25
  3. data/CHANGELOG.md +16 -0
  4. data/LICENSE +1 -1
  5. data/NEWS.md +3 -0
  6. data/README.md +128 -44
  7. data/carray.gemspec +22 -24
  8. data/ext/ca_array_pool.c +91 -0
  9. data/ext/ca_axis_descriptor.h +186 -0
  10. data/ext/ca_axis_dispatch.c +924 -0
  11. data/ext/ca_axis_group.c +1208 -0
  12. data/ext/ca_bincmp_dispatch.c +76 -0
  13. data/ext/ca_bincmp_dispatch.h +85 -0
  14. data/ext/ca_binop_dispatch.c +125 -0
  15. data/ext/ca_binop_dispatch.h +159 -0
  16. data/ext/ca_categorical_iterator.c +1375 -0
  17. data/ext/ca_compare.c +94 -0
  18. data/ext/ca_compare.h +26 -0
  19. data/ext/ca_composite_dispatch.c +414 -0
  20. data/ext/ca_composite_dispatch.h +116 -0
  21. data/ext/ca_for_buffer.h +96 -0
  22. data/ext/ca_for_each_element.h +241 -0
  23. data/ext/ca_group_iter.c +304 -0
  24. data/ext/ca_iter_substrate.h +325 -0
  25. data/ext/ca_kernel_iterator.c +4321 -0
  26. data/ext/ca_kernel_iterator.h +2603 -0
  27. data/ext/ca_moncmp_dispatch.c +37 -0
  28. data/ext/ca_moncmp_dispatch.h +62 -0
  29. data/ext/ca_monop_dispatch.c +200 -0
  30. data/ext/ca_monop_dispatch.h +235 -0
  31. data/ext/ca_obj_array.c +355 -359
  32. data/ext/ca_obj_bincmp.c +809 -0
  33. data/ext/ca_obj_binop.c +892 -0
  34. data/ext/ca_obj_bitarray.c +369 -164
  35. data/ext/ca_obj_bitfield.c +294 -234
  36. data/ext/ca_obj_block.c +189 -711
  37. data/ext/ca_obj_byte_swap.c +766 -0
  38. data/ext/ca_obj_const_string.c +965 -0
  39. data/ext/ca_obj_face.c +670 -0
  40. data/ext/ca_obj_face.h +247 -0
  41. data/ext/ca_obj_fake.c +228 -100
  42. data/ext/ca_obj_farray.c +54 -441
  43. data/ext/ca_obj_field.c +82 -529
  44. data/ext/ca_obj_fixlen_string.c +306 -0
  45. data/ext/ca_obj_grid.c +858 -440
  46. data/ext/ca_obj_meld.c +1034 -0
  47. data/ext/ca_obj_moncmp.c +569 -0
  48. data/ext/ca_obj_monop.c +1111 -0
  49. data/ext/ca_obj_object.c +774 -298
  50. data/ext/ca_obj_record.c +468 -0
  51. data/ext/ca_obj_reduce.c +97 -82
  52. data/ext/ca_obj_refer.c +569 -459
  53. data/ext/ca_obj_remap.c +475 -0
  54. data/ext/ca_obj_repeat.c +92 -477
  55. data/ext/ca_obj_roll.c +616 -0
  56. data/ext/ca_obj_select.c +344 -296
  57. data/ext/ca_obj_select_axis.c +1296 -0
  58. data/ext/ca_obj_shift.c +230 -792
  59. data/ext/ca_obj_source.c +78 -0
  60. data/ext/ca_obj_stack.c +1173 -0
  61. data/ext/ca_obj_stride.c +2501 -0
  62. data/ext/ca_obj_string.c +268 -0
  63. data/ext/ca_obj_tile.c +614 -0
  64. data/ext/ca_obj_time.c +546 -0
  65. data/ext/ca_obj_timedelta.c +435 -0
  66. data/ext/ca_obj_transpose.c +62 -516
  67. data/ext/ca_obj_triop.c +746 -0
  68. data/ext/ca_obj_unbound_repeat.c +208 -241
  69. data/ext/ca_obj_window.c +1131 -563
  70. data/ext/ca_op_byte_swap.c +175 -0
  71. data/ext/ca_op_ipower.c +319 -0
  72. data/ext/ca_op_powi.h +88 -0
  73. data/ext/ca_sort_kernels.h +132 -0
  74. data/ext/ca_sweep_engine.c +430 -0
  75. data/ext/ca_sweep_engine.h +157 -0
  76. data/ext/ca_transform_common.c +228 -0
  77. data/ext/ca_triop_dispatch.c +55 -0
  78. data/ext/ca_triop_dispatch.h +62 -0
  79. data/ext/carray.h +795 -402
  80. data/ext/carray_access.c +831 -711
  81. data/ext/carray_attribute.c +98 -330
  82. data/ext/carray_bincount.c +255 -0
  83. data/ext/carray_broadcast.c +283 -0
  84. data/ext/carray_call_cfunc.c +1360 -828
  85. data/ext/carray_call_cfunc.h +160 -0
  86. data/ext/carray_cast.c +1212 -301
  87. data/ext/carray_cast_func.rb +81 -40
  88. data/ext/carray_class.c +53 -63
  89. data/ext/carray_config.h +28 -0
  90. data/ext/carray_conversion.c +350 -346
  91. data/ext/carray_copy.c +156 -268
  92. data/ext/carray_core.c +1342 -199
  93. data/ext/carray_count.c +312 -0
  94. data/ext/carray_data_type.c +43 -19
  95. data/ext/carray_element.c +585 -213
  96. data/ext/carray_factorize.c +2542 -0
  97. data/ext/carray_generate.c +230 -559
  98. data/ext/carray_histogram.c +490 -0
  99. data/ext/carray_hold.c +228 -0
  100. data/ext/carray_index_classifier.c +1035 -0
  101. data/ext/carray_index_classifier.h +27 -0
  102. data/ext/carray_internal.h +120 -0
  103. data/ext/carray_kernels_bincmp.c +4445 -0
  104. data/ext/carray_kernels_binop.c +10979 -0
  105. data/ext/carray_kernels_init.c +36 -0
  106. data/ext/carray_kernels_map.c +3466 -0
  107. data/ext/carray_kernels_moncmp.c +2096 -0
  108. data/ext/carray_kernels_monop.c +18312 -0
  109. data/ext/carray_kernels_reduce_aggregate.c +25836 -0
  110. data/ext/carray_kernels_reduce_boolean.c +329 -0
  111. data/ext/carray_kernels_reduce_cumulative.c +14592 -0
  112. data/ext/carray_kernels_reduce_extreme.c +16947 -0
  113. data/ext/carray_kernels_reduce_variance.c +3909 -0
  114. data/ext/carray_kernels_scan.c +3692 -0
  115. data/ext/carray_kernels_search.c +32137 -0
  116. data/ext/carray_kernels_sort.c +10625 -0
  117. data/ext/carray_kernels_triop.c +1391 -0
  118. data/ext/carray_lazy.c +567 -0
  119. data/ext/carray_loop.c +88 -200
  120. data/ext/carray_mask.c +848 -154
  121. data/ext/carray_math_kernel.h +120 -0
  122. data/ext/carray_mathfunc.c +10 -241
  123. data/ext/carray_median_percentile.c +1257 -0
  124. data/ext/carray_memory_view.c +1625 -0
  125. data/ext/carray_operator.c +1526 -318
  126. data/ext/carray_order.c +664 -1394
  127. data/ext/carray_partition.c +416 -0
  128. data/ext/carray_random.c +518 -0
  129. data/ext/carray_scatter.c +357 -0
  130. data/ext/carray_slab.c +1219 -0
  131. data/ext/carray_slab.h +84 -0
  132. data/ext/carray_sort.c +829 -0
  133. data/ext/carray_sort_kernel.c +620 -0
  134. data/ext/carray_struct.c +695 -0
  135. data/ext/carray_test.c +343 -229
  136. data/ext/carray_undef.c +34 -17
  137. data/ext/carray_utils.c +175 -74
  138. data/ext/extconf.rb +216 -55
  139. data/ext/mk_call_cfunc.rb +480 -0
  140. data/ext/mkkernel.rb +8842 -0
  141. data/ext/ruby_carray.c +202 -101
  142. data/ext/version.h +4 -14
  143. data/ext/version.rb +5 -13
  144. data/lib/carray/arrow_tensor.rb +401 -0
  145. data/lib/carray/attribute.rb +166 -0
  146. data/lib/carray/autoload_carray.rb +220 -0
  147. data/lib/carray/autoload_method_extension.rb +44 -0
  148. data/lib/carray/axis_group.rb +711 -0
  149. data/lib/carray/basics.rb +481 -0
  150. data/lib/carray/bincount_nd.rb +358 -0
  151. data/lib/carray/block_iterator.rb +604 -0
  152. data/lib/carray/boolean_reduce.rb +109 -0
  153. data/lib/carray/categorical.rb +561 -0
  154. data/lib/carray/categorical_iterator.rb +1062 -0
  155. data/lib/carray/complex.rb +150 -0
  156. data/lib/carray/conditional.rb +216 -0
  157. data/lib/carray/const_string.rb +228 -0
  158. data/lib/carray/construct.rb +139 -328
  159. data/lib/carray/core_extensions.rb +240 -0
  160. data/lib/carray/data_type_extension.rb +233 -0
  161. data/lib/carray/fixlen_string.rb +95 -0
  162. data/lib/carray/frame/concat.rb +132 -0
  163. data/lib/carray/frame/convert.rb +95 -0
  164. data/lib/carray/frame/csv_parser.rb +211 -0
  165. data/lib/carray/frame/frame.rb +649 -0
  166. data/lib/carray/frame/group.rb +186 -0
  167. data/lib/carray/frame/io.rb +164 -0
  168. data/lib/carray/frame/join.rb +248 -0
  169. data/lib/carray/frame/records.rb +99 -0
  170. data/lib/carray/frame/sort.rb +113 -0
  171. data/lib/carray/frame/verbs.rb +299 -0
  172. data/lib/carray/frame.rb +16 -0
  173. data/lib/carray/histogram.rb +512 -0
  174. data/lib/carray/inspect.rb +37 -20
  175. data/lib/carray/iterator.rb +57 -349
  176. data/lib/carray/lazy.rb +889 -0
  177. data/lib/carray/mask_gap_fill.rb +200 -0
  178. data/lib/carray/math.rb +78 -342
  179. data/lib/carray/meld_reduce.rb +289 -0
  180. data/lib/carray/methods/align_addr.rb +116 -0
  181. data/lib/carray/methods/bin.rb +128 -0
  182. data/lib/carray/methods/bincount.rb +87 -0
  183. data/lib/carray/methods/bit_string.rb +92 -0
  184. data/lib/carray/methods/broadcast.rb +63 -0
  185. data/lib/carray/methods/choose.rb +39 -0
  186. data/lib/carray/methods/composition.rb +280 -0
  187. data/lib/carray/methods/gather_nd.rb +206 -0
  188. data/lib/carray/methods/index.rb +39 -0
  189. data/lib/carray/methods/insert_block.rb +99 -0
  190. data/lib/carray/methods/is_in.rb +141 -0
  191. data/lib/carray/methods/join.rb +90 -0
  192. data/lib/carray/methods/locate_addr.rb +47 -0
  193. data/lib/carray/methods/mask_duplicates.rb +41 -0
  194. data/lib/carray/methods/meshgrid.rb +91 -0
  195. data/lib/carray/methods/mode.rb +126 -0
  196. data/lib/carray/methods/nunique.rb +46 -0
  197. data/lib/carray/methods/resize.rb +56 -0
  198. data/lib/carray/methods/snap.rb +156 -0
  199. data/lib/carray/methods/string_format.rb +57 -0
  200. data/lib/carray/methods/unique.rb +47 -0
  201. data/lib/carray/methods/value_counts.rb +71 -0
  202. data/lib/carray/mkmf.rb +124 -101
  203. data/lib/carray/runtime.rb +108 -0
  204. data/lib/carray/serialize.rb +478 -167
  205. data/lib/carray/slab_iterator.rb +292 -0
  206. data/lib/carray/stack.rb +291 -0
  207. data/lib/carray/string.rb +56 -180
  208. data/lib/carray/string_operation_extension.rb +289 -0
  209. data/lib/carray/struct.rb +335 -323
  210. data/lib/carray/struct_builder.rb +697 -0
  211. data/lib/carray/table.rb +41 -2
  212. data/lib/carray/time.rb +2255 -38
  213. data/lib/carray/window_iterator.rb +655 -0
  214. data/lib/carray.rb +55 -57
  215. metadata +163 -130
  216. data/Rakefile +0 -51
  217. data/TODO.md +0 -18
  218. data/ext/ca_iter_block.c +0 -257
  219. data/ext/ca_iter_dimension.c +0 -299
  220. data/ext/ca_iter_window.c +0 -214
  221. data/ext/ca_obj_mapping.c +0 -644
  222. data/ext/carray_iterator.c +0 -641
  223. data/ext/carray_math.rb +0 -850
  224. data/ext/carray_numeric.c +0 -259
  225. data/ext/carray_sort_addr.c +0 -254
  226. data/ext/carray_stat.c +0 -2100
  227. data/ext/carray_stat_proc.rb +0 -1999
  228. data/ext/mkmath.rb +0 -741
  229. data/ext/ruby_ccomplex.c +0 -509
  230. data/ext/ruby_float_func.c +0 -86
  231. data/lib/carray/array.rb +0 -8
  232. data/lib/carray/autoload/autoload_base.rb +0 -19
  233. data/lib/carray/autoload/autoload_gem_cairo.rb +0 -9
  234. data/lib/carray/autoload/autoload_gem_ffi.rb +0 -9
  235. data/lib/carray/autoload/autoload_gem_gnuplot.rb +0 -2
  236. data/lib/carray/autoload/autoload_gem_io_csv.rb +0 -14
  237. data/lib/carray/autoload/autoload_gem_io_pg.rb +0 -6
  238. data/lib/carray/autoload/autoload_gem_io_sqlite3.rb +0 -12
  239. data/lib/carray/autoload/autoload_gem_narray.rb +0 -10
  240. data/lib/carray/autoload/autoload_gem_numo_narray.rb +0 -15
  241. data/lib/carray/autoload/autoload_gem_opencv.rb +0 -16
  242. data/lib/carray/autoload/autoload_gem_random.rb +0 -8
  243. data/lib/carray/autoload/autoload_gem_rmagick.rb +0 -23
  244. data/lib/carray/autoload/autoload_gem_zimg.rb +0 -3
  245. data/lib/carray/autoload/autoload_io_imagemagick.rb +0 -6
  246. data/lib/carray/autoload/autoload_math_histogram.rb +0 -5
  247. data/lib/carray/autoload/autoload_math_recurrence.rb +0 -6
  248. data/lib/carray/autoload/autoload_object_iterator.rb +0 -1
  249. data/lib/carray/autoload/autoload_object_link.rb +0 -1
  250. data/lib/carray/autoload/autoload_object_pack.rb +0 -2
  251. data/lib/carray/autoload.rb +0 -141
  252. data/lib/carray/basic.rb +0 -191
  253. data/lib/carray/broadcast.rb +0 -101
  254. data/lib/carray/compose.rb +0 -315
  255. data/lib/carray/convert.rb +0 -115
  256. data/lib/carray/info.rb +0 -110
  257. data/lib/carray/io/imagemagick.rb +0 -235
  258. data/lib/carray/mask.rb +0 -102
  259. data/lib/carray/math/histogram.rb +0 -177
  260. data/lib/carray/math/recurrence.rb +0 -93
  261. data/lib/carray/object/ca_obj_iterator.rb +0 -50
  262. data/lib/carray/object/ca_obj_link.rb +0 -50
  263. data/lib/carray/object/ca_obj_pack.rb +0 -99
  264. data/lib/carray/obsolete.rb +0 -256
  265. data/lib/carray/ordering.rb +0 -181
  266. data/lib/carray/testing.rb +0 -51
  267. data/lib/carray/transform.rb +0 -109
  268. data/misc/Methods.ja.md +0 -182
  269. data/misc/NOTE +0 -51
  270. data/spec/Classes/CABitfield_spec.rb +0 -58
  271. data/spec/Classes/CABlockIterator_spec.rb +0 -114
  272. data/spec/Classes/CABlock_spec.rb +0 -205
  273. data/spec/Classes/CAField_spec.rb +0 -39
  274. data/spec/Classes/CAGrid_spec.rb +0 -75
  275. data/spec/Classes/CAMap_spec.rb +0 -0
  276. data/spec/Classes/CAMapping_spec.rb +0 -105
  277. data/spec/Classes/CAObject_attribute_spec.rb +0 -33
  278. data/spec/Classes/CAObject_spec.rb +0 -33
  279. data/spec/Classes/CARefer_spec.rb +0 -93
  280. data/spec/Classes/CARepeat_spec.rb +0 -65
  281. data/spec/Classes/CASelect_spec.rb +0 -22
  282. data/spec/Classes/CAShift_spec.rb +0 -16
  283. data/spec/Classes/CAStruct_spec.rb +0 -71
  284. data/spec/Classes/CATranspose_spec.rb +0 -60
  285. data/spec/Classes/CAUnboudRepeat_spec.rb +0 -102
  286. data/spec/Classes/CAWindow_spec.rb +0 -54
  287. data/spec/Classes/CAWrap_spec.rb +0 -8
  288. data/spec/Classes/CArray_spec.rb +0 -184
  289. data/spec/Classes/CScalar_spec.rb +0 -55
  290. data/spec/Classes/ex1.rb +0 -46
  291. data/spec/Features/feature_130_spec.rb +0 -19
  292. data/spec/Features/feature_attributes_spec.rb +0 -280
  293. data/spec/Features/feature_boolean_spec.rb +0 -98
  294. data/spec/Features/feature_broadcast.rb +0 -116
  295. data/spec/Features/feature_cast_function.rb +0 -19
  296. data/spec/Features/feature_cast_spec.rb +0 -33
  297. data/spec/Features/feature_class_spec.rb +0 -84
  298. data/spec/Features/feature_complex_spec.rb +0 -42
  299. data/spec/Features/feature_composite_spec.rb +0 -124
  300. data/spec/Features/feature_convert_spec.rb +0 -46
  301. data/spec/Features/feature_copy_spec.rb +0 -123
  302. data/spec/Features/feature_creation_spec.rb +0 -84
  303. data/spec/Features/feature_element_spec.rb +0 -144
  304. data/spec/Features/feature_extream_spec.rb +0 -54
  305. data/spec/Features/feature_generate_spec.rb +0 -74
  306. data/spec/Features/feature_index_spec.rb +0 -69
  307. data/spec/Features/feature_mask_spec.rb +0 -580
  308. data/spec/Features/feature_math_spec.rb +0 -97
  309. data/spec/Features/feature_order_spec.rb +0 -146
  310. data/spec/Features/feature_ref_store_spec.rb +0 -209
  311. data/spec/Features/feature_serialization_spec.rb +0 -125
  312. data/spec/Features/feature_stat_spec.rb +0 -397
  313. data/spec/Features/feature_virtual_spec.rb +0 -48
  314. data/spec/Features/method_eq_spec.rb +0 -81
  315. data/spec/Features/method_is_nan_spec.rb +0 -12
  316. data/spec/Features/method_map_spec.rb +0 -54
  317. data/spec/Features/method_max_with.rb +0 -20
  318. data/spec/Features/method_min_with.rb +0 -19
  319. data/spec/Features/method_ne_spec.rb +0 -18
  320. data/spec/Features/method_project_spec.rb +0 -188
  321. data/spec/Features/method_ref_spec.rb +0 -27
  322. data/spec/Features/method_round_spec.rb +0 -11
  323. data/spec/Features/method_s_linspace_spec.rb +0 -48
  324. data/spec/Features/method_s_span_spec.rb +0 -14
  325. data/spec/Features/method_seq_spec.rb +0 -47
  326. data/spec/Features/method_sort_with.rb +0 -43
  327. data/spec/Features/method_sorted_with.rb +0 -29
  328. data/spec/Features/method_span_spec.rb +0 -42
  329. data/spec/Features/method_wrap_readonly_spec.rb +0 -43
  330. data/spec/UnitTest/test_CAVirtual.rb +0 -214
  331. data/spec/spec_all.rb +0 -10
  332. data/utils/ca_ase.rb +0 -21
  333. data/utils/ca_methods.rb +0 -15
  334. data/utils/cast_checker.rb +0 -30
  335. data/utils/convert_test.rb +0 -73
  336. data/utils/extract_yard.rb +0 -22
  337. data/utils/guess_shape.rb +0 -76
  338. data/utils/monkey_patch_methods.rb +0 -62
  339. data/utils/remove_resource_fork.sh +0 -5
@@ -0,0 +1,416 @@
1
+ /* ---------------------------------------------------------------------------
2
+
3
+ Partition family (quickselect): `partition` (CARemap view via
4
+ partition_addr_ki) and `partition_copy` (eager per-fiber quickselect).
5
+ Sibling of carray_order.c (full sort + search surfaces) and
6
+ ca_sort_kernels.h (typed numeric kernels).
7
+
8
+ Dispatch by data_type:
9
+ numeric (i8..f64) -> typed ca_partition_quick_* kernels
10
+ CA_FIXLEN -> ca_quickselect_bytes (memcmp lexicographic order)
11
+ CA_OBJECT -> partition_index_ki trampoline (rb_funcall(<=>))
12
+
13
+ ---------------------------------------------------------------------------- */
14
+
15
+ #include "ruby.h"
16
+ #include "carray.h"
17
+ #include "ca_kernel_iterator.h" /* CA_FOR_EACH_FIBER_INOUT */
18
+ #include "ca_obj_face.h" /* CA_FACE_LIFT_IF_FACE */
19
+ #include "ca_sort_kernels.h" /* ca_partition_quick_* / ca_partition_nan_* */
20
+ #include <string.h> /* memcmp */
21
+ #include <math.h>
22
+
23
+ /* External entries reached at link time (extern declarations rather than
24
+ carray.h additions to keep the public header lean): */
25
+ extern VALUE rb_ca_remap_new (VALUE cary, VALUE rmapper); /* ca_obj_remap.c */
26
+ extern VALUE rb_ca_partition_addr_ki (VALUE self, VALUE vaxis, VALUE vkth); /* carray_kernels.c (generated, bind_ruby: false) */
27
+ /* _mp ("masked position") twin: explicit masked_last, used by `partition`
28
+ / `partition_copy` to pass masked_position: through (see MASKED_POSITION
29
+ rev1 in mkkernel.rb's MkKernel.sort doc). */
30
+ extern VALUE rb_ca_partition_addr_ki_mp (VALUE self, VALUE vaxis, VALUE vkth, int masked_last);
31
+
32
+ /* Generic comparator-based quickselect on a flat byte buffer.
33
+ *
34
+ * Reorders `buf[lo..hi]` (cell size = `bytes`) in place so that
35
+ * `buf[kth]` is the kth-smallest element under `cmp`, with all cells
36
+ * before kth <= pivot and all after >= pivot. Order within the two
37
+ * regions is unspecified. Average O(n). Median-of-three pivot +
38
+ * Hoare partition + one-side recursion. Insertion-sort base case for
39
+ * small ranges (< 8 cells).
40
+ *
41
+ * Comparator-based dispatch keeps this routine data_type-generic (used
42
+ * by the CA_FIXLEN partition_copy path); the mkkernel-generated
43
+ * partition_index_quickselect_* functions inline the comparator per
44
+ * dtype for the numeric paths.
45
+ *
46
+ * `swap_tmp` and `pivot` must be caller-provided scratch buffers of size
47
+ * `bytes` (used for cell swaps via memcpy; avoids alloca / per-swap
48
+ * malloc).
49
+ */
50
+ static void
51
+ ca_quickselect_bytes (char *buf, ca_size_t lo, ca_size_t hi,
52
+ ca_size_t kth, ca_size_t bytes,
53
+ int (*cmp)(const void *, const void *),
54
+ char *swap_tmp, char *pivot)
55
+ {
56
+ #define SWAP_CELL(_a, _b) \
57
+ do { \
58
+ memcpy(swap_tmp, (_a), (size_t) bytes); \
59
+ memcpy((_a), (_b), (size_t) bytes); \
60
+ memcpy((_b), swap_tmp, (size_t) bytes); \
61
+ } while (0)
62
+ #define CELL(_i) (buf + (_i) * bytes)
63
+ /* cmp == NULL signals raw memcmp over `bytes` (= CA_FIXLEN lexicographic
64
+ order; revived for the fixlen partition_copy path). Otherwise use the
65
+ supplied comparator (= numeric type-specific cmp). The ternary
66
+ evaluates each operand once (only one branch runs), so CELL(++i) side
67
+ effects are safe. */
68
+ #define QSCMP(_a, _b) ( cmp ? cmp((_a), (_b)) : memcmp((_a), (_b), (size_t) bytes) )
69
+
70
+ while ( lo < hi ) {
71
+ /* Small-range base case: insertion sort. */
72
+ if ( hi - lo < 8 ) {
73
+ for ( ca_size_t i = lo + 1; i <= hi; i++ ) {
74
+ memcpy(swap_tmp, CELL(i), (size_t) bytes);
75
+ ca_size_t j = i;
76
+ while ( j > lo && QSCMP(swap_tmp, CELL(j - 1)) < 0 ) {
77
+ memcpy(CELL(j), CELL(j - 1), (size_t) bytes);
78
+ j--;
79
+ }
80
+ memcpy(CELL(j), swap_tmp, (size_t) bytes);
81
+ }
82
+ return;
83
+ }
84
+ /* Median-of-three pivot: order buf[lo], buf[mid], buf[hi]. */
85
+ ca_size_t mid = lo + (hi - lo) / 2;
86
+ if ( QSCMP(CELL(mid), CELL(lo)) < 0 ) SWAP_CELL(CELL(mid), CELL(lo));
87
+ if ( QSCMP(CELL(hi), CELL(lo)) < 0 ) SWAP_CELL(CELL(hi), CELL(lo));
88
+ if ( QSCMP(CELL(hi), CELL(mid)) < 0 ) SWAP_CELL(CELL(hi), CELL(mid));
89
+ /* Stash pivot at hi-1 (Hoare partition variant). */
90
+ SWAP_CELL(CELL(mid), CELL(hi - 1));
91
+ /* Copy pivot into caller-provided scratch (avoid per-iteration
92
+ xmalloc; pivot lifetime is just this partition step). */
93
+ memcpy(pivot, CELL(hi - 1), (size_t) bytes);
94
+ /* Hoare partition (pivot at hi-1; scan lo..hi-2). */
95
+ ca_size_t i = lo, j = hi - 1;
96
+ for (;;) {
97
+ while ( QSCMP(CELL(++i), pivot) < 0 );
98
+ while ( QSCMP(CELL(--j), pivot) > 0 );
99
+ if ( i >= j ) break;
100
+ SWAP_CELL(CELL(i), CELL(j));
101
+ }
102
+ /* Restore pivot to its final position. */
103
+ SWAP_CELL(CELL(i), CELL(hi - 1));
104
+ /* buf[lo..i-1] <= pivot, buf[i] == pivot, buf[i+1..hi] >= pivot. */
105
+ if ( kth == i ) return;
106
+ else if ( kth < i ) hi = i - 1;
107
+ else lo = i + 1;
108
+ }
109
+ #undef SWAP_CELL
110
+ #undef CELL
111
+ #undef QSCMP
112
+ }
113
+
114
+ /* partition_copy(kth, axis: 0) — eager counterpart to `partition(kth,
115
+ * axis:)`. Returns a fresh entity CArray with the kth fiber-local
116
+ * position holding the kth-smallest value. Average O(n) per fiber via
117
+ * quickselect. Mask handling, axis kwarg, kth validation: identical to
118
+ * rb_ca_partitioned_view.
119
+ *
120
+ * C-callable twin of rb_ca_partition_copy (the Ruby entry) that skips
121
+ * rb_scan_args (the `:`-options form requires a proper Ruby method
122
+ * dispatch frame and segfaults when called from C). Takes vkth + vaxis
123
+ * directly. Non-static so carray_median_percentile.c can call it via
124
+ * an extern decl.
125
+ */
126
+ VALUE
127
+ rb_ca_partition_copy_c (VALUE self, VALUE vkth, VALUE vaxis)
128
+ {
129
+ /* Mask handling (= same as partition view). */
130
+ VALUE target = self;
131
+ CArray *cat;
132
+ TypedData_Get_Struct(target, CArray, &carray_data_type, cat);
133
+ if ( ca_has_mask(cat) ) {
134
+ if ( RTEST(rb_ca_is_any_masked(target)) ) {
135
+ rb_raise(rb_eArgError,
136
+ "partition_copy: masked input not supported "
137
+ "(use ca.value or ca.strip_mask(fill))");
138
+ }
139
+ target = rb_ca_value_array(target);
140
+ TypedData_Get_Struct(target, CArray, &carray_data_type, cat);
141
+ }
142
+
143
+ /* Normalize axis. */
144
+ int axis = NUM2INT(vaxis);
145
+ if ( axis < 0 ) axis += cat->ndim;
146
+ if ( axis < 0 || axis >= cat->ndim ) {
147
+ rb_raise(rb_eArgError, "partition_copy: axis %d out of range for ndim %d",
148
+ NUM2INT(vaxis), cat->ndim);
149
+ }
150
+
151
+ /* Normalize kth (-dim[axis] <= kth < dim[axis], negative counts from end). */
152
+ ca_size_t fiber_n = cat->dim[axis];
153
+ ca_size_t kth = (ca_size_t) NUM2SSIZET(vkth);
154
+ if ( kth < 0 ) kth += fiber_n;
155
+ if ( kth < 0 || kth >= fiber_n ) {
156
+ rb_raise(rb_eArgError,
157
+ "partition_copy: kth %ld out of range for axis %d (dim=%ld)",
158
+ (long) NUM2SSIZET(vkth), axis, (long) fiber_n);
159
+ }
160
+
161
+ /* CA_OBJECT branch: partition_index_ki carries the rb_funcall(<=>)
162
+ * comparator. Build the entity via partition_index_ki ->
163
+ * take_along_axis -> copy. Slower than the numeric inline quickselect,
164
+ * but the cost is the per-pair rb_funcall, not an architectural
165
+ * penalty. */
166
+ if ( cat->data_type == CA_OBJECT ) {
167
+ static ID id_partition_index_ki = 0;
168
+ static ID id_take_along_axis = 0;
169
+ static ID id_axis_sym = 0;
170
+ if ( !id_partition_index_ki ) {
171
+ id_partition_index_ki = rb_intern("partition_index_ki");
172
+ id_take_along_axis = rb_intern("take_along_axis");
173
+ id_axis_sym = rb_intern("axis");
174
+ }
175
+ VALUE idx = rb_funcall(target, id_partition_index_ki, 2,
176
+ INT2NUM(axis), SIZET2NUM(kth));
177
+ VALUE kw = rb_hash_new();
178
+ rb_hash_aset(kw, ID2SYM(id_axis_sym), INT2NUM(axis));
179
+ VALUE tla_argv[2] = { idx, kw };
180
+ VALUE view = rb_funcallv_kw(target, id_take_along_axis, 2, tla_argv,
181
+ RB_PASS_KEYWORDS);
182
+ return rb_ca_copy(view);
183
+ }
184
+
185
+ /* CA_FIXLEN branch: per-fiber quickselect via the generic byte-buffer
186
+ selector with raw memcmp ordering (cmp == NULL). Same lexicographic
187
+ total order as the fixlen sort/partition kernels and the bincmp
188
+ operators. Output preserves the fixlen byte width. */
189
+ if ( ca_is_fixlen_type(cat) ) {
190
+ ca_size_t fbytes = (ca_size_t) cat->bytes;
191
+ volatile VALUE vout = rb_ca_template_with_type(target,
192
+ INT2NUM(cat->data_type),
193
+ INT2NUM((int) fbytes));
194
+ CArray *cao;
195
+ TypedData_Get_Struct(vout, CArray, &carray_data_type, cao);
196
+ char *swap_tmp = ALLOCA_N(char, fbytes);
197
+ char *pivot = ALLOCA_N(char, fbytes);
198
+ ca_iter_state st_in, st_out;
199
+ char *pi, *po;
200
+ ca_size_t n;
201
+ CA_FOR_EACH_FIBER_INOUT(st_in, st_out, cat, cao, (int8_t) axis,
202
+ CA_KERNEL_NO_MASK, pi, po, n) {
203
+ memcpy(po, pi, (size_t) n * (size_t) fbytes);
204
+ if ( n > 1 ) {
205
+ ca_quickselect_bytes(po, 0, n - 1, kth, fbytes, NULL, swap_tmp, pivot);
206
+ }
207
+ }
208
+ return vout;
209
+ }
210
+
211
+ if ( cat->data_type != CA_BOOLEAN &&
212
+ (cat->data_type < CA_INT8 || cat->data_type > CA_FLOAT64) ) {
213
+ rb_raise(rb_eCADataTypeError,
214
+ "partition_copy: data_type %d not supported "
215
+ "(expected one of: bool, i8, u8, i16, u16, i32, u32, i64, u64, f32, f64, object)",
216
+ cat->data_type);
217
+ }
218
+
219
+ volatile VALUE vout = rb_ca_template_with_type(target,
220
+ INT2NUM(cat->data_type),
221
+ INT2NUM(0));
222
+ CArray *cao;
223
+ TypedData_Get_Struct(vout, CArray, &carray_data_type, cao);
224
+
225
+ /* Numeric path: per-dtype quickselect with inline cmp via the typed
226
+ ca_partition_quick_* kernels (avoids the function-pointer
227
+ indirection of the comparator-based ca_quickselect_bytes).
228
+ Float NaN policy = pre-partition NaN to tail (same convention as
229
+ sort_copy); if kth falls in the finite slice, quickselect over
230
+ finite; if kth >= finite_count, the cell is already NaN and no
231
+ further work is needed. */
232
+ ca_size_t bytes = (ca_size_t) cat->bytes;
233
+
234
+ ca_iter_state st_in, st_out;
235
+ char *pi, *po;
236
+ ca_size_t n;
237
+
238
+ CA_FOR_EACH_FIBER_INOUT(st_in, st_out, cat, cao, (int8_t) axis,
239
+ CA_KERNEL_NO_MASK, pi, po, n) {
240
+ memcpy(po, pi, (size_t) n * (size_t) bytes);
241
+ if ( n <= 1 ) continue;
242
+ switch ( cat->data_type ) {
243
+ case CA_BOOLEAN: ca_partition_quick_u8 ((uint8_t *) po, n, kth); break;
244
+ case CA_INT8: ca_partition_quick_i8 ((int8_t *) po, n, kth); break;
245
+ case CA_UINT8: ca_partition_quick_u8 ((uint8_t *) po, n, kth); break;
246
+ case CA_INT16: ca_partition_quick_i16((int16_t *) po, n, kth); break;
247
+ case CA_UINT16: ca_partition_quick_u16((uint16_t *) po, n, kth); break;
248
+ case CA_INT32: ca_partition_quick_i32((int32_t *) po, n, kth); break;
249
+ case CA_UINT32: ca_partition_quick_u32((uint32_t *) po, n, kth); break;
250
+ case CA_INT64: ca_partition_quick_i64((int64_t *) po, n, kth); break;
251
+ case CA_UINT64: ca_partition_quick_u64((uint64_t *) po, n, kth); break;
252
+ case CA_FLOAT32: {
253
+ ca_size_t fin = ca_partition_nan_f32((float32_t *) po, n);
254
+ if ( kth < fin ) ca_partition_quick_f32((float32_t *) po, fin, kth);
255
+ /* else: po[kth] is NaN already (= NaN-at-end policy), nothing to do */
256
+ break;
257
+ }
258
+ case CA_FLOAT64: {
259
+ ca_size_t fin = ca_partition_nan_f64((double *) po, n);
260
+ if ( kth < fin ) ca_partition_quick_f64((double *) po, fin, kth);
261
+ break;
262
+ }
263
+ default:
264
+ rb_raise(rb_eCADataTypeError,
265
+ "partition_copy: BUG: unexpected data_type %d", cat->data_type);
266
+ }
267
+ }
268
+
269
+ return vout;
270
+ }
271
+
272
+ /* masked_position: -aware twin of {rb_ca_partition_copy_c}. Masked
273
+ * cells are an incomparable sentinel (same role NaN plays for float
274
+ * dtypes): unmasked input takes the fast rb_ca_partition_copy_c path
275
+ * unchanged; masked input delegates to {partition} (which handles the
276
+ * masked_position split via partition_addr_ki_mp) + copy, mirroring the
277
+ * CA_FIXLEN / CA_OBJECT delegation pattern already used by sort_copy.
278
+ * rb_ca_partition_copy_c itself keeps raising on masked input (its
279
+ * other callers -- carray_median_percentile.c's flat lane -- always
280
+ * pre-strip the mask before calling it, per
281
+ * devel/MEMO_PER_AXIS_ORDER_STAT_MASK.md; per-axis order statistics
282
+ * with mask are a separate, still-open gap).
283
+ */
284
+ static VALUE
285
+ rb_ca_partition_copy_c_mp (VALUE self, VALUE vkth, VALUE vaxis, int masked_last)
286
+ {
287
+ CArray *ca;
288
+ TypedData_Get_Struct(self, CArray, &carray_data_type, ca);
289
+ if ( ! ca_has_mask(ca) ) {
290
+ return rb_ca_partition_copy_c(self, vkth, vaxis);
291
+ }
292
+ /* rb_ca_partitioned_view's rb_scan_args "1:" pattern needs call-frame
293
+ keyword-splat state, only set by full Ruby method dispatch -- a raw
294
+ C call segfaults/misparses (same constraint documented on
295
+ rb_ca_partition_copy_c above). Go through rb_funcallv_kw +
296
+ RB_PASS_KEYWORDS instead, same pattern as the CA_OBJECT branch in
297
+ rb_ca_partition_copy_c below. */
298
+ static ID id_partition = 0;
299
+ if ( !id_partition ) id_partition = rb_intern("partition");
300
+ VALUE kw = rb_hash_new();
301
+ rb_hash_aset(kw, ID2SYM(rb_intern("axis")), vaxis);
302
+ rb_hash_aset(kw, ID2SYM(rb_intern("masked_position")), masked_last ? ID2SYM(rb_intern("last")) : ID2SYM(rb_intern("first")));
303
+ VALUE pv_argv[2] = { vkth, kw };
304
+ VALUE view = rb_funcallv_kw(self, id_partition, 2, pv_argv, RB_PASS_KEYWORDS);
305
+ VALUE obj = rb_ca_copy(view);
306
+ CA_FACE_LIFT_IF_FACE(obj, self, ca);
307
+ return obj;
308
+ }
309
+
310
+ /* Ruby entry: partition_copy(kth, axis: 0, masked_position: :last) */
311
+ static VALUE
312
+ rb_ca_partition_copy (int argc, VALUE *argv, VALUE self)
313
+ {
314
+ VALUE vkth, rkw = Qnil;
315
+ VALUE vaxis = INT2NUM(0);
316
+ VALUE vmasked_position = Qnil;
317
+ rb_scan_args(argc, argv, "1:", &vkth, &rkw);
318
+ rb_scan_options(rkw, "axis,masked_position", &vaxis, &vmasked_position);
319
+
320
+ int masked_last = 1;
321
+ if ( !NIL_P(vmasked_position) ) {
322
+ static ID sym_first = 0, sym_last = 0;
323
+ if ( !sym_first ) sym_first = rb_intern("first");
324
+ if ( !sym_last ) sym_last = rb_intern("last");
325
+ ID mp_id = SYM2ID(vmasked_position);
326
+ if ( mp_id == sym_last ) masked_last = 1;
327
+ else if ( mp_id == sym_first ) masked_last = 0;
328
+ else {
329
+ rb_raise(rb_eArgError,
330
+ "partition_copy: unknown masked_position %s (expected :first or :last)",
331
+ rb_id2name(mp_id));
332
+ }
333
+ }
334
+ return rb_ca_partition_copy_c_mp(self, vkth, vaxis, masked_last);
335
+ }
336
+
337
+ /* partition(kth, axis: 0, masked_position: :last)
338
+ *
339
+ * Returns a CARemap view of +self+ partitioned along +axis+ such that
340
+ * the cell at the kth fiber-local position contains the kth-smallest
341
+ * value, with all cells before it <= and all cells after >=. Order
342
+ * within the < and > regions is unspecified. Average O(n) per fiber
343
+ * via the partition_addr_ki kernel (mkkernel `:sort` kind, algorithm:
344
+ * :partition).
345
+ *
346
+ * Signature mirrors rb_ca_sorted_view: positional kth + optional axis: /
347
+ * masked_position: kwargs (default 0 / :last). Mask handling mirrors
348
+ * {sort}: masked cells are an incomparable sentinel clustered at
349
+ * masked_position:, excluded from the kth-selection; a kth landing in
350
+ * the masked cluster needs no selection (unspecified order, same
351
+ * contract as the < / > regions). kth validation matches
352
+ * partition_addr_ki (-dim[axis] <= kth < dim[axis], negative counts
353
+ * from end).
354
+ */
355
+ static VALUE
356
+ rb_ca_partitioned_view (int argc, VALUE *argv, VALUE self)
357
+ {
358
+ VALUE vkth, rkw = Qnil;
359
+ VALUE vaxis = INT2NUM(0);
360
+ VALUE vmasked_position = Qnil;
361
+
362
+ rb_scan_args(argc, argv, "1:", &vkth, &rkw);
363
+ rb_scan_options(rkw, "axis,masked_position", &vaxis, &vmasked_position);
364
+
365
+ int masked_last = 1;
366
+ if ( !NIL_P(vmasked_position) ) {
367
+ static ID sym_first = 0, sym_last = 0;
368
+ if ( !sym_first ) sym_first = rb_intern("first");
369
+ if ( !sym_last ) sym_last = rb_intern("last");
370
+ ID mp_id = SYM2ID(vmasked_position);
371
+ if ( mp_id == sym_last ) masked_last = 1;
372
+ else if ( mp_id == sym_first ) masked_last = 0;
373
+ else {
374
+ rb_raise(rb_eArgError,
375
+ "partition: unknown masked_position %s (expected :first or :last)",
376
+ rb_id2name(mp_id));
377
+ }
378
+ }
379
+
380
+ CArray *ca;
381
+ TypedData_Get_Struct(self, CArray, &carray_data_type, ca);
382
+
383
+ /* CA_FIXLEN flows through the same partition_addr_ki fixlen dialect
384
+ as numeric; masked_position: applies uniformly across dtypes. */
385
+ VALUE target = self;
386
+
387
+ /* partition_addr_ki_mp validates axis + kth, splits masked cells to
388
+ masked_position:, and quickselects per fiber over the unmasked
389
+ sub-range. Returns CA_SIZE same-shape array of view-flat addresses
390
+ suitable for direct ca_remap_new wrap; the remap gather carries
391
+ target's mask bits through so masked cells stay masked at their
392
+ clustered position (no extra output-mask step needed here). */
393
+ volatile VALUE sigma_addr = rb_ca_partition_addr_ki_mp(target, vaxis, vkth, masked_last);
394
+ {
395
+ VALUE obj = rb_ca_remap_new(target, sigma_addr);
396
+ CA_FACE_LIFT_IF_FACE(obj, self, ca);
397
+ return obj;
398
+ }
399
+ }
400
+
401
+ /* ----------------------------------------------------------------------- */
402
+
403
+ void
404
+ Init_carray_partition (void)
405
+ {
406
+ /* `partition(kth, axis: k)` returns a CARemap view via the
407
+ quickselect kernel partition_addr_ki. The eager-position sibling
408
+ `partition_index` (lib/carray/ordering.rb) wraps partition_index_ki
409
+ under the same convention. */
410
+ rb_define_method(rb_cCArray, "partition", rb_ca_partitioned_view, -1);
411
+
412
+ /* `partition_copy` is the eager counterpart to `partition`: returns a
413
+ fresh entity array via per-fiber quickselect, bypassing the CARemap
414
+ scatter layer. Same mask / axis / kth semantics as the view form. */
415
+ rb_define_method(rb_cCArray, "partition_copy", rb_ca_partition_copy, -1);
416
+ }