carray 2.0.1 → 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 +772 -297
  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 +229 -791
  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 +207 -240
  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 -329
  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 +1341 -198
  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 +5 -15
  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/mailmap +0 -1
  269. data/misc/Methods.ja.md +0 -182
  270. data/misc/NOTE +0 -51
  271. data/spec/Classes/CABitfield_spec.rb +0 -58
  272. data/spec/Classes/CABlockIterator_spec.rb +0 -114
  273. data/spec/Classes/CABlock_spec.rb +0 -205
  274. data/spec/Classes/CAField_spec.rb +0 -39
  275. data/spec/Classes/CAGrid_spec.rb +0 -75
  276. data/spec/Classes/CAMap_spec.rb +0 -0
  277. data/spec/Classes/CAMapping_spec.rb +0 -105
  278. data/spec/Classes/CAObject_attribute_spec.rb +0 -33
  279. data/spec/Classes/CAObject_spec.rb +0 -33
  280. data/spec/Classes/CARefer_spec.rb +0 -93
  281. data/spec/Classes/CARepeat_spec.rb +0 -65
  282. data/spec/Classes/CASelect_spec.rb +0 -22
  283. data/spec/Classes/CAShift_spec.rb +0 -16
  284. data/spec/Classes/CAStruct_spec.rb +0 -71
  285. data/spec/Classes/CATranspose_spec.rb +0 -60
  286. data/spec/Classes/CAUnboudRepeat_spec.rb +0 -102
  287. data/spec/Classes/CAWindow_spec.rb +0 -54
  288. data/spec/Classes/CAWrap_spec.rb +0 -8
  289. data/spec/Classes/CArray_spec.rb +0 -184
  290. data/spec/Classes/CScalar_spec.rb +0 -55
  291. data/spec/Features/feature_130_spec.rb +0 -19
  292. data/spec/Features/feature_attributes_spec.rb +0 -280
  293. data/spec/Features/feature_boolean_spec.rb +0 -98
  294. data/spec/Features/feature_broadcast.rb +0 -116
  295. data/spec/Features/feature_cast_function.rb +0 -19
  296. data/spec/Features/feature_cast_spec.rb +0 -33
  297. data/spec/Features/feature_class_spec.rb +0 -84
  298. data/spec/Features/feature_complex_spec.rb +0 -42
  299. data/spec/Features/feature_composite_spec.rb +0 -124
  300. data/spec/Features/feature_convert_spec.rb +0 -46
  301. data/spec/Features/feature_copy_spec.rb +0 -123
  302. data/spec/Features/feature_creation_spec.rb +0 -84
  303. data/spec/Features/feature_element_spec.rb +0 -144
  304. data/spec/Features/feature_extream_spec.rb +0 -54
  305. data/spec/Features/feature_generate_spec.rb +0 -74
  306. data/spec/Features/feature_index_spec.rb +0 -69
  307. data/spec/Features/feature_mask_spec.rb +0 -580
  308. data/spec/Features/feature_math_spec.rb +0 -97
  309. data/spec/Features/feature_order_spec.rb +0 -146
  310. data/spec/Features/feature_ref_store_spec.rb +0 -209
  311. data/spec/Features/feature_serialization_spec.rb +0 -125
  312. data/spec/Features/feature_stat_spec.rb +0 -397
  313. data/spec/Features/feature_virtual_spec.rb +0 -48
  314. data/spec/Features/method_eq_spec.rb +0 -81
  315. data/spec/Features/method_is_nan_spec.rb +0 -12
  316. data/spec/Features/method_map_spec.rb +0 -54
  317. data/spec/Features/method_max_with.rb +0 -20
  318. data/spec/Features/method_min_with.rb +0 -19
  319. data/spec/Features/method_ne_spec.rb +0 -18
  320. data/spec/Features/method_project_spec.rb +0 -188
  321. data/spec/Features/method_ref_spec.rb +0 -27
  322. data/spec/Features/method_round_spec.rb +0 -11
  323. data/spec/Features/method_s_linspace_spec.rb +0 -48
  324. data/spec/Features/method_s_span_spec.rb +0 -14
  325. data/spec/Features/method_seq_spec.rb +0 -47
  326. data/spec/Features/method_sort_with.rb +0 -43
  327. data/spec/Features/method_sorted_with.rb +0 -29
  328. data/spec/Features/method_span_spec.rb +0 -42
  329. data/spec/Features/method_wrap_readonly_spec.rb +0 -43
  330. data/spec/UnitTest/test_CAVirtual.rb +0 -214
  331. data/spec/spec_all.rb +0 -10
  332. data/utils/ca_ase.rb +0 -21
  333. data/utils/ca_methods.rb +0 -15
  334. data/utils/cast_checker.rb +0 -30
  335. data/utils/convert_test.rb +0 -73
  336. data/utils/extract_yard.rb +0 -22
  337. data/utils/guess_shape.rb +0 -76
  338. data/utils/monkey_patch_methods.rb +0 -62
  339. data/utils/remove_resource_fork.sh +0 -5
data/ext/carray_order.c CHANGED
@@ -1,27 +1,17 @@
1
- /* ---------------------------------------------------------------------------
2
-
3
- carray_order.c
4
-
5
- This file is part of Ruby/CArray extension library.
6
-
7
- Copyright (C) 2005-2025 Hiroki Motoyoshi
8
-
9
- ---------------------------------------------------------------------------- */
10
-
11
1
  #include "ruby.h"
12
2
  #include "carray.h"
3
+ #include "ca_kernel_iterator.h"
4
+ #include "ca_obj_face.h"
13
5
  #include <math.h>
14
6
  #include <float.h>
7
+ #include <stdlib.h>
15
8
 
16
- static ID id_equal;
17
-
18
- static VALUE
19
- rb_ca_value_not_masked (VALUE self)
20
- {
21
- VALUE rval = rb_ca_value_array(self);
22
- VALUE select = rb_ca_is_not_masked(self);
23
- return rb_ca_fetch(rval, select);
24
- }
9
+ /* Interned IDs used by the dispatchers below. rb_funcall is retained
10
+ * only where there is no clean C entry: `-` (operator dispatch in
11
+ * rb_ca_order handles Integer vs CArray uniformly via method dispatch)
12
+ * and Symbol tag comparison for `method:` kwarg. */
13
+ static ID id_axis, id_sub,
14
+ id_sym_binary, id_sym_linear;
25
15
 
26
16
  /* ------------------------------------------------------------------- */
27
17
 
@@ -62,6 +52,8 @@ rb_ca_value_not_masked (VALUE self)
62
52
  } \
63
53
  }
64
54
 
55
+ /* Inner loop of ca_project: gather ca[ci[i]] into co with optional
56
+ * lfill/ufill for out-of-range indices. Called only by ca_project. */
65
57
  static void
66
58
  ca_project_loop (CArray *co, CArray *ca, CArray *ci, char *lfill, char *ufill)
67
59
  {
@@ -119,8 +111,8 @@ ca_project_loop (CArray *co, CArray *ca, CArray *ci, char *lfill, char *ufill)
119
111
  }
120
112
  break;
121
113
  }
122
- free(mi);
123
- free(ma);
114
+ xfree(mi);
115
+ xfree(ma);
124
116
  }
125
117
 
126
118
  CArray *
@@ -138,27 +130,36 @@ ca_project (CArray *ca, CArray *ci, char *lfill, char *ufill)
138
130
  return co;
139
131
  }
140
132
 
141
- /* @overload project (idx, lval=nil, uval=nil)
142
-
143
- [TBD]. Creates new array the element of the object as address.
144
- */
145
-
133
+ /* project(idx, lval=nil, uval=nil) — gather: for each element of `idx`,
134
+ * pick `self[idx[i]]`. Returns an entity CArray shaped like `idx`.
135
+ *
136
+ * Out-of-range index handling: negative or >= self.elements indices
137
+ * produce `lval` (lower) / `uval` (upper) if provided, otherwise mask
138
+ * the output cell. Masked inputs (`idx` mask or `self` mask) also
139
+ * propagate as masked output.
140
+ */
146
141
  VALUE
147
142
  rb_ca_project (int argc, VALUE *argv, VALUE self)
148
143
  {
149
- volatile VALUE obj, ridx, vlfval, vufval;
144
+ volatile VALUE obj, ridx, vlfval, vufval, vstorage;
150
145
  CArray *ca, *ci, *co;
151
146
  char *lfval, *ufval;
147
+ int self_is_face;
152
148
 
153
149
  rb_scan_args(argc, argv, "12", (VALUE *)&ridx, (VALUE *) &vlfval, (VALUE *) &vufval);
154
150
 
155
151
  TypedData_Get_Struct(self, CArray, &carray_data_type, ca);
152
+ self_is_face = ca_is_face(ca);
156
153
 
157
154
  rb_check_carray_object(ridx);
158
155
  ci = ca_wrap_readonly(ridx, CA_SIZE);
159
156
 
160
- lfval = malloc_with_check(ca->bytes);
161
- ufval = malloc_with_check(ca->bytes);
157
+ /* The fill args arrive as surface values. rb_ca_obj2ptr owns the
158
+ surface->storage conversion: for a Face self it fires the scalar_to_storage
159
+ write hook (a datetime scalar / Time is reconciled to the Face's unit),
160
+ for a plain array it writes the storage bytes directly. */
161
+ lfval = xmalloc(ca->bytes);
162
+ ufval = xmalloc(ca->bytes);
162
163
 
163
164
  if ( ! NIL_P(vlfval) ) {
164
165
  rb_ca_obj2ptr(self, vlfval, lfval);
@@ -169,1519 +170,788 @@ rb_ca_project (int argc, VALUE *argv, VALUE self)
169
170
  rb_ca_obj2ptr(self, vufval, ufval);
170
171
  }
171
172
 
173
+ /* Strip Face -> gather on storage -> face-lift (the same tail sort/search
174
+ use, see docs/authoring/FaceOrderingSearch.md). The gather runs on the raw
175
+ storage so length / miss->UNDEF / fill semantics are unchanged; the
176
+ result is then re-wrapped as the source Face, and copy_state carries the
177
+ subclass state (datetime/timedelta unit, categorical labels). */
178
+ vstorage = self;
179
+ if ( self_is_face ) {
180
+ vstorage = rb_ca_strip_face_value(self);
181
+ TypedData_Get_Struct(vstorage, CArray, &carray_data_type, ca);
182
+ }
183
+
172
184
  co = ca_project(ca, ci,
173
185
  ( ! NIL_P(vlfval) ) ? lfval : NULL,
174
186
  ( ( ! NIL_P(vufval) ) || ( ! NIL_P(vlfval) ) ) ? ufval : NULL);
175
187
 
176
- free(lfval);
177
- free(ufval);
188
+ xfree(lfval);
189
+ xfree(ufval);
178
190
 
179
191
  obj = ca_wrap_struct(co);
180
- rb_ca_data_type_inherit(obj, self);
181
192
 
182
193
  if ( ! ca_is_any_masked(co) ) {
183
194
  obj = rb_ca_unmask_copy(obj);
184
195
  }
185
196
 
186
- return obj;
187
- }
188
-
189
- /* ----------------------------------------------------------------- */
190
-
191
- #define proc_reverse_bang_mask() \
192
- { \
193
- boolean8_t *p = (boolean8_t *)ca->mask->ptr; \
194
- boolean8_t *q = (boolean8_t *)ca->mask->ptr + ca->elements - 1; \
195
- boolean8_t v; \
196
- for (; p<q; p++, q--) { \
197
- v = *p; *p = *q; *q = v; \
198
- } \
199
- }
200
-
201
-
202
- #define proc_reverse_bang(type) \
203
- { \
204
- type *p = (type *)ca->ptr; \
205
- type *q = (type *)ca->ptr + ca->elements - 1; \
206
- type v; \
207
- for (; p<q; p++, q--) { \
208
- v = *p; *p = *q; *q = v; \
209
- } \
210
- }
211
-
212
- #define proc_reverse_bang_data() \
213
- { \
214
- ca_size_t bytes = ca->bytes; \
215
- char *p = ca->ptr; \
216
- char *q = ca->ptr + bytes * (ca->elements - 1); \
217
- char *v = malloc_with_check(bytes); \
218
- for (; p<q; p+=bytes, q-=bytes) { \
219
- memcpy(v, p, bytes); \
220
- memcpy(p, q, bytes); \
221
- memcpy(q, v, bytes); \
222
- } \
223
- free(v); \
197
+ if ( self_is_face ) {
198
+ obj = ca_face_lift(obj, self);
224
199
  }
225
200
 
226
- /* @overload reverse!
201
+ return obj;
202
+ }
227
203
 
228
- Reverses the elements of +ca+ in place.
229
- */
204
+ /* ------------------------------------------------------------------------- */
230
205
 
206
+ /* ---- search family kwarg trampolines (dual API: index vs addr) ------
207
+ *
208
+ * Each trampoline parses the `axis:` kwarg and dispatches:
209
+ * axis nil -> inline no-axis flat path: flatten self + flatten query
210
+ * (if CArray) + call the _ki kernel at axis 0, then
211
+ * reshape the result to the query's shape via
212
+ * ca_reshape_search_result
213
+ * axis k -> per-axis kernel from carray_kernels.c. The "index"
214
+ * variants use the :fiber_local kernels (axis-local
215
+ * position per fiber); the "addr" variants use the
216
+ * :view_flat kernels (view-flat address per fiber).
217
+ *
218
+ * For the no-axis path both variant families route through the index
219
+ * kernel (_ki, not _addr_ki): the 1-D flattening makes addr == index,
220
+ * so the two names share the same implementation.
221
+ */
222
+
223
+ extern VALUE rb_ca_bsearch_ki (VALUE self, VALUE rval, VALUE raxis);
224
+ extern VALUE rb_ca_bsearch_addr_ki (VALUE self, VALUE rval, VALUE raxis);
225
+ extern VALUE rb_ca_search_ki (int argc, VALUE *argv, VALUE self);
226
+ extern VALUE rb_ca_search_addr_ki (int argc, VALUE *argv, VALUE self);
227
+ extern VALUE rb_ca_search_nearest_ki (VALUE self, VALUE rval, VALUE raxis);
228
+ extern VALUE rb_ca_search_nearest_addr_ki (VALUE self, VALUE rval, VALUE raxis);
229
+
230
+ /* Direct C function entries used by the dispatchers below. Declared
231
+ * here when not already in carray.h / umbrella headers.
232
+ *
233
+ * The `_c` suffix marks "C-callable entry": a thin twin of the Ruby-
234
+ * binding function that skips rb_scan_args. Ruby's `:` kwarg parsing
235
+ * reads the call-frame keyword-splat state, which is only set by full
236
+ * method dispatch -- calling the Ruby entry directly from C would
237
+ * segfault. Pattern: Ruby entry parses argv via rb_scan_args and
238
+ * forwards to the `_c` twin.
239
+ */
240
+ extern VALUE rb_ca_sort_addr_c (VALUE self, VALUE axis, int stable, int masked_last);
241
+ extern VALUE rb_ca_axis2addr_c (VALUE self, VALUE vindices, VALUE vaxis);
242
+ extern VALUE rb_ca_count_not_masked_c (VALUE self, VALUE axis_val);
243
+ extern VALUE rb_ca_flip_axis (VALUE self, long axis);
244
+ extern VALUE rb_ca_argmin_addr_ki (int argc, VALUE *argv, VALUE self);
245
+ extern VALUE rb_ca_argmax_addr_ki (int argc, VALUE *argv, VALUE self);
246
+ extern VALUE rb_ca_minmax_ki (int argc, VALUE *argv, VALUE self);
247
+ extern VALUE rb_ca_linear_section_binary_ki (VALUE self, VALUE rval, VALUE raxis);
248
+ extern VALUE rb_ca_linear_section_linear_ki (VALUE self, VALUE rval, VALUE raxis);
249
+ extern VALUE rb_ca_linear_fetch_ki (VALUE self, VALUE rval, VALUE raxis);
250
+ extern VALUE rb_ca_insert_axis (int argc, VALUE *argv, VALUE self);
251
+ extern VALUE rb_ca_reshape (int argc, VALUE *argv, VALUE self);
252
+
253
+ /* mkkernel-emitted kernels marked `c_callable: true`: extern symbols
254
+ * in carray_kernels.c that allow direct C-level dispatch without going
255
+ * through rb_funcall + kwarg hash construction. */
256
+ extern VALUE rb_ca_sort_index_ki_quick (VALUE self, VALUE vaxis);
257
+ extern VALUE rb_ca_partition_index_ki (VALUE self, VALUE vaxis, VALUE vkth);
258
+ extern VALUE rb_ca_rank_index_ki_quick_dense (VALUE self, VALUE vaxis, int dense);
259
+
260
+ /* ---- no-axis (flat) search helper -----------------------------------
261
+ *
262
+ * Contract for the no-axis path (`axis: nil`):
263
+ * non-CArray query -> scalar result (Integer / nil)
264
+ * CArray query -> query-shaped CArray result (UNDEF cells on no-match)
265
+ *
266
+ * Implementation: flatten self + flatten query, run the per-axis _ki
267
+ * kernel at axis 0, then reshape the raw 1-D result to query's shape
268
+ * via the helper below.
269
+ */
270
+
271
+ /* Rebuild a query-shaped result from the kernel output. Two paths:
272
+ * (a) scalar collapse: _ki returns Integer / nil when query is a
273
+ * single-element CArray. Build a query-shaped entity directly
274
+ * and store the value (CA_UNDEF for nil -> auto-mask).
275
+ * (b) CArray result: reshape to query.shape (the helpers flatten the
276
+ * query before calling _ki, so the raw result is 1-D).
277
+ *
278
+ * Called by the search family trampolines below (bsearch_kw /
279
+ * search_kw / search_nearest_kw and their _addr siblings) on the
280
+ * no-axis path. */
231
281
  static VALUE
232
- rb_ca_reverse_bang (VALUE self)
282
+ ca_reshape_search_result (VALUE query, VALUE rraw)
233
283
  {
234
- CArray *ca;
235
-
236
- rb_ca_modify(self);
284
+ CArray *cq;
285
+ GetCArray(query, cq);
237
286
 
238
- TypedData_Get_Struct(self, CArray, &carray_data_type, ca);
239
- ca_attach(ca);
240
-
241
- switch ( ca->data_type ) {
242
- case CA_FIXLEN: proc_reverse_bang_data(); break;
243
- case CA_BOOLEAN:
244
- case CA_INT8:
245
- case CA_UINT8: proc_reverse_bang(int8_t); break;
246
- case CA_INT16:
247
- case CA_UINT16: proc_reverse_bang(int16_t); break;
248
- case CA_INT32:
249
- case CA_UINT32:
250
- case CA_FLOAT32: proc_reverse_bang(int32_t); break;
251
- case CA_INT64:
252
- case CA_UINT64:
253
- case CA_FLOAT64: proc_reverse_bang(float64_t); break;
254
- case CA_FLOAT128: proc_reverse_bang(float128_t); break;
255
- #ifdef HAVE_COMPLEX_H
256
- case CA_CMPLX64: proc_reverse_bang(float64_t); break;
257
- case CA_CMPLX128: proc_reverse_bang(cmplx128_t); break;
258
- case CA_CMPLX256: proc_reverse_bang(cmplx256_t); break;
259
- #endif
260
- case CA_OBJECT: proc_reverse_bang(VALUE); break;
261
- default:
262
- rb_raise(rb_eCADataTypeError, "[BUG] array has an unknown data type");
287
+ if ( ! rb_obj_is_carray(rraw) ) {
288
+ VALUE vr = rb_carray_new(CA_SIZE, cq->ndim, cq->dim, 0, NULL);
289
+ rb_ca_store_addr(vr, 0, NIL_P(rraw) ? CA_UNDEF : rraw);
290
+ return vr;
263
291
  }
264
292
 
265
- if ( ca_has_mask(ca) ) {
266
- proc_reverse_bang_mask();
293
+ VALUE shape_argv[CA_RANK_MAX];
294
+ for ( int8_t k = 0; k < cq->ndim; k++ ) {
295
+ shape_argv[k] = LONG2NUM((long) cq->dim[k]);
267
296
  }
268
-
269
- ca_sync(ca);
270
- ca_detach(ca);
271
-
272
- return self;
297
+ return rb_ca_reshape((int) cq->ndim, shape_argv, rraw);
273
298
  }
274
299
 
275
- /* @overload reverse
276
-
277
- Returns a new CArray object containing <i>ca</i>'s elements in
278
- reverse order.
279
- */
280
-
281
300
  static VALUE
282
- rb_ca_reversed_copy (VALUE self)
283
- {
284
- volatile VALUE out = rb_ca_copy(self);
285
- rb_ca_data_type_inherit(out, self);
286
- return rb_ca_reverse_bang(out);
287
- }
288
-
289
- /* ------------------------------------------------------------------------- */
290
-
291
- typedef struct {
292
- ca_size_t bytes;
293
- char *ptr;
294
- } cmp_data;
295
-
296
- typedef int (*ca_qsort_cmp_func)();
297
-
298
- #define qcmp_type(type) \
299
- static int \
300
- qcmp_## type (type *a, type *b) \
301
- { \
302
- if ( *a > *b ) return 1; \
303
- if ( *a < *b ) return -1; \
304
- return 0; \
305
- }
306
-
307
- #define qcmp_f_type(type) \
308
- static int \
309
- qcmp_## type (type *a, type *b) \
310
- { \
311
- if ( isnan(*a) && ( ! isnan(*b) ) ) return 1; \
312
- if ( isnan(*b) && ( ! isnan(*a) ) ) return -1; \
313
- if ( *a > *b ) return 1; \
314
- if ( *a < *b ) return -1; \
315
- return 0; \
316
- }
317
-
318
- static int
319
- qcmp_VALUE (VALUE *a, VALUE *b)
301
+ rb_ca_bsearch_kw (int argc, VALUE *argv, VALUE self)
320
302
  {
321
- int cmp;
322
- cmp = NUM2INT(rb_funcall(*a, rb_intern("<=>"), 1, *b));
323
- if ( cmp != 0 ) return cmp;
324
- return 0;
303
+ VALUE ropt = rb_pop_options(&argc, &argv);
304
+ VALUE raxis = Qnil;
305
+ rb_scan_options(ropt, "axis", &raxis);
306
+ rb_check_arity(argc, 1, 1);
307
+
308
+ if ( NIL_P(raxis) ) {
309
+ VALUE flat = rb_ca_flatten(self);
310
+ VALUE q = rb_obj_is_carray(argv[0]) ? rb_ca_flatten(argv[0]) : argv[0];
311
+ VALUE r = rb_ca_bsearch_ki(flat, q, INT2FIX(0));
312
+ return rb_obj_is_carray(argv[0]) ? ca_reshape_search_result(argv[0], r) : r;
313
+ }
314
+ return rb_ca_bsearch_ki(self, argv[0], raxis);
325
315
  }
326
316
 
327
- static int
328
- qcmp_data (cmp_data *a, cmp_data *b)
317
+ static VALUE
318
+ rb_ca_bsearch_addr_kw (int argc, VALUE *argv, VALUE self)
329
319
  {
330
- int cmp;
331
- cmp = memcmp(a->ptr, b->ptr, a->bytes);
332
- if ( cmp != 0 ) return cmp;
333
- return 0;
320
+ VALUE ropt = rb_pop_options(&argc, &argv);
321
+ VALUE raxis = Qnil;
322
+ rb_scan_options(ropt, "axis", &raxis);
323
+ rb_check_arity(argc, 1, 1);
324
+
325
+ if ( NIL_P(raxis) ) {
326
+ /* 1-D self: view-flat addr == axis-local index, so the no-axis
327
+ path reuses _ki (not _addr_ki) -- both variants share output. */
328
+ VALUE flat = rb_ca_flatten(self);
329
+ VALUE q = rb_obj_is_carray(argv[0]) ? rb_ca_flatten(argv[0]) : argv[0];
330
+ VALUE r = rb_ca_bsearch_ki(flat, q, INT2FIX(0));
331
+ return rb_obj_is_carray(argv[0]) ? ca_reshape_search_result(argv[0], r) : r;
332
+ }
333
+ return rb_ca_bsearch_addr_ki(self, argv[0], raxis);
334
334
  }
335
335
 
336
- qcmp_type(boolean8_t)
337
- qcmp_type(int8_t)
338
- qcmp_type(uint8_t)
339
- qcmp_type(int16_t)
340
- qcmp_type(uint16_t)
341
- qcmp_type(int32_t)
342
- qcmp_type(uint32_t)
343
- qcmp_type(int64_t)
344
- qcmp_type(uint64_t)
345
- qcmp_f_type(float32_t)
346
- qcmp_f_type(float64_t)
347
- qcmp_f_type(float128_t)
348
-
349
- static int
350
- qcmp_not_implement (void *a, void *b)
336
+ static VALUE
337
+ rb_ca_search_kw (int argc, VALUE *argv, VALUE self)
351
338
  {
352
- rb_raise(rb_eNotImpError,
353
- "compare function is not implemented for the data type");
339
+ VALUE ropt = rb_pop_options(&argc, &argv);
340
+ VALUE raxis = Qnil;
341
+ rb_scan_options(ropt, "axis", &raxis);
342
+ rb_check_arity(argc, 1, 2);
343
+
344
+ if ( NIL_P(raxis) ) {
345
+ VALUE flat = rb_ca_flatten(self);
346
+ VALUE q = rb_obj_is_carray(argv[0]) ? rb_ca_flatten(argv[0]) : argv[0];
347
+ VALUE r;
348
+ VALUE ki_argv[3] = { q, INT2FIX(0), argv[1] };
349
+ if ( argc >= 2 ) {
350
+ r = rb_ca_search_ki(3, ki_argv, flat);
351
+ }
352
+ else {
353
+ r = rb_ca_search_ki(2, ki_argv, flat);
354
+ }
355
+ return rb_obj_is_carray(argv[0]) ? ca_reshape_search_result(argv[0], r) : r;
356
+ }
357
+ /* search_ki(val, axis, eps) -- argc=2 (val,axis) or 3 (val,axis,eps) */
358
+ VALUE ki_argv[3] = { argv[0], raxis, argv[1] };
359
+ if ( argc >= 2 ) {
360
+ return rb_ca_search_ki(3, ki_argv, self);
361
+ }
362
+ return rb_ca_search_ki(2, ki_argv, self);
354
363
  }
355
364
 
356
- ca_qsort_cmp_func
357
- ca_qsort_cmp[CA_NTYPE] = {
358
- qcmp_data,
359
- qcmp_boolean8_t,
360
- qcmp_int8_t,
361
- qcmp_uint8_t,
362
- qcmp_int16_t,
363
- qcmp_uint16_t,
364
- qcmp_int32_t,
365
- qcmp_uint32_t,
366
- qcmp_int64_t,
367
- qcmp_uint64_t,
368
- qcmp_float32_t,
369
- qcmp_float64_t,
370
- qcmp_float128_t,
371
- qcmp_not_implement,
372
- qcmp_not_implement,
373
- qcmp_not_implement,
374
- qcmp_VALUE,
375
- };
376
-
377
- /* @overload sort!
378
-
379
- Sorts <i>ca</i>'s elements in place.
380
- */
381
-
382
365
  static VALUE
383
- rb_ca_sort_bang (VALUE self)
366
+ rb_ca_search_addr_kw (int argc, VALUE *argv, VALUE self)
384
367
  {
385
- CArray *ca;
386
-
387
- if ( rb_ca_is_any_masked(self) ) {
388
- rb_ca_sort_bang(rb_ca_value_not_masked(self));
389
- return self;
390
- }
391
-
392
- rb_ca_modify(self);
393
-
394
- TypedData_Get_Struct(self, CArray, &carray_data_type, ca);
395
- ca_attach(ca);
396
-
397
- if ( ca_is_fixlen_type(ca) ) {
398
- cmp_data *cmp_ptr, *p;
399
- char *ca_ptr, *q;
400
- ca_size_t i;
401
- cmp_ptr = malloc_with_check(sizeof(cmp_data)*ca->elements);
402
- ca_ptr = malloc_with_check(ca_length(ca));
403
- for (i=0, p=cmp_ptr, q=ca->ptr; i<ca->elements; i++, p++, q+=ca->bytes) {
404
- p->bytes = ca->bytes;
405
- p->ptr = q;
368
+ VALUE ropt = rb_pop_options(&argc, &argv);
369
+ VALUE raxis = Qnil;
370
+ rb_scan_options(ropt, "axis", &raxis);
371
+ rb_check_arity(argc, 1, 2);
372
+
373
+ if ( NIL_P(raxis) ) {
374
+ /* 1-D self: addr == index reuses _ki (not _addr_ki). */
375
+ VALUE flat = rb_ca_flatten(self);
376
+ VALUE q = rb_obj_is_carray(argv[0]) ? rb_ca_flatten(argv[0]) : argv[0];
377
+ VALUE r;
378
+ VALUE ki_argv[3] = { q, INT2FIX(0), argv[1] };
379
+ if ( argc >= 2 ) {
380
+ r = rb_ca_search_ki(3, ki_argv, flat);
406
381
  }
407
- qsort(cmp_ptr, ca->elements, sizeof(cmp_data), ca_qsort_cmp[CA_FIXLEN]);
408
- for (i=0, p=cmp_ptr, q=ca_ptr; i<ca->elements; i++, p++, q+=ca->bytes) {
409
- memcpy(q, p->ptr, ca->bytes);
382
+ else {
383
+ r = rb_ca_search_ki(2, ki_argv, flat);
410
384
  }
411
- free(ca->ptr);
412
- ca->ptr = ca_ptr;
413
- free(cmp_ptr);
385
+ return rb_obj_is_carray(argv[0]) ? ca_reshape_search_result(argv[0], r) : r;
414
386
  }
415
- else {
416
- qsort(ca->ptr, ca->elements, ca->bytes, ca_qsort_cmp[ca->data_type]);
387
+ VALUE ki_argv[3] = { argv[0], raxis, argv[1] };
388
+ if ( argc >= 2 ) {
389
+ return rb_ca_search_addr_ki(3, ki_argv, self);
417
390
  }
418
- ca_sync(ca);
419
- ca_detach(ca);
420
- return self;
391
+ return rb_ca_search_addr_ki(2, ki_argv, self);
421
392
  }
422
393
 
423
- /* @overload sort
424
-
425
- Returns a new CArray object containing <i>ca</i>'s elements sorted.
426
- */
427
-
428
394
  static VALUE
429
- rb_ca_sorted_copy (VALUE self)
395
+ rb_ca_search_nearest_kw (int argc, VALUE *argv, VALUE self)
430
396
  {
431
- volatile VALUE out = rb_ca_copy(self);
432
- rb_ca_data_type_inherit(out, self);
433
- return rb_ca_sort_bang(out);
397
+ VALUE ropt = rb_pop_options(&argc, &argv);
398
+ VALUE raxis = Qnil;
399
+ rb_scan_options(ropt, "axis", &raxis);
400
+ rb_check_arity(argc, 1, 1);
401
+
402
+ if ( NIL_P(raxis) ) {
403
+ VALUE flat = rb_ca_flatten(self);
404
+ VALUE q = rb_obj_is_carray(argv[0]) ? rb_ca_flatten(argv[0]) : argv[0];
405
+ VALUE r = rb_ca_search_nearest_ki(flat, q, INT2FIX(0));
406
+ return rb_obj_is_carray(argv[0]) ? ca_reshape_search_result(argv[0], r) : r;
407
+ }
408
+ return rb_ca_search_nearest_ki(self, argv[0], raxis);
434
409
  }
435
410
 
411
+ static VALUE
412
+ rb_ca_search_nearest_addr_kw (int argc, VALUE *argv, VALUE self)
413
+ {
414
+ VALUE ropt = rb_pop_options(&argc, &argv);
415
+ VALUE raxis = Qnil;
416
+ rb_scan_options(ropt, "axis", &raxis);
417
+ rb_check_arity(argc, 1, 1);
418
+
419
+ if ( NIL_P(raxis) ) {
420
+ /* 1-D self: addr == index reuses _ki (not _addr_ki) -- same as bsearch_addr. */
421
+ VALUE flat = rb_ca_flatten(self);
422
+ VALUE q = rb_obj_is_carray(argv[0]) ? rb_ca_flatten(argv[0]) : argv[0];
423
+ VALUE r = rb_ca_search_nearest_ki(flat, q, INT2FIX(0));
424
+ return rb_obj_is_carray(argv[0]) ? ca_reshape_search_result(argv[0], r) : r;
425
+ }
426
+ return rb_ca_search_nearest_addr_ki(self, argv[0], raxis);
427
+ }
436
428
 
437
- /* --------------------------------------------------------------- */
438
429
 
439
- /* @overload bsearch
430
+ /* ===========================================================================
431
+ * Ordering surface (C-side implementations of CArray ordering methods).
432
+ *
433
+ * Call sites use direct C function calls wherever possible; rb_funcall
434
+ * is retained only where there is no clean C entry: the `-` operator
435
+ * (Ruby method dispatch handles both `Integer - Integer` and
436
+ * `CArray - Integer` / `Integer - CArray` uniformly) and Symbol tag
437
+ * comparison for `method:` kwarg.
438
+ * =========================================================================== */
440
439
 
441
- Returns a new CArray object containing <i>ca</i>'s elements sorted.
442
- */
440
+ /* ---- sort_by_key family ---------------------------------------------- */
443
441
 
442
+ /* sort_by_key(key, axis: 0) -- gather self at key.sort_addr(axis:). */
444
443
  static VALUE
445
- rb_ca_binary_search (VALUE self, volatile VALUE rval)
444
+ rb_ca_sort_by_key (int argc, VALUE *argv, VALUE self)
446
445
  {
447
- volatile VALUE out;
448
- CArray *ca;
449
- char *val;
450
- TypedData_Get_Struct(self, CArray, &carray_data_type, ca);
451
-
452
- /* FIXME : treat mask */
453
- /*
454
- if ( ca_has_mask(ca) && ca_is_any_masked(self) ) {
455
- VALUE val = rb_funcall(self, rb_intern("value"), 0);
456
- VALUE select = rb_ca_is_not_masked(self);
457
- VALUE obj = rb_funcall(val, rb_intern("[]"), 1, select);
458
- return rb_ca_binary_search(obj, rval);
459
- }
460
- */
446
+ VALUE rkey, rkw = Qnil, raxis = Qnil;
447
+ rb_scan_args(argc, argv, "1:", &rkey, &rkw);
448
+ rb_scan_options(rkw, "axis", &raxis);
449
+ if ( NIL_P(raxis) ) {
450
+ raxis = INT2FIX(0);
451
+ }
452
+ VALUE addrs = rb_ca_sort_addr_c(rkey, raxis, 0, 1); /* masked_last=1 (:last), unchanged default */
453
+ return rb_ca_fetch(self, addrs);
454
+ }
461
455
 
462
- if ( ca_is_any_masked(ca) ) {
463
- rb_raise(rb_eRuntimeError,
464
- "CArray#bsearch can't be applied to carray with masked element.");
456
+ /* max_by_key(key, axis: nil) -- self[key.max_addr(axis:)] (or UNDEF if empty). */
457
+ static VALUE
458
+ rb_ca_max_by_key (int argc, VALUE *argv, VALUE self)
459
+ {
460
+ VALUE rkey, rkw = Qnil, raxis = Qnil;
461
+ rb_scan_args(argc, argv, "1:", &rkey, &rkw);
462
+ rb_scan_options(rkw, "axis", &raxis);
463
+ if ( RTEST(rb_ca_is_empty(self)) ) {
464
+ return rb_const_get(rb_cObject, rb_intern("UNDEF"));
465
465
  }
466
466
 
467
- ca_attach(ca);
467
+ VALUE addrs;
468
468
 
469
- if ( rb_obj_is_carray(rval) ) {
470
- volatile VALUE vidx;
471
- CArray *cv, *co;
472
- char *ptr, *val;
473
- ca_size_t i, idx;
474
- TypedData_Get_Struct(rval, CArray, &carray_data_type, cv);
475
- if ( ca->data_type != cv->data_type ) {
476
- cv = ca_wrap_readonly(rval, ca->data_type);
477
- }
478
- co = carray_new(CA_SIZE, cv->ndim, cv->dim, 0, NULL);
479
- out = ca_wrap_struct(co);
480
- ca_attach(cv);
481
- if ( ca_is_fixlen_type(ca) ) {
482
- cmp_data *cmp_ptr, *p, *ptr, cmp_val;
483
- char *q;
484
- ca_size_t i;
485
- cmp_val.bytes = ca->bytes;
486
- cmp_ptr = malloc_with_check(sizeof(cmp_data)*ca->elements);
487
- for (i=0, p=cmp_ptr, q=ca->ptr; i<ca->elements; i++, p++, q+=ca->bytes) {
488
- p->bytes = ca->bytes;
489
- p->ptr = q;
490
- }
491
- for (i=0; i<cv->elements; i++) {
492
- cmp_val.ptr = ca_ptr_at_addr(cv, i);
493
- ptr = bsearch(&cmp_val, cmp_ptr, ca->elements, sizeof(cmp_data),
494
- ca_qsort_cmp[CA_FIXLEN]);
495
- vidx = ( ! ptr ) ? CA_UNDEF : SIZE2NUM(ptr - cmp_ptr);
496
- rb_ca_store_addr(out, i, vidx);
497
- }
498
- free(cmp_ptr);
499
- }
500
- else {
501
- for (i=0; i<cv->elements; i++) {
502
- val = ca_ptr_at_addr(cv, i);
503
- ptr = bsearch(val, ca->ptr, ca->elements, ca->bytes,
504
- ca_qsort_cmp[ca->data_type]);
505
- if ( ! ptr ) {
506
- rb_ca_store_addr(out, i, CA_UNDEF);
507
- }
508
- else {
509
- idx = (ptr - ca->ptr)/ca->bytes;
510
- ca_store_addr(co, i, &idx);
511
- }
512
- }
513
- }
514
- ca_detach(cv);
469
+ if ( NIL_P(raxis) ) {
470
+ VALUE ki_argv[1] = { Qnil }; /* non-NULL for safety, unused at argc=0 */
471
+ addrs = rb_ca_argmax_addr_ki(0, ki_argv, rkey);
515
472
  }
516
473
  else {
517
- val = ALLOCA_N(char, ca->bytes);
518
- rb_ca_obj2ptr(self, rval, val);
519
- if ( ca_is_fixlen_type(ca) ) {
520
- cmp_data *cmp_ptr, *p, *ptr, cmp_val;
521
- char *q;
522
- ca_size_t i;
523
- cmp_val.bytes = ca->bytes;
524
- cmp_val.ptr = val;
525
- cmp_ptr = malloc_with_check(sizeof(cmp_data)*ca->elements);
526
- for (i=0, p=cmp_ptr, q=ca->ptr; i<ca->elements; i++, p++, q+=ca->bytes) {
527
- p->bytes = ca->bytes;
528
- p->ptr = q;
529
- }
530
- ptr = bsearch(&cmp_val, cmp_ptr, ca->elements, sizeof(cmp_data),
531
- ca_qsort_cmp[CA_FIXLEN]);
532
- out = ( ! ptr ) ? Qnil : SIZE2NUM((ptr - cmp_ptr));
533
- free(cmp_ptr);
534
- }
535
- else {
536
- char *ptr;
537
- ptr = bsearch(val, ca->ptr, ca->elements, ca->bytes,
538
- ca_qsort_cmp[ca->data_type]);
539
- out = ( ! ptr ) ? Qnil : SIZE2NUM((ptr - ca->ptr)/ca->bytes);
540
- }
474
+ VALUE kw = rb_hash_new();
475
+ rb_hash_aset(kw, ID2SYM(id_axis), raxis);
476
+ addrs = rb_ca_argmax_addr_ki(1, &kw, rkey);
541
477
  }
542
- ca_detach(ca);
543
- return out;
478
+ return rb_ca_fetch(self, addrs);
544
479
  }
545
480
 
546
- /* @overload bsearch_index
547
-
548
- [TBD].
549
- */
550
-
481
+ /* min_by_key(key, axis: nil) -- self[key.min_addr(axis:)] (or UNDEF if empty). */
551
482
  static VALUE
552
- rb_ca_binary_search_index (VALUE self, volatile VALUE rval)
483
+ rb_ca_min_by_key (int argc, VALUE *argv, VALUE self)
553
484
  {
554
- VALUE raddr = rb_ca_binary_search(self, rval);
555
- return ( NIL_P(raddr) ) ? Qnil : rb_ca_addr2index(self, raddr);
556
- }
557
-
558
- /* ------------------------------------------------------------------------- */
559
-
560
- #define proc_find_value(type) \
561
- { \
562
- type *ptr = (type *) ca->ptr; \
563
- boolean8_t *m = (ca->mask) ? (boolean8_t*) ca->mask->ptr : NULL; \
564
- type val = (type) NUM2LL(value); \
565
- ca_size_t i; \
566
- if ( m ) { \
567
- for (i=0; i<ca->elements; i++, ptr++) { \
568
- if ( ! *m++ ) { \
569
- if ( *ptr == val ) { \
570
- addr = i; \
571
- break; \
572
- } \
573
- } \
574
- } \
575
- } \
576
- else { \
577
- for (i=0; i<ca->elements; i++, ptr++) { \
578
- if ( *ptr == val ) { \
579
- addr = i; \
580
- break; \
581
- } \
582
- } \
583
- } \
485
+ VALUE rkey, rkw = Qnil, raxis = Qnil;
486
+ rb_scan_args(argc, argv, "1:", &rkey, &rkw);
487
+ rb_scan_options(rkw, "axis", &raxis);
488
+ if ( RTEST(rb_ca_is_empty(self)) ) {
489
+ return rb_const_get(rb_cObject, rb_intern("UNDEF"));
584
490
  }
585
491
 
586
- #define proc_find_value_float(type, defeps) \
587
- { \
588
- type *ptr = (type *) ca->ptr; \
589
- boolean8_t *m = (ca->mask) ? (boolean8_t*) ca->mask->ptr : NULL; \
590
- type val = (type) NUM2DBL(value); \
591
- double eps = (NIL_P(veps)) ? defeps*fabs(val) : NUM2DBL(veps); \
592
- ca_size_t i; \
593
- if ( m ) { \
594
- for (i=0; i<ca->elements; i++, ptr++) { \
595
- if ( ! *m++ ) { \
596
- if ( fabs(*ptr - val) <= eps ) { \
597
- addr = i; \
598
- break; \
599
- } \
600
- } \
601
- } \
602
- } \
603
- else { \
604
- for (i=0; i<ca->elements; i++, ptr++) { \
605
- if ( fabs(*ptr - val) <= eps ) { \
606
- addr = i; \
607
- break; \
608
- } \
609
- } \
610
- } \
611
- }
612
-
613
- #define proc_find_value_float128(type, defeps) \
614
- { \
615
- type *ptr = (type *) ca->ptr; \
616
- boolean8_t *m = (ca->mask) ? (boolean8_t*) ca->mask->ptr : NULL; \
617
- type val = (type) NUM2DBL(value); \
618
- float128_t eps = (NIL_P(veps)) ? defeps*fabsl(val) : NUM2DBL(veps); \
619
- ca_size_t i; \
620
- if ( m ) { \
621
- for (i=0; i<ca->elements; i++, ptr++) { \
622
- if ( ! *m++ ) { \
623
- if ( fabsl(*ptr - val) <= eps ) { \
624
- addr = i; \
625
- break; \
626
- } \
627
- } \
628
- } \
629
- } \
630
- else { \
631
- for (i=0; i<ca->elements; i++, ptr++) { \
632
- if ( fabsl(*ptr - val) <= eps ) { \
633
- addr = i; \
634
- break; \
635
- } \
636
- } \
637
- } \
638
- }
492
+ VALUE addrs;
639
493
 
640
- #define proc_find_value_cmplx(type, defeps) \
641
- { \
642
- type *ptr = (type *) ca->ptr; \
643
- boolean8_t *m = (ca->mask) ? (boolean8_t*) ca->mask->ptr : NULL; \
644
- type val = (type) NUM2CC(value); \
645
- double eps = (NIL_P(veps)) ? defeps*cabs(val) : NUM2DBL(veps); \
646
- ca_size_t i; \
647
- if ( m ) { \
648
- for (i=0; i<ca->elements; i++, ptr++) { \
649
- if ( ! *m++ ) { \
650
- if ( cabs(*ptr - val) <= eps ) { \
651
- addr = i; \
652
- break; \
653
- } \
654
- } \
655
- } \
656
- } \
657
- else { \
658
- for (i=0; i<ca->elements; i++, ptr++) { \
659
- if ( cabs(*ptr - val) <= eps ) { \
660
- addr = i; \
661
- break; \
662
- } \
663
- } \
664
- } \
494
+ if ( NIL_P(raxis) ) {
495
+ VALUE ki_argv[1] = { Qnil };
496
+ addrs = rb_ca_argmin_addr_ki(0, ki_argv, rkey);
665
497
  }
666
-
667
- #define proc_find_value_object() \
668
- { \
669
- VALUE *ptr = (VALUE *) ca->ptr; \
670
- boolean8_t *m = (ca->mask) ? (boolean8_t*) ca->mask->ptr : NULL; \
671
- ca_size_t i; \
672
- if ( m ) { \
673
- for (i=0; i<ca->elements; i++, ptr++) { \
674
- if ( ! *m++ ) { \
675
- if ( rb_funcall(value, id_equal, 1, *ptr) ) { \
676
- addr = i; \
677
- break; \
678
- } \
679
- } \
680
- } \
681
- } \
682
- else { \
683
- for (i=0; i<ca->elements; i++, ptr++) { \
684
- if ( rb_funcall(value, id_equal, 1, *ptr) ) { \
685
- addr = i; \
686
- break; \
687
- } \
688
- } \
689
- } \
498
+ else {
499
+ VALUE kw = rb_hash_new();
500
+ rb_hash_aset(kw, ID2SYM(id_axis), raxis);
501
+ addrs = rb_ca_argmin_addr_ki(1, &kw, rkey);
690
502
  }
503
+ return rb_ca_fetch(self, addrs);
504
+ }
691
505
 
506
+ /* ---- take_along_axis / put_along_axis -------------------------------- */
692
507
 
693
- /* @overload search
694
-
695
- [TBD].
696
- */
697
-
508
+ /* C-callable positional twin: skip rb_scan_args / kwarg hash machinery
509
+ so internal C consumers (e.g. nlargest / nsmallest family) can call
510
+ directly without a kwarg trampoline. The Ruby binding forwards here. */
698
511
  static VALUE
699
- rb_ca_linear_search (int argc, VALUE *argv, VALUE self)
512
+ rb_ca_take_along_axis_c (VALUE self, VALUE indices, VALUE raxis)
700
513
  {
701
- volatile VALUE value, veps;
702
- CArray *ca;
703
- ca_size_t addr;
704
-
705
- rb_scan_args(argc, argv, "11", (VALUE *) &value, (VALUE *) &veps);
706
-
707
- TypedData_Get_Struct(self, CArray, &carray_data_type, ca);
708
-
709
- ca_attach(ca);
710
-
711
- addr = -1;
712
-
713
- switch ( ca->data_type ) {
714
- case CA_BOOLEAN:
715
- case CA_INT8:
716
- case CA_UINT8: proc_find_value(int8_t); break;
717
- case CA_INT16:
718
- case CA_UINT16: proc_find_value(int16_t); break;
719
- case CA_INT32: proc_find_value(int32_t); break;
720
- case CA_UINT32: proc_find_value(uint32_t); break;
721
- case CA_INT64: proc_find_value(int64_t); break;
722
- case CA_UINT64: proc_find_value(uint64_t); break;
723
- case CA_FLOAT32: proc_find_value_float(float32_t, FLT_EPSILON); break;
724
- case CA_FLOAT64: proc_find_value_float(float64_t, DBL_EPSILON); break;
725
- case CA_FLOAT128: proc_find_value_float128(float128_t, DBL_EPSILON); break;
726
- #ifdef HAVE_COMPLEX_H
727
- case CA_CMPLX64: proc_find_value_cmplx(cmplx64_t, FLT_EPSILON); break;
728
- case CA_CMPLX128: proc_find_value_cmplx(cmplx128_t, DBL_EPSILON); break;
729
- case CA_CMPLX256: proc_find_value_cmplx(cmplx256_t, DBL_EPSILON); break;
730
- #endif
731
- case CA_OBJECT: proc_find_value_object(); break;
732
- default:
733
- rb_raise(rb_eCADataTypeError, "invalid data type");
514
+ if ( NIL_P(raxis) ) {
515
+ raxis = INT2FIX(0);
734
516
  }
735
-
736
- ca_detach(ca);
737
-
738
- return ( addr == -1 ) ? Qnil : SIZE2NUM(addr);
517
+ VALUE addrs = rb_ca_axis2addr_c(self, indices, raxis);
518
+ return rb_ca_fetch(self, addrs);
739
519
  }
740
520
 
741
- /* @overload search_index
742
-
743
- [TBD].
744
- */
745
-
746
521
  static VALUE
747
- rb_ca_linear_search_index (int argc, VALUE *argv, VALUE self)
522
+ rb_ca_take_along_axis (int argc, VALUE *argv, VALUE self)
748
523
  {
749
- VALUE raddr = rb_ca_linear_search(argc, argv, self);
750
- return ( NIL_P(raddr) ) ? Qnil : rb_ca_addr2index(self, raddr);
524
+ VALUE ropt = rb_pop_options(&argc, &argv);
525
+ VALUE raxis = Qnil;
526
+ rb_scan_options(ropt, "axis", &raxis);
527
+ rb_check_arity(argc, 1, 1);
528
+ return rb_ca_take_along_axis_c(self, argv[0], raxis);
751
529
  }
752
530
 
753
- /* ----------------------------------------------------------------- */
754
-
755
- #define proc_nearest_addr(type, from, ABS) \
756
- { \
757
- type *ptr = (type *) ca->ptr; \
758
- boolean8_t *m = (ca->mask) ? (boolean8_t*) ca->mask->ptr : NULL; \
759
- type val = (type) from(value); \
760
- double trial; \
761
- double diff = 1.0/0.0; \
762
- ca_size_t i; \
763
- addr = -1; \
764
- if ( m ) { \
765
- for (i=0; i<ca->elements; i++, ptr++) { \
766
- if ( ! *m++ ) { \
767
- trial = ABS(val - *ptr); \
768
- if ( trial < diff ) { \
769
- addr = i; \
770
- diff = trial; \
771
- } \
772
- } \
773
- } \
774
- } \
775
- else { \
776
- for (i=0; i<ca->elements; i++, ptr++) { \
777
- trial = ABS(val - *ptr); \
778
- if ( trial < diff ) { \
779
- addr = i; \
780
- diff = trial; \
781
- } \
782
- } \
783
- } \
784
- }
785
-
786
- #define proc_nearest_addr_VALUE() \
787
- { \
788
- VALUE *ptr = (VALUE *) ca->ptr; \
789
- boolean8_t *m = (ca->mask) ? (boolean8_t*) ca->mask->ptr : NULL; \
790
- VALUE val = value; \
791
- VALUE trial; \
792
- VALUE diff = rb_float_new(1.0/0.0); \
793
- ca_size_t i; \
794
- addr = -1; \
795
- if ( m ) { \
796
- for (i=0; i<ca->elements; i++, ptr++) { \
797
- if ( ! *m++ ) { \
798
- trial = rb_funcall(val, rb_intern("distance"), 1, *ptr); \
799
- if ( rb_funcall(trial, rb_intern("<"), 1, diff) ) { \
800
- addr = i; \
801
- diff = trial; \
802
- } \
803
- } \
804
- } \
805
- } \
806
- else { \
807
- for (i=0; i<ca->elements; i++, ptr++) { \
808
- trial = rb_funcall(val, rb_intern("distance"), 1, *ptr); \
809
- if ( rb_funcall(trial, rb_intern("<"), 1, diff) ) { \
810
- addr = i; \
811
- diff = trial; \
812
- } \
813
- } \
814
- } \
815
- }
816
-
817
- /* @overload search_nearest
818
-
819
- [TBD].
820
- */
821
-
822
531
  static VALUE
823
- rb_ca_linear_search_nearest (VALUE self, VALUE value)
532
+ rb_ca_put_along_axis (int argc, VALUE *argv, VALUE self)
824
533
  {
825
- CArray *ca;
826
- ca_size_t addr;
827
-
828
- TypedData_Get_Struct(self, CArray, &carray_data_type, ca);
829
-
830
- ca_attach(ca);
831
-
832
- switch ( ca->data_type ) {
833
- case CA_BOOLEAN:
834
- case CA_INT8: proc_nearest_addr(int8_t, NUM2LONG, fabs); break;
835
- case CA_UINT8: proc_nearest_addr(uint8_t, NUM2ULONG, fabs); break;
836
- case CA_INT16: proc_nearest_addr(int16_t, NUM2LONG, fabs); break;
837
- case CA_UINT16: proc_nearest_addr(uint16_t, NUM2ULONG, fabs); break;
838
- case CA_INT32: proc_nearest_addr(int32_t, NUM2LONG, fabs); break;
839
- case CA_UINT32: proc_nearest_addr(uint32_t, NUM2ULONG, fabs); break;
840
- case CA_INT64: proc_nearest_addr(int64_t, NUM2LL, fabs); break;
841
- case CA_UINT64: proc_nearest_addr(uint64_t, rb_num2ull, fabs); break;
842
- case CA_FLOAT32: proc_nearest_addr(float32_t, NUM2DBL, fabs); break;
843
- case CA_FLOAT64: proc_nearest_addr(float64_t, NUM2DBL, fabs); break;
844
- case CA_FLOAT128: proc_nearest_addr(float128_t, NUM2DBL, fabs); break;
845
- #ifdef HAVE_COMPLEX_H
846
- case CA_CMPLX64: proc_nearest_addr(cmplx64_t, NUM2CC, cabs); break;
847
- case CA_CMPLX128: proc_nearest_addr(cmplx128_t, NUM2CC, cabs); break;
848
- case CA_CMPLX256: proc_nearest_addr(cmplx256_t, NUM2CC, cabs); break;
849
- #endif
850
- case CA_OBJECT: proc_nearest_addr_VALUE(); break;
851
- default:
852
- rb_raise(rb_eCADataTypeError, "invalid data type for nearest_addr()");
853
- }
854
-
855
- ca_detach(ca);
856
-
857
- return ( addr == -1 ) ? Qnil : SIZE2NUM(addr);
534
+ VALUE ropt = rb_pop_options(&argc, &argv);
535
+ VALUE raxis = Qnil;
536
+ rb_scan_options(ropt, "axis", &raxis);
537
+ rb_check_arity(argc, 2, 2);
538
+ if ( NIL_P(raxis) ) {
539
+ raxis = INT2FIX(0);
540
+ }
541
+ VALUE addrs = rb_ca_axis2addr_c(self, argv[0], raxis);
542
+ rb_ca_store(self, addrs, argv[1]);
543
+ return self;
858
544
  }
859
545
 
860
- /* @overload search_nearest_index
861
-
862
- [TBD].
863
- */
546
+ /* ---- range -- (min..max) via fused minmax ----------------------------- */
864
547
 
865
548
  static VALUE
866
- rb_ca_linear_search_nearest_index (VALUE self, VALUE value)
549
+ rb_ca_range_method (VALUE self)
867
550
  {
868
- VALUE raddr = rb_ca_linear_search_nearest(self, value);
869
- return ( NIL_P(raddr) ) ? Qnil : rb_ca_addr2index(self, raddr);
551
+ VALUE mm_argv[1] = { Qnil };
552
+ VALUE pair = rb_ca_minmax_ki(0, mm_argv, self);
553
+ VALUE lo = rb_ary_entry(pair, 0);
554
+ VALUE hi = rb_ary_entry(pair, 1);
555
+ return rb_range_new(lo, hi, 0);
870
556
  }
871
557
 
872
- /* ----------------------------------------------------------------- */
558
+ /* ---- nlargest / nsmallest family ------------------------------------- */
873
559
 
874
- static ca_size_t
875
- linear_index (ca_size_t n, double *y, double yy, double *idx)
560
+ /* Per-axis top-k positions (cap clamped, axis normalized).
561
+ * Called by rb_ca_topk_index below. */
562
+ static VALUE
563
+ rb_ca_topk_positions (VALUE self, long cap, long axis_norm, int desc)
876
564
  {
877
- ca_size_t a, b, c, x1;
878
- double ya, yb, yc;
879
- double y1, y2;
880
- double rest;
881
-
882
- if ( yy <= y[0] ) {
883
- x1 = 0;
884
- goto found;
885
- }
886
-
887
- if ( yy >= y[n-1] ) {
888
- x1 = n-2;
889
- goto found;
890
- }
891
-
892
- /* check for equally spaced scale */
893
-
894
- a = (ca_size_t)((yy-y[0])/(y[n-1]-y[0])*(n-1));
895
-
896
- if ( a >= 0 && a < n-1 ) {
897
- if ( (y[a] - yy) * (y[a+1] - yy) <= 0 ) { /* lucky case */
898
- x1 = a;
899
- goto found;
900
- }
901
- }
902
-
903
- /* binary section method */
904
-
905
- a = 0;
906
- b = n-1;
907
-
908
- ya = y[a];
909
- yb = y[b];
910
-
911
- if ( ya > yb ) {
912
- return -1; /* input scale array should have accending order */
913
- }
914
-
915
- while ( (b - a) >= 1 ) {
916
-
917
- c = (a + b)/2;
918
- yc = y[c];
919
- if ( a == c ) {
920
- break;
921
- }
922
-
923
- if ( yc == yy ) {
924
- a = c;
925
- break;
926
- }
927
- else if ( (ya - yy) * (yc - yy) <= 0 ) {
928
- b = c;
929
- yb = yc;
930
- }
931
- else {
932
- a = c;
933
- ya = yc;
934
- }
565
+ CArray *ca;
566
+ TypedData_Get_Struct(self, CArray, &carray_data_type, ca);
567
+ long dim_axis = (long) ca->dim[axis_norm];
568
+ VALUE raxis_norm = LONG2NUM(axis_norm);
935
569
 
936
- if ( ya > yb ) {
937
- return -1; /* input scale array should have accending order */
570
+ if ( cap >= dim_axis ) {
571
+ VALUE full = rb_ca_sort_index_ki_quick(self, raxis_norm);
572
+ if ( desc ) {
573
+ full = rb_ca_flip_axis(full, NUM2LONG(raxis_norm));
938
574
  }
939
- }
940
-
941
- x1 = a;
942
-
943
- found:
944
-
945
- y1 = y[x1];
946
- y2 = y[x1+1];
947
- rest = (yy-y1)/(y2-y1);
948
-
949
- if ( fabs(y2-yy)/fabs(y2) < DBL_EPSILON*100 ) {
950
- *idx = (double) (x1 + 1);
951
- }
952
- else if ( fabs(y1-yy)/fabs(y1) < DBL_EPSILON*100 ) {
953
- *idx = (double) x1;
954
- }
955
- else {
956
- *idx = rest + (double) x1;
957
- }
958
-
959
- return 0;
575
+ return rb_ca_copy(full);
576
+ }
577
+
578
+ long pivot = desc ? (dim_axis - cap) : (cap - 1);
579
+ VALUE rpivot = LONG2NUM(pivot);
580
+
581
+ /* Build a Ruby indexer spec: nil on every axis except `axis_norm`,
582
+ where we take the top-k (or bottom-k) range after partitioning. */
583
+ int ndim = (int) ca->ndim;
584
+ VALUE spec[CA_RANK_MAX];
585
+ for ( int k = 0; k < ndim; k++ ) {
586
+ spec[k] = Qnil;
587
+ }
588
+ spec[axis_norm] = desc
589
+ ? rb_range_new(rpivot, LONG2NUM(dim_axis - 1), 0) /* (pivot..dim-1) */
590
+ : rb_range_new(INT2FIX(0), LONG2NUM(cap), 1); /* (0...cap) */
591
+
592
+ /* partidx_full[*spec] |> take_along_axis(...) |> sort_index(...) |>
593
+ flip(if desc) |> take_along_axis(...) |> copy. */
594
+ VALUE partidx_full = rb_ca_partition_index_ki(self, raxis_norm, rpivot);
595
+ VALUE partidx_top = rb_ca_fetch2(partidx_full, ndim, spec);
596
+ VALUE vals_top = rb_ca_take_along_axis_c(self, partidx_top, raxis_norm);
597
+ VALUE order = rb_ca_sort_index_ki_quick(vals_top, raxis_norm);
598
+ if ( desc ) {
599
+ order = rb_ca_flip_axis(order, NUM2LONG(raxis_norm));
600
+ }
601
+ VALUE picked = rb_ca_take_along_axis_c(partidx_top, order, raxis_norm);
602
+ return rb_ca_copy(picked);
960
603
  }
961
604
 
962
-
963
- /* ----------------------------------------------------------------- */
964
-
605
+ /* Dispatcher: normalize axis, clamp cap, empty-cap corner.
606
+ * Called by rb_ca_nlargest_index / rb_ca_nsmallest_index (index
607
+ * variants) and indirectly by rb_ca_topk_values below. */
965
608
  static VALUE
966
- rb_ca_binary_search_linear_index (volatile VALUE self, volatile VALUE vx)
609
+ rb_ca_topk_index (VALUE self, VALUE rn, VALUE raxis, int desc, const char *name)
967
610
  {
968
- volatile VALUE out, out0;
969
- CArray *ca, *sc, *cx, *co0, *co;
970
- ca_size_t n;
971
- double *x;
972
- double *px;
973
- double *po;
974
- ca_size_t i;
975
-
611
+ CArray *ca;
976
612
  TypedData_Get_Struct(self, CArray, &carray_data_type, ca);
977
-
978
- if ( rb_ca_is_any_masked(self) ) {
979
- rb_raise(rb_eRuntimeError, "self should not have any masked elements");
980
- }
981
-
982
- sc = ca_wrap_readonly(self, CA_FLOAT64);
983
- cx = ca_wrap_readonly(vx, CA_FLOAT64);
984
-
985
- co0 = carray_new(ca->data_type, cx->ndim, cx->dim, 0, NULL);
986
- out = out0 = ca_wrap_struct(co0);
987
- co = ca_wrap_writable(out, CA_FLOAT64);
988
-
989
- ca_attach_n(3, sc, cx, co);
990
-
991
- n = sc->elements;
992
- x = (double*) sc->ptr;
993
- px = (double*) cx->ptr;
994
- po = (double*) co->ptr;
995
-
996
- ca_update_mask(cx);
997
- if ( cx->mask ) {
998
- boolean8_t *mx, *mo;
999
- ca_create_mask(co);
1000
- mx = (boolean8_t *) cx->mask->ptr;
1001
- mo = (boolean8_t *) co->mask->ptr;
1002
- for (i=0; i<cx->elements; i++) {
1003
- if ( ! *mx ) {
1004
- linear_index(n, x, *px, po);
1005
- }
1006
- else {
1007
- *mo = 1;
1008
- }
1009
- mx++; mo++; px++, po++;
1010
- }
1011
- }
1012
- else {
1013
- for (i=0; i<cx->elements; i++) {
1014
- linear_index(n, x, *px, po);
1015
- px++; po++;
613
+ long a = rb_ca_normalize_axis_value(self, raxis, name);
614
+ long n_val = NUM2LONG(rn);
615
+ long dim_a = (long) ca->dim[a];
616
+ long cap = (n_val < dim_a) ? n_val : dim_a;
617
+ if ( cap < 0 ) {
618
+ cap = 0;
619
+ }
620
+ if ( cap == 0 ) {
621
+ /* Return zero-along-axis int64 array via the C carray constructor. */
622
+ ca_size_t out_dim[CA_RANK_MAX];
623
+ for ( int k = 0; k < (int) ca->ndim; k++ ) {
624
+ out_dim[k] = (k == a) ? 0 : ca->dim[k];
1016
625
  }
626
+ return rb_carray_new(CA_INT64, ca->ndim, out_dim, 0, NULL);
1017
627
  }
1018
-
1019
- ca_sync(co);
1020
- ca_detach_n(3, sc, cx, co);
1021
-
1022
- if ( rb_ca_is_scalar(vx) ) {
1023
- return rb_funcall(out0, rb_intern("[]"), 1, INT2NUM(0));
1024
- }
1025
- else {
1026
- return out0;
1027
- }
628
+ return rb_ca_topk_positions(self, cap, a, desc);
1028
629
  }
1029
630
 
1030
-
1031
631
  static VALUE
1032
- rb_ca_binary_search_linear_index_vectorized (volatile VALUE self, volatile VALUE vx)
632
+ rb_ca_nlargest_index (int argc, VALUE *argv, VALUE self)
1033
633
  {
1034
- volatile VALUE out, out0;
1035
- CArray *ca, *sc, *cx, *co0, *co;
1036
- double *x;
1037
- double *px;
1038
- double *po;
1039
- ca_size_t nseri, nlist;
1040
- ca_size_t odim[CA_DIM_MAX];
1041
- ca_size_t i, k;
1042
-
1043
- TypedData_Get_Struct(self, CArray, &carray_data_type, ca);
1044
-
1045
- if ( rb_ca_is_any_masked(self) ) {
1046
- rb_raise(rb_eRuntimeError, "self should not have any masked elements");
1047
- }
1048
-
1049
- sc = ca_wrap_readonly(self, CA_FLOAT64);
1050
- cx = ca_wrap_readonly(vx, CA_FLOAT64);
1051
-
1052
- if ( sc->ndim < 2 ) {
1053
- rb_raise(rb_eRuntimeError, "ndim of self should be larger than 2");
1054
- }
1055
-
1056
- if ( cx->ndim > CA_DIM_MAX ) {
1057
- rb_raise(rb_eRuntimeError, "2nd argument carray has too large dimension");
1058
- }
1059
-
1060
- nseri = 1;
1061
- for (i=0; i<sc->ndim-1; i++) {
1062
- nseri *= sc->dim[i];
1063
- }
1064
- nlist = sc->dim[sc->ndim-1];
1065
-
1066
- if ( rb_ca_is_scalar(vx) ) {
1067
- for (i=0; i<sc->ndim-1; i++) {
1068
- odim[i] = sc->dim[i];
1069
- }
1070
- co0 = carray_new(ca->data_type, sc->ndim-1, odim, 0, NULL);
1071
- }
1072
- else {
1073
- for (i=0; i<sc->ndim-1; i++) {
1074
- odim[i] = sc->dim[i];
1075
- }
1076
- memcpy(&odim[sc->ndim], cx->dim, cx->ndim*sizeof(ca_size_t));
1077
- co0 = carray_new(ca->data_type, sc->ndim-1 + cx->ndim, odim, 0, NULL);
1078
- }
1079
-
1080
- out = out0 = ca_wrap_struct(co0);
1081
- co = ca_wrap_writable(out, CA_FLOAT64);
1082
-
1083
- ca_attach_n(3, sc, cx, co);
1084
-
1085
- x = (double*) sc->ptr;
1086
- po = (double*) co->ptr;
1087
-
1088
- ca_update_mask(cx);
1089
- if ( cx->mask ) {
1090
- boolean8_t *mx, *mo;
1091
- ca_create_mask(co);
1092
- mx = (boolean8_t *) cx->mask->ptr;
1093
- mo = (boolean8_t *) co->mask->ptr;
1094
- for (k=0; k<nseri; k++) {
1095
- px = (double*) cx->ptr;
1096
- for (i=0; i<cx->elements; i++) {
1097
- if ( ! *mx ) {
1098
- linear_index(nlist, x, *px, po);
1099
- }
1100
- else {
1101
- *mo = 1;
1102
- }
1103
- mx++; mo++; px++, po++;
1104
- }
1105
- x += nlist;
1106
- }
1107
- }
1108
- else {
1109
- for (k=0; k<nseri; k++) {
1110
- px = (double*) cx->ptr;
1111
- for (i=0; i<cx->elements; i++) {
1112
- linear_index(nlist, x, *px, po);
1113
- px++; po++;
1114
- }
1115
- x += nlist;
1116
- }
1117
- }
1118
-
1119
- ca_sync(co);
1120
- ca_detach_n(3, sc, cx, co);
1121
-
1122
- return out0;
634
+ VALUE ropt = rb_pop_options(&argc, &argv);
635
+ VALUE raxis = Qnil;
636
+ rb_scan_options(ropt, "axis", &raxis);
637
+ rb_check_arity(argc, 1, 1);
638
+ if ( NIL_P(raxis) ) {
639
+ rb_raise(rb_eArgError, "nlargest_index: axis: kwarg is required");
640
+ }
641
+ return rb_ca_topk_index(self, argv[0], raxis, 1, "nlargest_index");
1123
642
  }
1124
643
 
1125
- /* ----------------------------------------------------------------- */
1126
-
1127
- static ca_size_t
1128
- linear_index_linear (ca_size_t n, double *y, double yy, double *idx)
644
+ static VALUE
645
+ rb_ca_nsmallest_index (int argc, VALUE *argv, VALUE self)
1129
646
  {
1130
- ca_size_t a, b, c, x1;
1131
- double ya, yb, yc;
1132
- double y1, y2;
1133
- double rest;
1134
- int k;
1135
-
1136
- /* linear search method */
1137
-
1138
- for (k=0; k<n-1; k++) {
1139
- if ( (yy - y[k])*(yy - y[k+1]) <= 0 ) {
1140
- x1 = k;
1141
- goto found;
1142
- }
1143
- }
1144
-
1145
- *idx = -1;
1146
- return -1;
1147
-
1148
- found:
1149
-
1150
- y1 = y[x1];
1151
- y2 = y[x1+1];
1152
- rest = (yy-y1)/(y2-y1);
1153
-
1154
- *idx = rest + (double) x1;
1155
- return 0;
647
+ VALUE ropt = rb_pop_options(&argc, &argv);
648
+ VALUE raxis = Qnil;
649
+ rb_scan_options(ropt, "axis", &raxis);
650
+ rb_check_arity(argc, 1, 1);
651
+ if ( NIL_P(raxis) ) {
652
+ rb_raise(rb_eArgError, "nsmallest_index: axis: kwarg is required");
653
+ }
654
+ return rb_ca_topk_index(self, argv[0], raxis, 0, "nsmallest_index");
1156
655
  }
1157
656
 
657
+ /* Shared body for nlargest / nsmallest (desc=1 vs desc=0).
658
+ * Called by rb_ca_nlargest and rb_ca_nsmallest below. */
1158
659
  static VALUE
1159
- rb_ca_linear_search_linear_index (volatile VALUE self, volatile VALUE vx)
660
+ rb_ca_topk_values (VALUE self, VALUE rn, VALUE raxis, int desc, const char *name)
1160
661
  {
1161
- volatile VALUE out, out0;
1162
- CArray *ca, *sc, *cx, *co0, *co;
1163
- ca_size_t n;
1164
- double *x;
1165
- double *px;
1166
- double *po;
1167
- ca_size_t i;
1168
-
1169
- TypedData_Get_Struct(self, CArray, &carray_data_type, ca);
1170
-
1171
- if ( rb_ca_is_any_masked(self) ) {
1172
- rb_raise(rb_eRuntimeError, "self should not have any masked elements");
1173
- }
1174
-
1175
- sc = ca_wrap_readonly(self, CA_FLOAT64);
1176
- cx = ca_wrap_readonly(vx, CA_FLOAT64);
1177
-
1178
- co0 = carray_new(ca->data_type, cx->ndim, cx->dim, 0, NULL);
1179
- out = out0 = ca_wrap_struct(co0);
1180
- co = ca_wrap_writable(out, CA_FLOAT64);
1181
-
1182
- ca_attach_n(3, sc, cx, co);
1183
-
1184
- n = sc->elements;
1185
- x = (double*) sc->ptr;
1186
- px = (double*) cx->ptr;
1187
- po = (double*) co->ptr;
1188
-
1189
- ca_update_mask(cx);
1190
- if ( cx->mask ) {
1191
- boolean8_t *mx, *mo;
1192
- ca_create_mask(co);
1193
- mx = (boolean8_t *) cx->mask->ptr;
1194
- mo = (boolean8_t *) co->mask->ptr;
1195
- for (i=0; i<cx->elements; i++) {
1196
- if ( ! *mx ) {
1197
- if ( linear_index_linear(n, x, *px, po) < 0 )
1198
- *mo = 1;
1199
- }
1200
- else {
1201
- *mo = 1;
1202
- }
1203
- mx++; mo++; px++, po++;
1204
- }
1205
- }
1206
- else {
1207
- for (i=0; i<cx->elements; i++) {
1208
- linear_index_linear(n, x, *px, po);
1209
- px++; po++;
1210
- }
1211
- }
1212
-
1213
- ca_sync(co);
1214
- ca_detach_n(3, sc, cx, co);
1215
-
1216
- if ( rb_ca_is_scalar(vx) ) {
1217
- return rb_funcall(out0, rb_intern("[]"), 1, INT2NUM(0));
1218
- }
1219
- else {
1220
- return out0;
1221
- }
662
+ if ( NIL_P(raxis) ) {
663
+ /* No-axis path: flatten + axis-0 topk + fetch. */
664
+ VALUE flat = rb_ca_flatten(self);
665
+ VALUE idx = rb_ca_topk_index(flat, rn, INT2FIX(0), desc, name);
666
+ return rb_ca_take_along_axis_c(flat, idx, INT2FIX(0));
667
+ }
668
+ long a = rb_ca_normalize_axis_value(self, raxis, name);
669
+ VALUE idx = rb_ca_topk_index(self, rn, LONG2NUM(a), desc, name);
670
+ return rb_ca_take_along_axis_c(self, idx, LONG2NUM(a));
1222
671
  }
1223
672
 
1224
673
  static VALUE
1225
- rb_ca_linear_search_linear_index_vectorized (volatile VALUE self, volatile VALUE vx)
674
+ rb_ca_nlargest (int argc, VALUE *argv, VALUE self)
1226
675
  {
1227
- volatile VALUE out, out0;
1228
- CArray *ca, *sc, *cx, *co0, *co;
1229
- double *x;
1230
- double *px;
1231
- double *po;
1232
- ca_size_t nseri, nlist;
1233
- ca_size_t odim[CA_DIM_MAX];
1234
- ca_size_t i, k;
1235
-
1236
- TypedData_Get_Struct(self, CArray, &carray_data_type, ca);
1237
-
1238
- if ( rb_ca_is_any_masked(self) ) {
1239
- rb_raise(rb_eRuntimeError, "self should not have any masked elements");
1240
- }
1241
-
1242
- sc = ca_wrap_readonly(self, CA_FLOAT64);
1243
- cx = ca_wrap_readonly(vx, CA_FLOAT64);
1244
-
1245
- if ( sc->ndim < 2 ) {
1246
- rb_raise(rb_eRuntimeError, "ndim of self should be larger than 2");
1247
- }
1248
-
1249
- if ( cx->ndim > CA_DIM_MAX ) {
1250
- rb_raise(rb_eRuntimeError, "2nd argument carray has too large dimension");
1251
- }
1252
-
1253
- nseri = 1;
1254
- for (i=0; i<sc->ndim-1; i++) {
1255
- nseri *= sc->dim[i];
1256
- }
1257
- nlist = sc->dim[sc->ndim-1];
1258
-
1259
- if ( rb_ca_is_scalar(vx) ) {
1260
- for (i=0; i<sc->ndim-1; i++) {
1261
- odim[i] = sc->dim[i];
1262
- }
1263
- co0 = carray_new(ca->data_type, sc->ndim-1, odim, 0, NULL);
1264
- }
1265
- else {
1266
- for (i=0; i<sc->ndim-1; i++) {
1267
- odim[i] = sc->dim[i];
1268
- }
1269
- memcpy(&odim[sc->ndim], cx->dim, cx->ndim*sizeof(ca_size_t));
1270
- co0 = carray_new(ca->data_type, sc->ndim-1 + cx->ndim, odim, 0, NULL);
1271
- }
1272
-
1273
- out = out0 = ca_wrap_struct(co0);
1274
- co = ca_wrap_writable(out, CA_FLOAT64);
1275
-
1276
- ca_attach_n(3, sc, cx, co);
1277
-
1278
- x = (double*) sc->ptr;
1279
- po = (double*) co->ptr;
1280
-
1281
- ca_update_mask(cx);
1282
- if ( cx->mask ) {
1283
- boolean8_t *mx, *mo;
1284
- ca_create_mask(co);
1285
- mx = (boolean8_t *) cx->mask->ptr;
1286
- mo = (boolean8_t *) co->mask->ptr;
1287
- for (k=0; k<nseri; k++) {
1288
- px = (double*) cx->ptr;
1289
- for (i=0; i<cx->elements; i++) {
1290
- if ( ! *mx ) {
1291
- if ( linear_index_linear(nlist, x, *px, po) < 0)
1292
- *mo = 1;
1293
- }
1294
- else {
1295
- *mo = 1;
1296
- }
1297
- mx++; mo++; px++, po++;
1298
- }
1299
- x += nlist;
1300
- }
1301
- }
1302
- else {
1303
- for (k=0; k<nseri; k++) {
1304
- px = (double*) cx->ptr;
1305
- for (i=0; i<cx->elements; i++) {
1306
- linear_index_linear(nlist, x, *px, po);
1307
- px++; po++;
1308
- }
1309
- x += nlist;
1310
- }
1311
- }
1312
-
1313
- ca_sync(co);
1314
- ca_detach_n(3, sc, cx, co);
1315
-
1316
- return out0;
676
+ VALUE ropt = rb_pop_options(&argc, &argv);
677
+ VALUE raxis = Qnil;
678
+ rb_scan_options(ropt, "axis", &raxis);
679
+ rb_check_arity(argc, 1, 1);
680
+ return rb_ca_topk_values(self, argv[0], raxis, 1, "nlargest");
1317
681
  }
1318
682
 
1319
- /* ----------------------------------------------------------------- */
1320
-
1321
- static int
1322
- fetch_linear_addr (ca_size_t n, double *y, double idx, double *val)
683
+ static VALUE
684
+ rb_ca_nsmallest (int argc, VALUE *argv, VALUE self)
1323
685
  {
1324
- ca_size_t il, iu;
1325
- double w;
1326
-
1327
- if ( idx < 0 || idx > n - 1 ) {
1328
- *val = 0.0/0.0;
1329
- return -1;
1330
- }
1331
-
1332
- il = (ca_size_t) floor(idx);
1333
- iu = (ca_size_t) ceil(idx);
1334
- w = idx - floor(idx);
1335
-
1336
- *val = y[iu]*w + y[il]*(1.0-w);
1337
-
1338
- /* printf("%g %i %i %g %g\n", idx, il, iu, w, *val); */
1339
-
1340
- return 0;
686
+ VALUE ropt = rb_pop_options(&argc, &argv);
687
+ VALUE raxis = Qnil;
688
+ rb_scan_options(ropt, "axis", &raxis);
689
+ rb_check_arity(argc, 1, 1);
690
+ return rb_ca_topk_values(self, argv[0], raxis, 0, "nsmallest");
1341
691
  }
1342
692
 
693
+ /* ---- order(axis:, descending:, method:) -------------------------------
694
+ *
695
+ * method: :ordinal (default) assigns every cell a distinct rank (ties
696
+ * broken by stable original position, scipy.stats.rankdata 'ordinal'
697
+ * style). method: :dense assigns tied values the same rank (no gaps),
698
+ * the standard "dense rank" -- lets order(descending:) compose as a
699
+ * sort_addr priority key without silently dropping a lower-priority
700
+ * key on ties: ordinal ranks are already a total order (no two cells
701
+ * ever compare equal), so a later sort_addr key never gets consulted;
702
+ * dense ranks preserve ties, so it does.
703
+ *
704
+ * descending: still works under method: :dense via the same
705
+ * (n-1)-ascending transform below: n is fixed per-fiber, so within a
706
+ * tied group ascending values are equal and (n-1)-ascending stays
707
+ * equal too -- ties survive the transform, and the group-to-group
708
+ * order still reverses correctly. */
709
+
1343
710
  static VALUE
1344
- rb_ca_fetch_linear_addr (volatile VALUE self, volatile VALUE vx)
711
+ rb_ca_order (int argc, VALUE *argv, VALUE self)
1345
712
  {
1346
- volatile VALUE out, out0;
1347
- CArray *ca, *sc, *cx, *co0, *co;
1348
- double *x;
1349
- double *px;
1350
- double *po;
1351
- ca_size_t nlist, nreq;
1352
- ca_size_t i;
1353
- boolean8_t *mx, *mo;
1354
-
1355
- TypedData_Get_Struct(self, CArray, &carray_data_type, ca);
1356
-
1357
- if ( rb_ca_is_any_masked(self) ) {
1358
- rb_raise(rb_eRuntimeError, "self should not have any masked elements");
1359
- }
1360
-
1361
- sc = ca_wrap_readonly(self, CA_FLOAT64);
1362
- cx = ca_wrap_readonly(vx, CA_FLOAT64);
1363
-
1364
- if ( sc->ndim != 1 ) {
1365
- rb_raise(rb_eRuntimeError, "ndim of self should be 1");
713
+ VALUE ropt = rb_pop_options(&argc, &argv);
714
+ VALUE raxis = Qnil, rdesc = Qfalse, rmethod = Qnil;
715
+ rb_scan_options(ropt, "axis,descending,method", &raxis, &rdesc, &rmethod);
716
+ rb_check_arity(argc, 0, 0);
717
+
718
+ int dense = 0; /* default method: :ordinal */
719
+ if ( ! NIL_P(rmethod) ) {
720
+ static ID sym_ordinal = 0, sym_dense = 0;
721
+ if ( ! sym_ordinal ) sym_ordinal = rb_intern("ordinal");
722
+ if ( ! sym_dense ) sym_dense = rb_intern("dense");
723
+ ID method_id = SYM2ID(rmethod);
724
+ if ( method_id == sym_ordinal ) dense = 0;
725
+ else if ( method_id == sym_dense ) dense = 1;
726
+ else {
727
+ rb_raise(rb_eArgError, "order: unknown method %s (expected :ordinal or :dense)",
728
+ rb_id2name(method_id));
729
+ }
1366
730
  }
1367
-
1368
- nlist = sc->dim[0];
1369
-
1370
- nreq = 1;
1371
- for (i=1; i<cx->ndim; i++) {
1372
- nreq *= cx->dim[i];
1373
- }
1374
-
1375
- co0 = carray_new(ca->data_type, cx->ndim, cx->dim, 0, NULL);
1376
- out = out0 = ca_wrap_struct(co0);
1377
- co = ca_wrap_writable(out, CA_FLOAT64);
1378
-
1379
- ca_attach_n(3, sc, cx, co);
1380
-
1381
- x = (double*) sc->ptr;
1382
- px = (double*) cx->ptr;
1383
- po = (double*) co->ptr;
1384
731
 
1385
- ca_create_mask(co);
1386
- ca_update_mask(cx);
1387
-
1388
- if ( cx->mask ) {
1389
- mx = (boolean8_t *) cx->mask->ptr;
1390
- mo = (boolean8_t *) co->mask->ptr;
1391
- for (i=0; i<nreq; i++) {
1392
- if ( ! *mx ) {
1393
- if ( fetch_linear_addr(nlist, x, *px, po) ) {
1394
- *mo = 1;
1395
- }
1396
- }
1397
- else {
1398
- *mo = 1;
1399
- }
1400
- mx++; mo++; px++, po++;
732
+ VALUE asc, n;
733
+ if ( NIL_P(raxis) ) {
734
+ VALUE flat = rb_ca_flatten(self);
735
+ /* Direct C-level dispatch via c_callable: true extern (mkkernel). */
736
+ VALUE ranked = rb_ca_rank_index_ki_quick_dense(flat, INT2FIX(0), dense);
737
+ /* reshape(*shape) */
738
+ CArray *ca;
739
+ TypedData_Get_Struct(self, CArray, &carray_data_type, ca);
740
+ VALUE shape_argv[CA_RANK_MAX];
741
+ for ( long k = 0; k < (long) ca->ndim; k++ ) {
742
+ shape_argv[k] = LONG2NUM((long) ca->dim[k]);
1401
743
  }
1402
- }
1403
- else {
1404
- mo = (boolean8_t *) co->mask->ptr;
1405
- for (i=0; i<nreq; i++) {
1406
- if ( fetch_linear_addr(nlist, x, *px, po) ) {
1407
- *mo = 1;
1408
- }
1409
- mo++; px++; po++;
744
+ asc = rb_ca_reshape((int) ca->ndim, shape_argv, ranked);
745
+ n = RTEST(rb_ca_has_mask(self))
746
+ ? SIZE2NUM(ca_count_not_masked(ca)) /* = elements - count_masked */
747
+ : rb_ca_elements(self);
748
+ } else {
749
+ long axis_norm = rb_ca_normalize_axis_value(self, raxis, "order");
750
+ VALUE raxis_norm = LONG2NUM(axis_norm);
751
+ asc = rb_ca_rank_index_ki_quick_dense(self, raxis_norm, dense);
752
+ if ( RTEST(rb_ca_has_mask(self)) ) {
753
+ n = rb_ca_count_not_masked_c(self, raxis_norm);
754
+ } else {
755
+ CArray *ca;
756
+ TypedData_Get_Struct(self, CArray, &carray_data_type, ca);
757
+ n = LONG2NUM((long) ca->dim[axis_norm]);
758
+ }
759
+ if ( RTEST(rb_obj_is_kind_of(n, rb_cCArray)) ) {
760
+ VALUE ia_argv[1] = { raxis_norm };
761
+ n = rb_ca_insert_axis(1, ia_argv, n);
1410
762
  }
1411
763
  }
1412
764
 
1413
- ca_sync(co);
1414
- ca_detach_n(3, sc, cx, co);
1415
-
1416
- if ( rb_ca_is_scalar(vx) ) {
1417
- return rb_funcall(out0, rb_intern("[]"), 1, INT2NUM(0));
1418
- }
1419
- else {
1420
- return out0;
1421
- }
765
+ VALUE diff;
766
+ if ( RTEST(rdesc) ) {
767
+ /* `-` via rb_funcall handles both Integer-Integer and
768
+ CArray/Integer mixed receivers uniformly via Ruby's operator
769
+ dispatch. No single C entry covers both. */
770
+ VALUE n_minus_1 = rb_funcall(n, id_sub, 1, INT2FIX(1));
771
+ diff = rb_funcall(n_minus_1, id_sub, 1, asc);
772
+ } else {
773
+ diff = asc;
774
+ }
775
+ /* Output data_type: CA_SIZE (= ca_size_t, int64 on 64-bit builds),
776
+ matching the rest of the *_index family (sort_index / partition_index
777
+ / rank_index) that order is built on. No explicit cast needed here:
778
+ asc (rank_index_ki's output) is already CA_SIZE, and Integer - CArray
779
+ promotion in the descending branch preserves it. */
780
+ return diff;
1422
781
  }
1423
782
 
783
+ /* ---- linear_section / linear_fetch ----------------------------------- */
1424
784
 
1425
- /*
1426
-
1427
-
1428
- self: ndim >= 2
1429
- 0...ndim : prev dimensions are vectorized elements
1430
- -1: last dimension is used for fetch_addr (as self)
1431
-
1432
- vx: ndim >= 2
1433
- 0...ndim : prev dimensions are vectorized elements should be equal to self's
1434
- -1: last dimension is used for fetch_addr (as addr)
1435
-
1436
- */
1437
-
1438
-
785
+ /* Coerce self to CA_FLOAT64 (no-copy when already f64 via
786
+ * rb_ca_to_float64), then optionally flatten when axis is nil.
787
+ * Called by rb_ca_linear_section_m and rb_ca_linear_fetch_m. */
1439
788
  static VALUE
1440
- rb_ca_find_linear_addr_vectorized (volatile VALUE self, volatile VALUE vx)
789
+ ca_linear_prep (VALUE self, VALUE raxis, const char *name, long *axis_out)
1441
790
  {
1442
- volatile VALUE out, out0;
1443
- CArray *ca, *sc, *cx, *co0, *co;
1444
- double *x;
1445
- double *px;
1446
- double *po;
1447
- ca_size_t nseri, nlist, nreq, xnseri;
1448
- ca_size_t i, k;
1449
- boolean8_t *mx, *mo;
1450
-
791
+ CArray *ca;
1451
792
  TypedData_Get_Struct(self, CArray, &carray_data_type, ca);
1452
-
1453
- if ( rb_ca_is_any_masked(self) ) {
1454
- rb_raise(rb_eRuntimeError, "self should not have any masked elements");
1455
- }
1456
-
1457
- sc = ca_wrap_readonly(self, CA_FLOAT64);
1458
- cx = ca_wrap_readonly(vx, CA_FLOAT64);
1459
-
1460
- if ( sc->ndim < 2 ) {
1461
- rb_raise(rb_eRuntimeError, "ndim of self should be larger than 2");
1462
- }
1463
-
1464
- nseri = 1;
1465
- for (i=0; i<sc->ndim-1; i++) {
1466
- nseri *= sc->dim[i];
1467
- }
1468
- nlist = sc->dim[sc->ndim-1];
1469
-
1470
- if ( cx->ndim < sc->ndim - 1 ) {
1471
- rb_raise(rb_eRuntimeError, "ndim of first argument should be larger than (ndim - 1) of self");
1472
- }
1473
-
1474
- xnseri = 1;
1475
- for (i=0; i<sc->ndim-1; i++) {
1476
- xnseri *= cx->dim[i];
1477
- }
1478
-
1479
- if ( xnseri != nseri ) {
1480
- rb_raise(rb_eRuntimeError, "1st dimension should be same between self and 1st argument");
1481
- }
1482
-
1483
- if ( cx->ndim == sc->ndim - 1 ) {
1484
- nreq = 1;
1485
- }
1486
- else {
1487
- nreq = cx->dim[cx->ndim-1];
1488
- }
1489
-
1490
- co0 = carray_new(ca->data_type, cx->ndim, cx->dim, 0, NULL);
1491
- out = out0 = ca_wrap_struct(co0);
1492
- co = ca_wrap_writable(out, CA_FLOAT64);
1493
-
1494
- ca_attach_n(3, sc, cx, co);
1495
-
1496
- x = (double*) sc->ptr;
1497
- px = (double*) cx->ptr;
1498
- po = (double*) co->ptr;
1499
-
1500
- ca_create_mask(co);
1501
- ca_update_mask(cx);
1502
-
1503
- if ( cx->mask ) {
1504
- mx = (boolean8_t *) cx->mask->ptr;
1505
- mo = (boolean8_t *) co->mask->ptr;
1506
- for (k=0; k<nseri; k++) {
1507
- for (i=0; i<nreq; i++) {
1508
- if ( ! *mx ) {
1509
- if ( linear_index(nlist, x, *px, po) ) {
1510
- *mo = 1;
1511
- }
1512
- }
1513
- else {
1514
- *mo = 1;
1515
- }
1516
- mx++; mo++; px++, po++;
1517
- }
1518
- x += nlist;
1519
- }
1520
- }
1521
- else {
1522
- mo = (boolean8_t *) co->mask->ptr;
1523
- for (k=0; k<nseri; k++) {
1524
- for (i=0; i<nreq; i++) {
1525
- if ( linear_index(nlist, x, *px, po) ) {
1526
- *mo = 1;
1527
- }
1528
- mo++; px++; po++;
1529
- }
1530
- x += nlist;
1531
- }
793
+ /* Face gate: linear_section / linear_fetch treat the axis as a continuous
794
+ numeric coordinate (fractional index by interpolating the stored values).
795
+ An ORDERABLE Face (storage order == surface order) is descended to its
796
+ storage here; the fraction is defined in storage space, which for a
797
+ numeric-relabel Face (datetime int64, etc.) is exactly the coordinate.
798
+ Non-orderable Faces raise. Non-numeric storage (e.g. a fixlen-string
799
+ Face) is caught by the rb_ca_to_float64 cast below, so no separate
800
+ float-numeric flag is needed. */
801
+ if ( ca_is_face(ca) ) {
802
+ if ( ! ca_test_flag(ca, CA_FLAG_FACE_ORDERABLE_STORAGE) ) {
803
+ rb_raise(rb_eArgError,
804
+ "%s: Face-typed input (%s) is not orderable by storage; "
805
+ "use ca.parent to descend to storage",
806
+ name, rb_obj_classname(self));
807
+ }
808
+ self = rb_ca_strip_face_value(self);
809
+ TypedData_Get_Struct(self, CArray, &carray_data_type, ca);
810
+ }
811
+ VALUE sc = (ca->data_type == CA_FLOAT64) ? self : rb_ca_to_float64(self);
812
+ if ( NIL_P(raxis) ) {
813
+ CArray *sca;
814
+ TypedData_Get_Struct(sc, CArray, &carray_data_type, sca);
815
+ if ( sca->ndim > 1 ) {
816
+ sc = rb_ca_flatten(sc);
817
+ }
818
+ *axis_out = 0;
819
+ } else {
820
+ *axis_out = rb_ca_normalize_axis_value(sc, raxis, name);
1532
821
  }
1533
-
1534
- ca_sync(co);
1535
- ca_detach_n(3, sc, cx, co);
1536
-
1537
- return out0;
822
+ return sc;
1538
823
  }
1539
824
 
1540
-
1541
825
  static VALUE
1542
- rb_ca_fetch_linear_addr_vectorized (volatile VALUE self, volatile VALUE vx)
826
+ rb_ca_linear_section_m (int argc, VALUE *argv, VALUE self)
1543
827
  {
1544
- volatile VALUE out, out0;
1545
- CArray *ca, *sc, *cx, *co0, *co;
1546
- double *x;
1547
- double *px;
1548
- double *po;
1549
- ca_size_t nseri, nlist, nreq, xnseri;
1550
- ca_size_t i, k;
1551
- boolean8_t *mx, *mo;
1552
-
1553
- TypedData_Get_Struct(self, CArray, &carray_data_type, ca);
1554
-
1555
- if ( rb_ca_is_any_masked(self) ) {
1556
- rb_raise(rb_eRuntimeError, "self should not have any masked elements");
1557
- }
1558
-
1559
- sc = ca_wrap_readonly(self, CA_FLOAT64);
1560
- cx = ca_wrap_readonly(vx, CA_FLOAT64);
1561
-
1562
- if ( sc->ndim < 2 ) {
1563
- rb_raise(rb_eRuntimeError, "ndim of self should be larger than 2");
1564
- }
1565
-
1566
- nseri = 1;
1567
- for (i=0; i<sc->ndim-1; i++) {
1568
- nseri *= sc->dim[i];
1569
- }
1570
- nlist = sc->dim[sc->ndim-1];
1571
-
1572
- if ( cx->ndim < sc->ndim - 1 ) {
1573
- rb_raise(rb_eRuntimeError, "ndim of first argument should be larger than (ndim - 1) of self");
828
+ VALUE ropt = rb_pop_options(&argc, &argv);
829
+ VALUE raxis = Qnil, rmethod = ID2SYM(id_sym_binary);
830
+ rb_scan_options(ropt, "axis,method", &raxis, &rmethod);
831
+ rb_check_arity(argc, 1, 1);
832
+ VALUE val = argv[0];
833
+
834
+ /* Query gate (generic, mirror search + reference flip
835
+ PROPOSAL_TO_COMPARABLE_RECEIVER_FLIP): the reference axis Face reconciles
836
+ the query into its own space. A COMPARABLE axis is directly comparable
837
+ -> strip a Face query, take a plain query as-is. A non-COMPARABLE Face
838
+ axis calls its own to_comparable(query) for ANY query type (Face CArray,
839
+ our Scalar, a Ruby Time / DateTime, ...), then strips; it raises if it
840
+ cannot reconcile the query. `self` is still the pre-strip axis Face
841
+ here, so it carries the unit for to_comparable and its COMPARABLE flag. */
842
+ {
843
+ int val_is_face = 0, self_is_face = 0, self_comparable = 0;
844
+ CArray *sca;
845
+ if ( rb_obj_is_kind_of(val, rb_cCArray) ) {
846
+ CArray *vca;
847
+ TypedData_Get_Struct(val, CArray, &carray_data_type, vca);
848
+ val_is_face = ca_is_face(vca);
849
+ }
850
+ TypedData_Get_Struct(self, CArray, &carray_data_type, sca);
851
+ if ( ca_is_face(sca) ) {
852
+ self_is_face = 1;
853
+ self_comparable = ca_test_flag(sca, CA_FLAG_FACE_COMPARABLE_STORAGE);
854
+ }
855
+ if ( self_comparable ) {
856
+ if ( val_is_face ) {
857
+ val = rb_ca_strip_face_value(val);
858
+ }
859
+ }
860
+ else if ( self_is_face ) {
861
+ if ( rb_respond_to(self, rb_intern("to_comparable")) ) {
862
+ val = rb_funcall(self, rb_intern("to_comparable"), 1, val);
863
+ val = rb_ca_strip_face_value(val);
864
+ }
865
+ else {
866
+ rb_raise(rb_eArgError,
867
+ "linear_section: non-comparable Face axis (%s) has no "
868
+ "to_comparable to reconcile the query; use ca.parent to "
869
+ "search the hidden storage explicitly",
870
+ rb_obj_classname(self));
871
+ }
872
+ }
1574
873
  }
1575
874
 
1576
- xnseri = 1;
1577
- for (i=0; i<sc->ndim-1; i++) {
1578
- xnseri *= cx->dim[i];
1579
- }
1580
-
1581
- if ( xnseri != nseri ) {
1582
- rb_raise(rb_eRuntimeError, "1st dimension should be same between self and 1st argument");
1583
- }
1584
-
1585
- if ( cx->ndim == sc->ndim - 1 ) {
1586
- nreq = 1;
1587
- }
1588
- else {
1589
- nreq = cx->dim[cx->ndim-1];
1590
- }
1591
-
1592
- co0 = carray_new(ca->data_type, cx->ndim, cx->dim, 0, NULL);
1593
- out = out0 = ca_wrap_struct(co0);
1594
- co = ca_wrap_writable(out, CA_FLOAT64);
1595
-
1596
- ca_attach_n(3, sc, cx, co);
875
+ long axis_norm = 0;
876
+ VALUE sc = ca_linear_prep(self, raxis, "linear_section", &axis_norm);
877
+ VALUE raxis_norm = LONG2NUM(axis_norm);
1597
878
 
1598
- x = (double*) sc->ptr;
1599
- px = (double*) cx->ptr;
1600
- po = (double*) co->ptr;
1601
-
1602
- ca_create_mask(co);
1603
- ca_update_mask(cx);
1604
-
1605
- if ( cx->mask ) {
1606
- mx = (boolean8_t *) cx->mask->ptr;
1607
- mo = (boolean8_t *) co->mask->ptr;
1608
- for (k=0; k<nseri; k++) {
1609
- for (i=0; i<nreq; i++) {
1610
- if ( ! *mx ) {
1611
- if ( fetch_linear_addr(nlist, x, *px, po) ) {
1612
- *mo = 1;
1613
- }
1614
- }
1615
- else {
1616
- *mo = 1;
1617
- }
1618
- mx++; mo++; px++, po++;
1619
- }
1620
- x += nlist;
1621
- }
1622
- }
1623
- else {
1624
- mo = (boolean8_t *) co->mask->ptr;
1625
- for (k=0; k<nseri; k++) {
1626
- for (i=0; i<nreq; i++) {
1627
- if ( fetch_linear_addr(nlist, x, *px, po) ) {
1628
- *mo = 1;
1629
- }
1630
- mo++; px++; po++;
1631
- }
1632
- x += nlist;
1633
- }
879
+ if ( rmethod == ID2SYM(id_sym_binary) ) {
880
+ return rb_ca_linear_section_binary_ki(sc, val, raxis_norm);
881
+ } else if ( rmethod == ID2SYM(id_sym_linear) ) {
882
+ return rb_ca_linear_section_linear_ki(sc, val, raxis_norm);
883
+ } else {
884
+ rb_raise(rb_eArgError,
885
+ "linear_section: unknown method %"PRIsVALUE
886
+ " (expected :binary or :linear)",
887
+ rb_inspect(rmethod));
1634
888
  }
1635
-
1636
- ca_sync(co);
1637
- ca_detach_n(3, sc, cx, co);
1638
-
1639
- return out0;
1640
889
  }
1641
890
 
1642
- void
1643
- Init_carray_order ()
891
+ static VALUE
892
+ rb_ca_linear_fetch_m (int argc, VALUE *argv, VALUE self)
1644
893
  {
1645
- id_equal = rb_intern("==");
1646
-
1647
- rb_define_method(rb_cCArray, "project", rb_ca_project, -1);
1648
-
1649
- rb_define_method(rb_cCArray, "reverse!", rb_ca_reverse_bang, 0);
1650
- rb_define_method(rb_cCArray, "reverse", rb_ca_reversed_copy, 0);
1651
-
1652
- rb_define_method(rb_cCArray, "sort!", rb_ca_sort_bang, 0);
1653
- rb_define_method(rb_cCArray, "sort", rb_ca_sorted_copy, 0);
1654
-
1655
- rb_define_method(rb_cCArray, "bsearch", rb_ca_binary_search, 1);
1656
- rb_define_method(rb_cCArray, "bsearch_index", rb_ca_binary_search_index, 1);
1657
-
1658
- rb_define_method(rb_cCArray, "search", rb_ca_linear_search, -1);
1659
- rb_define_method(rb_cCArray, "search_index", rb_ca_linear_search_index, -1);
1660
-
1661
- rb_define_method(rb_cCArray, "search_nearest",
1662
- rb_ca_linear_search_nearest, 1);
1663
- rb_define_method(rb_cCArray, "search_nearest_index",
1664
- rb_ca_linear_search_nearest_index, 1);
1665
-
1666
- rb_define_method(rb_cCArray, "section",
1667
- rb_ca_binary_search_linear_index, 1);
1668
-
1669
- rb_define_method(rb_cCArray, "vectorized_section",
1670
- rb_ca_binary_search_linear_index_vectorized, 1);
894
+ VALUE ropt = rb_pop_options(&argc, &argv);
895
+ VALUE raxis = Qnil;
896
+ rb_scan_options(ropt, "axis", &raxis);
897
+ rb_check_arity(argc, 1, 1);
898
+ VALUE addr = argv[0];
899
+
900
+ long axis_norm = 0;
901
+ VALUE sc = ca_linear_prep(self, raxis, "linear_fetch", &axis_norm);
902
+ return rb_ca_linear_fetch_ki(sc, addr, LONG2NUM(axis_norm));
903
+ }
1671
904
 
1672
- rb_define_method(rb_cCArray, "section_linear",
1673
- rb_ca_linear_search_linear_index, 1);
905
+ /* [MOVED] locate_addr / locate_nearest_addr (formerly matchup /
906
+ * matchup_nearest) -> lib/carray/methods/locate_addr.rb (thin
907
+ * compositions of sort_addr + fetch + bsearch (or linear_section +
908
+ * mask_invalid + round/floor/ceil + int64) + project, all of which have
909
+ * Ruby surfaces). */
1674
910
 
1675
- rb_define_method(rb_cCArray, "vectorized_section_linear",
1676
- rb_ca_linear_search_linear_index_vectorized, 1);
911
+ /* [MOVED] median / percentile / quantile -> ext/carray_median_percentile.c
912
+ * (kth-fetch + 5-method picker + partition-vs-sort dispatch). That
913
+ * file calls rb_ca_partition_copy_c (non-static in carray_partition.c)
914
+ * for the numeric path; CA_OBJECT routes through the CA_OBJECT branches
915
+ * of partition_copy / sort. */
1677
916
 
1678
- rb_define_method(rb_cCArray, "fetch_linear_addr",
1679
- rb_ca_fetch_linear_addr, 1);
917
+ extern VALUE rb_ca_value_array (VALUE self); /* carray_mask.c */
1680
918
 
1681
- rb_define_method(rb_cCArray, "vectorized_find_linear_addr",
1682
- rb_ca_find_linear_addr_vectorized, 1);
919
+ void
920
+ Init_carray_order (void)
921
+ {
922
+ id_axis = rb_intern("axis");
923
+ id_sub = rb_intern("-");
924
+ id_sym_binary = rb_intern("binary");
925
+ id_sym_linear = rb_intern("linear");
1683
926
 
1684
- rb_define_method(rb_cCArray, "vectorized_fetch_linear_addr",
1685
- rb_ca_fetch_linear_addr_vectorized, 1);
927
+ rb_define_method(rb_cCArray, "project", rb_ca_project, -1);
1686
928
 
929
+ /* Search family (dual API: index / addr) -- see the trampoline
930
+ comment block above for the axis: kwarg dispatch contract. */
931
+ rb_define_method(rb_cCArray, "bsearch", rb_ca_bsearch_kw, -1);
932
+ rb_define_method(rb_cCArray, "search", rb_ca_search_kw, -1);
933
+ rb_define_method(rb_cCArray, "search_nearest", rb_ca_search_nearest_kw, -1);
934
+ rb_define_method(rb_cCArray, "bsearch_addr", rb_ca_bsearch_addr_kw, -1);
935
+ rb_define_method(rb_cCArray, "search_addr", rb_ca_search_addr_kw, -1);
936
+ rb_define_method(rb_cCArray, "search_nearest_addr", rb_ca_search_nearest_addr_kw, -1);
937
+
938
+ rb_define_method(rb_cCArray, "sort_by_key", rb_ca_sort_by_key, -1);
939
+ rb_define_method(rb_cCArray, "max_by_key", rb_ca_max_by_key, -1);
940
+ rb_define_method(rb_cCArray, "min_by_key", rb_ca_min_by_key, -1);
941
+ rb_define_method(rb_cCArray, "take_along_axis", rb_ca_take_along_axis, -1);
942
+ rb_define_method(rb_cCArray, "put_along_axis", rb_ca_put_along_axis, -1);
943
+ rb_define_method(rb_cCArray, "range", rb_ca_range_method, 0);
944
+ rb_define_method(rb_cCArray, "nlargest", rb_ca_nlargest, -1);
945
+ rb_define_method(rb_cCArray, "nsmallest", rb_ca_nsmallest, -1);
946
+ rb_define_method(rb_cCArray, "nlargest_index", rb_ca_nlargest_index, -1);
947
+ rb_define_method(rb_cCArray, "nsmallest_index", rb_ca_nsmallest_index, -1);
948
+ rb_define_method(rb_cCArray, "order", rb_ca_order, -1);
949
+ rb_define_method(rb_cCArray, "linear_section", rb_ca_linear_section_m, -1);
950
+ rb_define_method(rb_cCArray, "linear_fetch", rb_ca_linear_fetch_m, -1);
951
+
952
+ /* [MOVED] bindings for the following live elsewhere:
953
+ sort / sort_copy -> carray_sort.c
954
+ partition / partition_copy -> carray_partition.c
955
+ median / percentile / quantile -> carray_median_percentile.c
956
+ (bound by Init_carray_median_percentile) */
1687
957
  }