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,620 @@
1
+ /* ---------------------------------------------------------------------------
2
+
3
+ Portable textbook sort kernels: per-dtype quicksort + bottom-up
4
+ mergesort with inline comparison (no function-pointer indirection
5
+ like libc qsort / mergesort). All 10 numeric dtypes plus paired
6
+ forms for argsort.
7
+
8
+ Internal C API only (no Ruby surface, no Init function). Declared
9
+ in ca_sort_kernels.h. Callers: rb_ca_sort_copy (carray_sort.c),
10
+ rb_ca_partition_copy_c (carray_partition.c), and the mkkernel-
11
+ emitted sort_addr_ki kernels in carray_kernels.c.
12
+
13
+ Algorithms:
14
+
15
+ * quicksort (ca_sort_quick_<dtype>)
16
+ median-of-3 pivot, Hoare partition, strict `<`, insertion-sort
17
+ base (threshold 16), smaller-side recursion / larger-side
18
+ iterate (stack O(log n)). Depth-limit escape to mergesort for
19
+ worst-case O(n log n).
20
+
21
+ * mergesort (ca_sort_merge_<dtype>)
22
+ Bottom-up, ping-pong (cur / next) per pass with no inter-pass
23
+ memcpy. Strict `<` (stable: equal -> take-left). Insertion-
24
+ sort pre-pass that converts the input into R=16-wide sorted
25
+ blocks before the merge loop. Single-compare sorted-skip
26
+ (per-merge `!(cur[mid] < cur[mid-1])` test, true -> memcpy and
27
+ skip the merge loop; no run-state machine, distinct from
28
+ Timsort galloping).
29
+
30
+ * paired forms (ca_sort_quick_pair_<dtype> / ca_sort_merge_pair_<dtype>)
31
+ Same algorithms over (value, index) pairs for argsort
32
+ (sort_index / sort_addr). Stable tie-break by index is built
33
+ into the comparison, so quicksort is effectively stable for
34
+ argsort even though the algorithm itself is not.
35
+
36
+ * NaN partitioning (ca_partition_nan_<dtype> + _pair_<dtype>)
37
+ float32 / float64 only. One-pass Hoare-style partition that
38
+ separates `[finite | NaN]` regions so the downstream sort
39
+ kernel can operate on the finite slice with plain `<`.
40
+
41
+ * value-level quickselect (ca_partition_quick_<dtype>)
42
+ median-of-3 + Hoare + insertion base + one-sided recursion
43
+ into the side containing kth (quickselect property, expected
44
+ O(n)). Depth-limit escape to mergesort matches the sort
45
+ kernels.
46
+
47
+ ---------------------------------------------------------------------------*/
48
+
49
+ #include "carray.h"
50
+ #include "ca_sort_kernels.h"
51
+ #include <string.h>
52
+ #include <math.h>
53
+
54
+ #define CA_SORT_INSERTION_THRESHOLD 16
55
+
56
+ /* ---------------------------------------------------------------------------
57
+ DEFINE_SORT_MERGE(TYPE, SUFFIX)
58
+
59
+ Emits public `void ca_sort_merge_<SUFFIX>(TYPE *a, TYPE *aux,
60
+ ca_size_t n)`.
61
+ `aux` must be a caller-provided buffer of `n * sizeof(TYPE)` bytes.
62
+ --------------------------------------------------------------------------- */
63
+
64
+ #define DEFINE_SORT_MERGE(TYPE, SUFFIX) \
65
+ \
66
+ /* insertion-sort pre-pass on a[lo..hi-1] (half-open). R-wide blocks. \
67
+ Writes only into `a` (no buffer juggling). Body is intentionally \
68
+ identical to the quicksort insertion base. */ \
69
+ static void \
70
+ ca_sort_merge_##SUFFIX##_inspass (TYPE *a, ca_size_t n) \
71
+ { \
72
+ ca_size_t lo = 0; \
73
+ while ( lo < n ) { \
74
+ ca_size_t hi_excl = lo + CA_SORT_INSERTION_THRESHOLD; \
75
+ if ( hi_excl > n ) hi_excl = n; \
76
+ for ( ca_size_t k = lo + 1; k < hi_excl; k++ ) { \
77
+ TYPE v = a[k]; \
78
+ ca_size_t m = k; \
79
+ while ( m > lo && v < a[m - 1] ) { \
80
+ a[m] = a[m - 1]; \
81
+ m--; \
82
+ } \
83
+ a[m] = v; \
84
+ } \
85
+ lo = hi_excl; \
86
+ } \
87
+ } \
88
+ \
89
+ void \
90
+ ca_sort_merge_##SUFFIX (TYPE *a, TYPE *aux, ca_size_t n) \
91
+ { \
92
+ if ( n <= 1 ) return; \
93
+ \
94
+ /* Pre-pass: convert a[] into R=16-wide sorted blocks in place. */ \
95
+ ca_sort_merge_##SUFFIX##_inspass(a, n); \
96
+ \
97
+ TYPE *cur = a; \
98
+ TYPE *next = aux; \
99
+ \
100
+ /* Start width = R (= pre-pass output run width). */ \
101
+ for ( ca_size_t width = CA_SORT_INSERTION_THRESHOLD; \
102
+ width < n; \
103
+ width <<= 1 ) { \
104
+ for ( ca_size_t lo = 0; lo < n; lo += (width << 1) ) { \
105
+ ca_size_t mid = lo + width; \
106
+ ca_size_t hi = lo + (width << 1); \
107
+ /* CAREFUL: tail run clamp must run before the drain `while` \
108
+ loops below; without these the drains over-walk the buffer \
109
+ when n is not a power of 2 times width. */ \
110
+ if ( mid > n ) mid = n; \
111
+ if ( hi > n ) hi = n; \
112
+ \
113
+ /* Single-compare sorted-skip: if left tail <= right head, the merge \
114
+ is the identity in [lo, hi). Strict `<` here so equal heads also \
115
+ skip (stable: take-left). Cheap memcpy keeps invariant that \
116
+ `next[lo..hi)` is the merged output. */ \
117
+ if ( mid < hi && !(cur[mid] < cur[mid - 1]) ) { \
118
+ memcpy(next + lo, cur + lo, (size_t)(hi - lo) * sizeof(TYPE)); \
119
+ continue; \
120
+ } \
121
+ \
122
+ ca_size_t i = lo, j = mid, k = lo; \
123
+ /* strict `<` -> equal-key left-take -> stable. */ \
124
+ while ( i < mid && j < hi ) { \
125
+ if ( cur[j] < cur[i] ) next[k++] = cur[j++]; \
126
+ else next[k++] = cur[i++]; \
127
+ } \
128
+ /* Drain residual tail (paired with the clamp above). */ \
129
+ while ( i < mid ) next[k++] = cur[i++]; \
130
+ while ( j < hi ) next[k++] = cur[j++]; \
131
+ } \
132
+ /* ping-pong: swap roles for next pass. */ \
133
+ { TYPE *t = cur; cur = next; next = t; } \
134
+ } \
135
+ \
136
+ /* CAREFUL: parity check by POINTER IDENTITY, never by pass counter. \
137
+ The pre-pass + width loop may leave `cur` pointing at either `a` or \
138
+ `aux` depending on dataset size and the sorted-skip short-circuit; \
139
+ a pass-count parity check is unreliable. Single fix-up memcpy when \
140
+ the result landed in `aux`. */ \
141
+ if ( cur != a ) { \
142
+ memcpy(a, cur, (size_t) n * sizeof(TYPE)); \
143
+ } \
144
+ } \
145
+ struct ca_sort_merge_##SUFFIX##_eat_semicolon
146
+
147
+
148
+ /* ---------------------------------------------------------------------------
149
+ DEFINE_SORT_QUICK(TYPE, SUFFIX)
150
+
151
+ Emits public `void ca_sort_quick_<SUFFIX>(TYPE *a, ca_size_t n)`.
152
+ Algorithm: median-of-3 pivot + Hoare partition + insertion-sort
153
+ base (threshold 16) + smaller-side recursion / larger-side
154
+ iterate (stack O(log n)).
155
+
156
+ Worst-case guarantee: a recursion depth counter starts at
157
+ 2*floor(log2(n)) + 2. Once a branch exceeds that limit the
158
+ remaining subrange escapes to ca_sort_merge_<SUFFIX>, which is
159
+ O(n log n) regardless of input shape (= avoids heapsort by
160
+ reusing the existing merge path).
161
+ --------------------------------------------------------------------- */
162
+
163
+ #define DEFINE_SORT_QUICK(TYPE, SUFFIX) \
164
+ \
165
+ static void \
166
+ ca_sort_quick_##SUFFIX##_range (TYPE *a, ca_size_t lo, ca_size_t hi, \
167
+ int depth_limit) \
168
+ { \
169
+ while ( hi - lo > CA_SORT_INSERTION_THRESHOLD ) { \
170
+ if ( depth_limit <= 0 ) { \
171
+ /* Depth-limit escape: mergesort the remaining subrange. \
172
+ The surrounding quicksort loop has already partitioned the \
173
+ higher subranges, so only this [lo, hi] window needs the aux \
174
+ buffer (local malloc/free). */ \
175
+ ca_size_t n_remain = hi - lo + 1; \
176
+ TYPE *aux = (TYPE *) xmalloc((size_t) n_remain * sizeof(TYPE)); \
177
+ ca_sort_merge_##SUFFIX(a + lo, aux, n_remain); \
178
+ xfree(aux); \
179
+ return; \
180
+ } \
181
+ depth_limit--; \
182
+ ca_size_t mid = lo + (hi - lo) / 2; \
183
+ /* median-of-3: order a[lo] <= a[mid] <= a[hi] */ \
184
+ if ( a[mid] < a[lo] ) { TYPE t = a[lo]; a[lo] = a[mid]; a[mid] = t; } \
185
+ if ( a[hi] < a[lo] ) { TYPE t = a[lo]; a[lo] = a[hi]; a[hi] = t; } \
186
+ if ( a[hi] < a[mid] ) { TYPE t = a[mid]; a[mid] = a[hi]; a[hi] = t; } \
187
+ TYPE pivot = a[mid]; \
188
+ /* Hoare partition */ \
189
+ ca_size_t i = lo, j = hi; \
190
+ for (;;) { \
191
+ while ( a[i] < pivot ) i++; \
192
+ while ( pivot < a[j] ) j--; \
193
+ if ( i >= j ) break; \
194
+ { TYPE t = a[i]; a[i] = a[j]; a[j] = t; } \
195
+ i++; \
196
+ j--; \
197
+ } \
198
+ /* smaller side recurse, larger side iterate (stack O(log n)) */ \
199
+ if ( j - lo < hi - (j + 1) ) { \
200
+ ca_sort_quick_##SUFFIX##_range(a, lo, j, depth_limit); \
201
+ lo = j + 1; \
202
+ } else { \
203
+ ca_sort_quick_##SUFFIX##_range(a, j + 1, hi, depth_limit); \
204
+ hi = j; \
205
+ } \
206
+ } \
207
+ /* insertion-sort base case */ \
208
+ for ( ca_size_t k = lo + 1; k <= hi; k++ ) { \
209
+ TYPE v = a[k]; \
210
+ ca_size_t m = k; \
211
+ while ( m > lo && v < a[m - 1] ) { \
212
+ a[m] = a[m - 1]; \
213
+ m--; \
214
+ } \
215
+ a[m] = v; \
216
+ } \
217
+ } \
218
+ \
219
+ void \
220
+ ca_sort_quick_##SUFFIX (TYPE *a, ca_size_t n) \
221
+ { \
222
+ if ( n <= 1 ) return; \
223
+ /* depth_limit = 2 * floor(log2(n)) + 2, computed via integer \
224
+ bit-length (cheap and exact for ca_size_t). */ \
225
+ int depth_limit = 0; \
226
+ ca_size_t m = n; \
227
+ while ( m > 0 ) { depth_limit++; m >>= 1; } \
228
+ depth_limit *= 2; \
229
+ ca_sort_quick_##SUFFIX##_range(a, 0, n - 1, depth_limit); \
230
+ } \
231
+ struct ca_sort_quick_##SUFFIX##_eat_semicolon
232
+
233
+
234
+ /* ---------------------------------------------------------------------------
235
+ PAIR sort macros (argsort kernels).
236
+
237
+ The pair form sorts an array of (value, index) structs by value,
238
+ used by sort_index / sort_addr to obtain the permutation. Stable
239
+ tie-break is built into the comparison (equal values fall through
240
+ to compare indices), so quicksort is effectively stable for
241
+ argsort even though the algorithm itself is not.
242
+
243
+ Structurally identical to the scalar quick / merge macros above;
244
+ the only differences are (a) pair struct typedef, (b) inline cmp
245
+ that includes the index tie-break, (c) per-cell swap copies the
246
+ whole pair via TYPE temp.
247
+
248
+ Pair memory footprint: sizeof(TYPE) + sizeof(ca_size_t) (8 bytes
249
+ alignment-padded). Caller allocates N pairs once per fiber
250
+ (mkkernel emit_sort_native in carray_kernels.c).
251
+ --------------------------------------------------------------------- */
252
+
253
+ /* Pair struct typedefs live in carray.h so callers (mkkernel emit) can
254
+ allocate them by name. The macro below skips the typedef and only
255
+ defines the algorithm. */
256
+ #define DEFINE_SORT_PAIR(TYPE, SUFFIX) \
257
+ \
258
+ /* ---- pair mergesort (= stable; called by quick escape too) ---- */ \
259
+ \
260
+ static void \
261
+ ca_sort_merge_pair_##SUFFIX##_inspass (ca_pair_##SUFFIX *a, ca_size_t n) \
262
+ { \
263
+ ca_size_t lo = 0; \
264
+ while ( lo < n ) { \
265
+ ca_size_t hi_excl = lo + CA_SORT_INSERTION_THRESHOLD; \
266
+ if ( hi_excl > n ) hi_excl = n; \
267
+ for ( ca_size_t k = lo + 1; k < hi_excl; k++ ) { \
268
+ ca_pair_##SUFFIX v = a[k]; \
269
+ ca_size_t m = k; \
270
+ /* stable cmp: equal values keep ascending index (= left-take) */ \
271
+ while ( m > lo && \
272
+ ( v.v < a[m - 1].v || \
273
+ (!(a[m - 1].v < v.v) && v.i < a[m - 1].i) ) ) { \
274
+ a[m] = a[m - 1]; \
275
+ m--; \
276
+ } \
277
+ a[m] = v; \
278
+ } \
279
+ lo = hi_excl; \
280
+ } \
281
+ } \
282
+ \
283
+ void \
284
+ ca_sort_merge_pair_##SUFFIX (ca_pair_##SUFFIX *a, \
285
+ ca_pair_##SUFFIX *aux, \
286
+ ca_size_t n) \
287
+ { \
288
+ if ( n <= 1 ) return; \
289
+ ca_sort_merge_pair_##SUFFIX##_inspass(a, n); \
290
+ ca_pair_##SUFFIX *cur = a; \
291
+ ca_pair_##SUFFIX *next = aux; \
292
+ for ( ca_size_t width = CA_SORT_INSERTION_THRESHOLD; \
293
+ width < n; \
294
+ width <<= 1 ) { \
295
+ for ( ca_size_t lo = 0; lo < n; lo += (width << 1) ) { \
296
+ ca_size_t mid = lo + width; \
297
+ ca_size_t hi = lo + (width << 1); \
298
+ if ( mid > n ) mid = n; \
299
+ if ( hi > n ) hi = n; \
300
+ /* sorted-skip: left-tail <= right-head (stable cmp) */ \
301
+ if ( mid < hi ) { \
302
+ TYPE lt = cur[mid - 1].v; \
303
+ TYPE rh = cur[mid].v; \
304
+ ca_size_t li = cur[mid - 1].i; \
305
+ ca_size_t ri = cur[mid].i; \
306
+ int strict_left_smaller = (lt < rh) || \
307
+ (!(rh < lt) && li < ri); \
308
+ int equal = !(lt < rh) && !(rh < lt) && (li == ri); \
309
+ if ( strict_left_smaller || equal ) { \
310
+ memcpy(next + lo, cur + lo, \
311
+ (size_t)(hi - lo) * sizeof(ca_pair_##SUFFIX)); \
312
+ continue; \
313
+ } \
314
+ } \
315
+ ca_size_t i = lo, j = mid, k = lo; \
316
+ while ( i < mid && j < hi ) { \
317
+ /* stable: if cur[j] < cur[i] strictly, take j; else take i */ \
318
+ int take_j = (cur[j].v < cur[i].v) || \
319
+ (!(cur[i].v < cur[j].v) && cur[j].i < cur[i].i); \
320
+ if ( take_j ) next[k++] = cur[j++]; \
321
+ else next[k++] = cur[i++]; \
322
+ } \
323
+ while ( i < mid ) next[k++] = cur[i++]; \
324
+ while ( j < hi ) next[k++] = cur[j++]; \
325
+ } \
326
+ { ca_pair_##SUFFIX *t = cur; cur = next; next = t; } \
327
+ } \
328
+ if ( cur != a ) { \
329
+ memcpy(a, cur, (size_t) n * sizeof(ca_pair_##SUFFIX)); \
330
+ } \
331
+ } \
332
+ \
333
+ /* ---- pair quicksort (= stable via index tie-break + depth-limit escape) ----*/ \
334
+ \
335
+ static void \
336
+ ca_sort_quick_pair_##SUFFIX##_range (ca_pair_##SUFFIX *a, \
337
+ ca_size_t lo, ca_size_t hi, \
338
+ int depth_limit) \
339
+ { \
340
+ while ( hi - lo > CA_SORT_INSERTION_THRESHOLD ) { \
341
+ if ( depth_limit <= 0 ) { \
342
+ ca_size_t n_remain = hi - lo + 1; \
343
+ ca_pair_##SUFFIX *aux = \
344
+ (ca_pair_##SUFFIX *) xmalloc((size_t) n_remain * sizeof(ca_pair_##SUFFIX)); \
345
+ ca_sort_merge_pair_##SUFFIX(a + lo, aux, n_remain); \
346
+ xfree(aux); \
347
+ return; \
348
+ } \
349
+ depth_limit--; \
350
+ ca_size_t mid = lo + (hi - lo) / 2; \
351
+ /* median-of-3 by value (tie-break NOT applied here -- approximate \
352
+ median is fine; full stable cmp applies during partition). */ \
353
+ if ( a[mid].v < a[lo].v ) { ca_pair_##SUFFIX t = a[lo]; a[lo] = a[mid]; a[mid] = t; } \
354
+ if ( a[hi].v < a[lo].v ) { ca_pair_##SUFFIX t = a[lo]; a[lo] = a[hi]; a[hi] = t; } \
355
+ if ( a[hi].v < a[mid].v ) { ca_pair_##SUFFIX t = a[mid]; a[mid] = a[hi]; a[hi] = t; } \
356
+ TYPE pivot_v = a[mid].v; \
357
+ ca_size_t pivot_i = a[mid].i; \
358
+ /* Hoare partition with stable cmp (= compare by v, tie-break by i). */ \
359
+ ca_size_t i = lo, j = hi; \
360
+ for (;;) { \
361
+ while ( a[i].v < pivot_v || \
362
+ (!(pivot_v < a[i].v) && a[i].i < pivot_i) ) i++; \
363
+ while ( pivot_v < a[j].v || \
364
+ (!(a[j].v < pivot_v) && pivot_i < a[j].i) ) j--; \
365
+ if ( i >= j ) break; \
366
+ { ca_pair_##SUFFIX t = a[i]; a[i] = a[j]; a[j] = t; } \
367
+ i++; \
368
+ j--; \
369
+ } \
370
+ if ( j - lo < hi - (j + 1) ) { \
371
+ ca_sort_quick_pair_##SUFFIX##_range(a, lo, j, depth_limit); \
372
+ lo = j + 1; \
373
+ } else { \
374
+ ca_sort_quick_pair_##SUFFIX##_range(a, j + 1, hi, depth_limit); \
375
+ hi = j; \
376
+ } \
377
+ } \
378
+ /* insertion-sort base (stable cmp). */ \
379
+ for ( ca_size_t k = lo + 1; k <= hi; k++ ) { \
380
+ ca_pair_##SUFFIX v = a[k]; \
381
+ ca_size_t m = k; \
382
+ while ( m > lo && \
383
+ ( v.v < a[m - 1].v || \
384
+ (!(a[m - 1].v < v.v) && v.i < a[m - 1].i) ) ) { \
385
+ a[m] = a[m - 1]; \
386
+ m--; \
387
+ } \
388
+ a[m] = v; \
389
+ } \
390
+ } \
391
+ \
392
+ void \
393
+ ca_sort_quick_pair_##SUFFIX (ca_pair_##SUFFIX *a, ca_size_t n) \
394
+ { \
395
+ if ( n <= 1 ) return; \
396
+ int depth_limit = 0; \
397
+ ca_size_t m = n; \
398
+ while ( m > 0 ) { depth_limit++; m >>= 1; } \
399
+ depth_limit *= 2; \
400
+ ca_sort_quick_pair_##SUFFIX##_range(a, 0, n - 1, depth_limit); \
401
+ } \
402
+ struct ca_sort_pair_##SUFFIX##_eat_semicolon
403
+
404
+
405
+ /* ===== Per-dtype instantiations ===========================================
406
+ `<` is well-defined and IEEE-stable for all numeric C types used
407
+ here; NaN handling for f32 / f64 lives in the pre-partition pass
408
+ below. */
409
+
410
+ /* ---------------------------------------------------------------------------
411
+ NaN pre-partition (float dtypes only).
412
+
413
+ One-pass Hoare-style partition that separates `a[0..n)` into
414
+ `[finite | NaN]` regions in place, returning the finite count k
415
+ (the NaN region starts at a[k..n)). Unstable within the finite
416
+ region (original order of finite values not preserved) -- the
417
+ downstream sort kernel reorders them anyway.
418
+
419
+ Loop invariant:
420
+ - a[0 .. i) all finite
421
+ - a[j .. n) all NaN
422
+ - a[i .. j) unprocessed
423
+ Terminates when i == j.
424
+
425
+ `isnan(a[i])` is the only NaN-aware logic. The downstream sort
426
+ kernels treat the finite slice with plain `<`, which is well-
427
+ defined and IEEE-stable for finite operands.
428
+ --------------------------------------------------------------------------- */
429
+
430
+ #define DEFINE_PARTITION_NAN(TYPE, SUFFIX) \
431
+ ca_size_t \
432
+ ca_partition_nan_##SUFFIX (TYPE *a, ca_size_t n) \
433
+ { \
434
+ ca_size_t i = 0, j = n; \
435
+ while ( i < j ) { \
436
+ if ( isnan(a[i]) ) { \
437
+ j--; \
438
+ TYPE t = a[j]; a[j] = a[i]; a[i] = t; \
439
+ } else { \
440
+ i++; \
441
+ } \
442
+ } \
443
+ return i; \
444
+ } \
445
+ struct ca_partition_nan_##SUFFIX##_eat_semicolon
446
+
447
+ DEFINE_PARTITION_NAN(float32_t, f32);
448
+ DEFINE_PARTITION_NAN(double, f64);
449
+
450
+ /* Pair variant for argsort: separates ca_pair_<dtype> by
451
+ isnan(v.value). Returns the finite count.
452
+
453
+ Stable within both finite and NaN regions (original order
454
+ preserved), so argsort on NaN-containing input keeps NaN indices
455
+ in ascending order.
456
+
457
+ Implementation: 2-pass stable filter via xmalloc scratch (single
458
+ xmalloc + xfree per fiber; negligible overhead vs the sort
459
+ itself). */
460
+ #define DEFINE_PARTITION_NAN_PAIR(TYPE, SUFFIX) \
461
+ ca_size_t \
462
+ ca_partition_nan_pair_##SUFFIX (ca_pair_##SUFFIX *a, ca_size_t n) \
463
+ { \
464
+ if ( n == 0 ) return 0; \
465
+ ca_pair_##SUFFIX *scratch = \
466
+ (ca_pair_##SUFFIX *) xmalloc((size_t) n * sizeof(ca_pair_##SUFFIX)); \
467
+ ca_size_t fin = 0, nan_pos = 0; \
468
+ for ( ca_size_t k = 0; k < n; k++ ) { \
469
+ if ( !isnan(a[k].v) ) scratch[fin++] = a[k]; \
470
+ } \
471
+ ca_size_t finite_count = fin; \
472
+ for ( ca_size_t k = 0; k < n; k++ ) { \
473
+ if ( isnan(a[k].v) ) scratch[finite_count + nan_pos++] = a[k]; \
474
+ } \
475
+ memcpy(a, scratch, (size_t) n * sizeof(ca_pair_##SUFFIX)); \
476
+ xfree(scratch); \
477
+ return finite_count; \
478
+ } \
479
+ struct ca_partition_nan_pair_##SUFFIX##_eat_semicolon
480
+
481
+ DEFINE_PARTITION_NAN_PAIR(float32_t, f32);
482
+ DEFINE_PARTITION_NAN_PAIR(double, f64);
483
+
484
+
485
+ /* ---------------------------------------------------------------------------
486
+ DEFINE_PARTITION_QUICK(TYPE, SUFFIX) -- value-level quickselect.
487
+
488
+ Emits public `void ca_partition_quick_<SUFFIX>(TYPE *a,
489
+ ca_size_t n, ca_size_t kth)` that reorders `a[0..n)` so that:
490
+ - a[kth] is the kth-smallest element under `<`
491
+ - a[0..kth-1] all compare <= a[kth]
492
+ - a[kth+1..n-1] all compare >= a[kth]
493
+ Order within the two regions is unspecified.
494
+
495
+ Algorithm: median-of-3 pivot + Hoare partition + insertion-sort
496
+ base (threshold 16) + one-sided recursion into the side
497
+ containing kth (true quickselect: expected O(n), stack O(log n)).
498
+
499
+ Worst-case guarantee: depth_limit = 2*floor(log2(n)) + 2 (matches
500
+ the sort kernels). On escape, mergesort the residual window;
501
+ mergesort fully sorts the window, which is strictly stronger than
502
+ the partition contract, so kth ends up correct as a side-effect.
503
+ Cost on escape: O(n log n) on the residual window only.
504
+ --------------------------------------------------------------------- */
505
+
506
+ #define DEFINE_PARTITION_QUICK(TYPE, SUFFIX) \
507
+ \
508
+ static void \
509
+ ca_partition_quick_##SUFFIX##_range (TYPE *a, ca_size_t lo, ca_size_t hi, \
510
+ ca_size_t kth, int depth_limit) \
511
+ { \
512
+ while ( hi - lo > CA_SORT_INSERTION_THRESHOLD ) { \
513
+ if ( depth_limit <= 0 ) { \
514
+ /* Depth-limit escape: mergesort the residual window. A full \
515
+ sort is strictly stronger than the partition contract, so kth \
516
+ ends up correct as a side-effect. Cheaper than continuing to \
517
+ degrade on adversarial input. */ \
518
+ ca_size_t n_remain = hi - lo + 1; \
519
+ TYPE *aux = (TYPE *) xmalloc((size_t) n_remain * sizeof(TYPE)); \
520
+ ca_sort_merge_##SUFFIX(a + lo, aux, n_remain); \
521
+ xfree(aux); \
522
+ return; \
523
+ } \
524
+ depth_limit--; \
525
+ ca_size_t mid = lo + (hi - lo) / 2; \
526
+ /* median-of-3: order a[lo] <= a[mid] <= a[hi]. */ \
527
+ if ( a[mid] < a[lo] ) { TYPE t = a[lo]; a[lo] = a[mid]; a[mid] = t; } \
528
+ if ( a[hi] < a[lo] ) { TYPE t = a[lo]; a[lo] = a[hi]; a[hi] = t; } \
529
+ if ( a[hi] < a[mid] ) { TYPE t = a[mid]; a[mid] = a[hi]; a[hi] = t; } \
530
+ TYPE pivot = a[mid]; \
531
+ /* Hoare partition. */ \
532
+ ca_size_t i = lo, j = hi; \
533
+ for (;;) { \
534
+ while ( a[i] < pivot ) i++; \
535
+ while ( pivot < a[j] ) j--; \
536
+ if ( i >= j ) break; \
537
+ { TYPE t = a[i]; a[i] = a[j]; a[j] = t; } \
538
+ i++; \
539
+ j--; \
540
+ } \
541
+ /* After partition: \
542
+ a[lo..j] all compare <= pivot \
543
+ a[j+1..hi] all compare >= pivot \
544
+ Recurse only into the side containing kth. \
545
+ If kth <= j: kth is in the left half (which contains pivot \
546
+ boundary), so we tighten hi = j. \
547
+ Else: kth is in the right half, lo = j + 1. \
548
+ This is the quickselect property: expected O(n) work since each \
549
+ step halves the candidate window. */ \
550
+ if ( kth <= j ) { \
551
+ hi = j; \
552
+ } else { \
553
+ lo = j + 1; \
554
+ } \
555
+ } \
556
+ /* insertion-sort the residual base window so a[kth] is exact within \
557
+ [lo, hi]. Cheaper than another quickselect step and gives the \
558
+ partition contract for free across the small window. */ \
559
+ for ( ca_size_t k = lo + 1; k <= hi; k++ ) { \
560
+ TYPE v = a[k]; \
561
+ ca_size_t m = k; \
562
+ while ( m > lo && v < a[m - 1] ) { \
563
+ a[m] = a[m - 1]; \
564
+ m--; \
565
+ } \
566
+ a[m] = v; \
567
+ } \
568
+ } \
569
+ \
570
+ void \
571
+ ca_partition_quick_##SUFFIX (TYPE *a, ca_size_t n, ca_size_t kth) \
572
+ { \
573
+ if ( n <= 1 ) return; \
574
+ if ( kth >= n ) return; /* out-of-range kth = no-op, caller validates */ \
575
+ int depth_limit = 0; \
576
+ ca_size_t m = n; \
577
+ while ( m > 0 ) { depth_limit++; m >>= 1; } \
578
+ depth_limit *= 2; \
579
+ ca_partition_quick_##SUFFIX##_range(a, 0, n - 1, kth, depth_limit); \
580
+ } \
581
+ struct ca_partition_quick_##SUFFIX##_eat_semicolon
582
+
583
+
584
+ DEFINE_SORT_QUICK(int8_t, i8); DEFINE_SORT_MERGE(int8_t, i8);
585
+ DEFINE_SORT_QUICK(uint8_t, u8); DEFINE_SORT_MERGE(uint8_t, u8);
586
+ DEFINE_SORT_QUICK(int16_t, i16); DEFINE_SORT_MERGE(int16_t, i16);
587
+ DEFINE_SORT_QUICK(uint16_t, u16); DEFINE_SORT_MERGE(uint16_t, u16);
588
+ DEFINE_SORT_QUICK(int32_t, i32); DEFINE_SORT_MERGE(int32_t, i32);
589
+ DEFINE_SORT_QUICK(uint32_t, u32); DEFINE_SORT_MERGE(uint32_t, u32);
590
+ DEFINE_SORT_QUICK(int64_t, i64); DEFINE_SORT_MERGE(int64_t, i64);
591
+ DEFINE_SORT_QUICK(uint64_t, u64); DEFINE_SORT_MERGE(uint64_t, u64);
592
+ DEFINE_SORT_QUICK(float32_t, f32); DEFINE_SORT_MERGE(float32_t, f32);
593
+ DEFINE_SORT_QUICK(double, f64); DEFINE_SORT_MERGE(double, f64);
594
+
595
+ /* Pair sort instantiations for argsort (sort_index / sort_addr). */
596
+ DEFINE_SORT_PAIR(int8_t, i8);
597
+ DEFINE_SORT_PAIR(uint8_t, u8);
598
+ DEFINE_SORT_PAIR(int16_t, i16);
599
+ DEFINE_SORT_PAIR(uint16_t, u16);
600
+ DEFINE_SORT_PAIR(int32_t, i32);
601
+ DEFINE_SORT_PAIR(uint32_t, u32);
602
+ DEFINE_SORT_PAIR(int64_t, i64);
603
+ DEFINE_SORT_PAIR(uint64_t, u64);
604
+ DEFINE_SORT_PAIR(float32_t, f32);
605
+ DEFINE_SORT_PAIR(double, f64);
606
+
607
+ /* Value-level quickselect for partition_copy. */
608
+ DEFINE_PARTITION_QUICK(int8_t, i8);
609
+ DEFINE_PARTITION_QUICK(uint8_t, u8);
610
+ DEFINE_PARTITION_QUICK(int16_t, i16);
611
+ DEFINE_PARTITION_QUICK(uint16_t, u16);
612
+ DEFINE_PARTITION_QUICK(int32_t, i32);
613
+ DEFINE_PARTITION_QUICK(uint32_t, u32);
614
+ DEFINE_PARTITION_QUICK(int64_t, i64);
615
+ DEFINE_PARTITION_QUICK(uint64_t, u64);
616
+ DEFINE_PARTITION_QUICK(float32_t, f32);
617
+ DEFINE_PARTITION_QUICK(double, f64);
618
+
619
+ /* No Init function: this file exposes only C kernels (via ca_sort_kernels.h);
620
+ it registers no Ruby methods. */