carray 3.0.2 → 3.0.4

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 (247) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +568 -0
  3. data/README.md +6 -6
  4. data/carray.gemspec +1 -1
  5. data/ext/ca_axis_descriptor.h +27 -24
  6. data/ext/ca_axis_dispatch.c +220 -0
  7. data/ext/ca_axis_group.c +201 -123
  8. data/ext/ca_binop_dispatch.c +46 -52
  9. data/ext/ca_binop_dispatch.h +1 -1
  10. data/ext/ca_categorical_iterator.c +249 -183
  11. data/ext/ca_compare.c +7 -12
  12. data/ext/ca_for_buffer.h +21 -68
  13. data/ext/ca_group_iter.c +38 -23
  14. data/ext/ca_kernel_iterator.c +1765 -2076
  15. data/ext/ca_kernel_iterator.h +584 -566
  16. data/ext/ca_monop_dispatch.c +86 -63
  17. data/ext/ca_monop_dispatch.h +5 -0
  18. data/ext/ca_obj_array.c +223 -56
  19. data/ext/ca_obj_bincmp.c +172 -138
  20. data/ext/ca_obj_binop.c +116 -230
  21. data/ext/ca_obj_bitarray.c +40 -26
  22. data/ext/ca_obj_bitfield.c +100 -68
  23. data/ext/ca_obj_block.c +19 -9
  24. data/ext/ca_obj_byte_swap.c +45 -46
  25. data/ext/ca_obj_const_string.c +269 -44
  26. data/ext/ca_obj_face.c +612 -89
  27. data/ext/ca_obj_face.h +111 -47
  28. data/ext/ca_obj_fake.c +36 -79
  29. data/ext/ca_obj_farray.c +7 -3
  30. data/ext/ca_obj_field.c +33 -8
  31. data/ext/ca_obj_fixlen_string.c +23 -24
  32. data/ext/ca_obj_grid.c +541 -210
  33. data/ext/ca_obj_meld.c +331 -111
  34. data/ext/ca_obj_moncmp.c +80 -65
  35. data/ext/ca_obj_monop.c +177 -149
  36. data/ext/ca_obj_object.c +664 -248
  37. data/ext/ca_obj_record.c +63 -23
  38. data/ext/ca_obj_reduce.c +18 -16
  39. data/ext/ca_obj_refer.c +140 -58
  40. data/ext/ca_obj_remap.c +85 -24
  41. data/ext/ca_obj_repeat.c +24 -9
  42. data/ext/ca_obj_roll.c +121 -16
  43. data/ext/ca_obj_select.c +61 -58
  44. data/ext/ca_obj_select_axis.c +73 -107
  45. data/ext/ca_obj_shift.c +32 -102
  46. data/ext/ca_obj_stack.c +372 -122
  47. data/ext/ca_obj_stride.c +348 -140
  48. data/ext/ca_obj_string.c +88 -22
  49. data/ext/ca_obj_tile.c +38 -21
  50. data/ext/ca_obj_time.c +45 -31
  51. data/ext/ca_obj_timedelta.c +43 -29
  52. data/ext/ca_obj_transpose.c +17 -6
  53. data/ext/ca_obj_triop.c +125 -141
  54. data/ext/ca_obj_window.c +330 -44
  55. data/ext/ca_op_ipower.c +24 -11
  56. data/ext/ca_op_powi.h +57 -30
  57. data/ext/ca_pow5_table.h +656 -0
  58. data/ext/ca_sort_kernels.h +70 -0
  59. data/ext/ca_sweep_engine.c +104 -81
  60. data/ext/ca_sweep_engine.h +7 -20
  61. data/ext/ca_triop_dispatch.c +4 -1
  62. data/ext/ca_triop_dispatch.h +5 -2
  63. data/ext/caframe_csv_reader.c +542 -0
  64. data/ext/carray.h +241 -92
  65. data/ext/carray_access.c +608 -276
  66. data/ext/carray_address_basis.c +494 -97
  67. data/ext/carray_attribute.c +129 -4
  68. data/ext/carray_bincount.c +39 -7
  69. data/ext/carray_broadcast.c +56 -19
  70. data/ext/carray_build_flags.h +1 -1
  71. data/ext/carray_call_cfunc.c +108 -28
  72. data/ext/carray_cast.c +1576 -223
  73. data/ext/carray_cast_func.rb +77 -24
  74. data/ext/carray_class.c +4 -3
  75. data/ext/carray_conversion.c +208 -75
  76. data/ext/carray_copy.c +82 -24
  77. data/ext/carray_core.c +646 -162
  78. data/ext/carray_count.c +171 -88
  79. data/ext/carray_element.c +94 -60
  80. data/ext/carray_factorize.c +670 -324
  81. data/ext/carray_generate.c +260 -83
  82. data/ext/carray_histogram.c +209 -106
  83. data/ext/carray_hold.c +14 -16
  84. data/ext/carray_index_classifier.c +422 -243
  85. data/ext/carray_internal.h +692 -3
  86. data/ext/carray_kernels_bincmp.c +7 -0
  87. data/ext/carray_kernels_binop.c +487 -120
  88. data/ext/carray_kernels_init.c +653 -50
  89. data/ext/carray_kernels_moncmp.c +15 -8
  90. data/ext/carray_kernels_monop.c +99 -43
  91. data/ext/carray_kernels_reduce_aggregate.c +3925 -6112
  92. data/ext/carray_kernels_reduce_boolean.c +112 -36
  93. data/ext/carray_kernels_reduce_cumulative.c +2127 -6903
  94. data/ext/carray_kernels_reduce_extreme.c +3032 -2892
  95. data/ext/carray_kernels_reduce_variance.c +439 -500
  96. data/ext/carray_kernels_scan.c +1097 -488
  97. data/ext/carray_kernels_search.c +3787 -2176
  98. data/ext/carray_kernels_sort.c +1200 -920
  99. data/ext/carray_kernels_triop.c +479 -7
  100. data/ext/carray_key_windows.c +216 -0
  101. data/ext/carray_lazy.c +967 -153
  102. data/ext/carray_loop.c +81 -65
  103. data/ext/carray_mask.c +910 -324
  104. data/ext/carray_math_kernel.h +1 -0
  105. data/ext/carray_median_percentile.c +299 -100
  106. data/ext/carray_memory_view.c +872 -758
  107. data/ext/carray_operator.c +1171 -364
  108. data/ext/carray_order.c +505 -133
  109. data/ext/carray_partition.c +112 -65
  110. data/ext/carray_random.c +327 -239
  111. data/ext/carray_round.c +273 -0
  112. data/ext/carray_scatter.c +346 -78
  113. data/ext/carray_segment.c +257 -0
  114. data/ext/carray_slab.c +249 -86
  115. data/ext/carray_sort.c +224 -227
  116. data/ext/carray_sort_kernel.c +8 -1
  117. data/ext/carray_struct.c +118 -39
  118. data/ext/carray_test.c +193 -69
  119. data/ext/carray_time_text.c +1182 -0
  120. data/ext/carray_undef.c +4 -0
  121. data/ext/carray_utils.c +579 -113
  122. data/ext/extconf.rb +27 -2
  123. data/ext/mk_call_cfunc.rb +19 -4
  124. data/ext/mk_pow5_table.rb +35 -0
  125. data/ext/mkkernel.rb +1202 -1014
  126. data/ext/ruby_carray.c +17 -5
  127. data/ext/version.h +4 -4
  128. data/ext/version.rb +4 -2
  129. data/lib/carray/attribute.rb +160 -17
  130. data/lib/carray/autoload_carray.rb +23 -27
  131. data/lib/carray/autoload_method_extension.rb +20 -5
  132. data/lib/carray/axis_group.rb +360 -63
  133. data/lib/carray/basics.rb +162 -28
  134. data/lib/carray/bincount_nd.rb +18 -2
  135. data/lib/carray/block_iterator.rb +231 -58
  136. data/lib/carray/boolean_reduce.rb +34 -42
  137. data/lib/carray/categorical.rb +242 -52
  138. data/lib/carray/categorical_iterator.rb +437 -827
  139. data/lib/carray/complex.rb +41 -34
  140. data/lib/carray/conditional.rb +150 -14
  141. data/lib/carray/const_string.rb +59 -4
  142. data/lib/carray/construct.rb +15 -11
  143. data/lib/carray/data_type_extension.rb +1 -0
  144. data/lib/carray/distance_limit.rb +77 -0
  145. data/lib/carray/fixlen_string.rb +7 -1
  146. data/lib/carray/frame/column_verbs.rb +70 -0
  147. data/lib/carray/frame/concat.rb +157 -6
  148. data/lib/carray/frame/convert.rb +82 -7
  149. data/lib/carray/frame/csv_parser.rb +688 -87
  150. data/lib/carray/frame/describe.rb +80 -0
  151. data/lib/carray/frame/frame.rb +369 -45
  152. data/lib/carray/frame/group.rb +631 -90
  153. data/lib/carray/frame/io.rb +506 -79
  154. data/lib/carray/frame/join.rb +411 -62
  155. data/lib/carray/frame/records.rb +116 -19
  156. data/lib/carray/frame/reshape.rb +455 -0
  157. data/lib/carray/frame/sort.rb +30 -18
  158. data/lib/carray/frame/verbs.rb +868 -61
  159. data/lib/carray/frame/windows.rb +117 -0
  160. data/lib/carray/frame.rb +4 -0
  161. data/lib/carray/fuse_source.rb +118 -2
  162. data/lib/carray/fusion.rb +257 -42
  163. data/lib/carray/histogram.rb +6 -1
  164. data/lib/carray/inspect.rb +54 -13
  165. data/lib/carray/iterator.rb +99 -9
  166. data/lib/carray/key_windows.rb +620 -0
  167. data/lib/carray/lazy.rb +216 -193
  168. data/lib/carray/mask_gap_fill.rb +112 -92
  169. data/lib/carray/math.rb +32 -19
  170. data/lib/carray/meld_reduce.rb +52 -21
  171. data/lib/carray/methods/align_addr.rb +8 -4
  172. data/lib/carray/methods/bin.rb +26 -18
  173. data/lib/carray/methods/bincount.rb +18 -2
  174. data/lib/carray/methods/bit_string.rb +15 -4
  175. data/lib/carray/methods/choose.rb +16 -3
  176. data/lib/carray/methods/composition.rb +18 -10
  177. data/lib/carray/methods/factorize.rb +2 -2
  178. data/lib/carray/methods/gather_nd.rb +25 -5
  179. data/lib/carray/methods/insert_block.rb +9 -14
  180. data/lib/carray/methods/is_between.rb +59 -0
  181. data/lib/carray/methods/is_in.rb +140 -10
  182. data/lib/carray/methods/join.rb +2 -7
  183. data/lib/carray/methods/lag.rb +49 -0
  184. data/lib/carray/methods/locate_addr.rb +182 -45
  185. data/lib/carray/methods/mode.rb +18 -1
  186. data/lib/carray/methods/nunique.rb +11 -6
  187. data/lib/carray/methods/repeat.rb +1 -20
  188. data/lib/carray/methods/resize.rb +21 -23
  189. data/lib/carray/methods/snap.rb +34 -12
  190. data/lib/carray/methods/unique.rb +11 -2
  191. data/lib/carray/methods/value_counts.rb +4 -11
  192. data/lib/carray/record_order.rb +208 -0
  193. data/lib/carray/rng.rb +3 -0
  194. data/lib/carray/runtime.rb +65 -17
  195. data/lib/carray/segment_iterator.rb +1221 -0
  196. data/lib/carray/serialize.rb +214 -30
  197. data/lib/carray/slab_iterator.rb +101 -27
  198. data/lib/carray/stack.rb +10 -4
  199. data/lib/carray/string.rb +105 -5
  200. data/lib/carray/string_operation_extension.rb +12 -4
  201. data/lib/carray/struct.rb +137 -58
  202. data/lib/carray/struct_builder.rb +124 -17
  203. data/lib/carray/time.rb +1222 -186
  204. data/lib/carray/window_iterator.rb +242 -81
  205. data/lib/carray.rb +9 -2
  206. data/yard-stubs/ca_obj_array.rb +4 -4
  207. data/yard-stubs/ca_obj_bitarray.rb +7 -0
  208. data/yard-stubs/ca_obj_bitfield.rb +23 -12
  209. data/yard-stubs/ca_obj_fake.rb +1 -1
  210. data/yard-stubs/ca_obj_field.rb +5 -1
  211. data/yard-stubs/ca_obj_grid.rb +21 -10
  212. data/yard-stubs/ca_obj_refer.rb +5 -4
  213. data/yard-stubs/ca_obj_roll.rb +2 -3
  214. data/yard-stubs/ca_obj_shift.rb +5 -1
  215. data/yard-stubs/ca_obj_stride.rb +10 -7
  216. data/yard-stubs/ca_obj_tile.rb +1 -1
  217. data/yard-stubs/ca_obj_transpose.rb +1 -1
  218. data/yard-stubs/ca_obj_window.rb +9 -5
  219. data/yard-stubs/carray_access.rb +11 -5
  220. data/yard-stubs/carray_attribute.rb +0 -5
  221. data/yard-stubs/carray_broadcast.rb +1 -1
  222. data/yard-stubs/carray_cast.rb +88 -43
  223. data/yard-stubs/carray_conversion.rb +15 -2
  224. data/yard-stubs/carray_copy.rb +12 -5
  225. data/yard-stubs/carray_core.rb +3 -3
  226. data/yard-stubs/carray_count.rb +9 -0
  227. data/yard-stubs/carray_element.rb +10 -4
  228. data/yard-stubs/carray_generate.rb +21 -1
  229. data/yard-stubs/carray_lazy.rb +5 -1
  230. data/yard-stubs/carray_loop.rb +4 -1
  231. data/yard-stubs/carray_mask.rb +60 -12
  232. data/yard-stubs/carray_math.rb +52 -14
  233. data/yard-stubs/carray_median_percentile.rb +15 -8
  234. data/yard-stubs/carray_memory_view.rb +40 -23
  235. data/yard-stubs/carray_order.rb +61 -29
  236. data/yard-stubs/carray_random.rb +13 -4
  237. data/yard-stubs/carray_scatter.rb +21 -9
  238. data/yard-stubs/carray_segment.rb +72 -0
  239. data/yard-stubs/carray_slab.rb +10 -2
  240. data/yard-stubs/carray_sort.rb +49 -8
  241. data/yard-stubs/carray_test.rb +15 -3
  242. data/yard-stubs/carray_utils.rb +0 -26
  243. metadata +19 -5
  244. data/ext/ca_for_each_element.h +0 -239
  245. data/ext/carray_kernels_map.c +0 -3467
  246. data/lib/carray/arrow_tensor.rb +0 -401
  247. data/lib/carray/table.rb +0 -108
@@ -24,6 +24,13 @@
24
24
  probed by operation-table function pointer (obj_type is runtime-
25
25
  assigned, so no CA_OBJ_STACK compile-time constant exists). */
26
26
  extern ca_operation_function_t ca_stack_func;
27
+ /* The square root of a variance: an ε-negative from rounding becomes 0,
28
+ a NaN stays NaN (fmax(NaN, 0.0) is 0.0). */
29
+ #ifndef CARRAY_SQRT_CLAMP_DEFINED
30
+ #define CARRAY_SQRT_CLAMP_DEFINED
31
+ static inline double ca_sqrt_clamp (double v) { return sqrt(v < 0.0 ? 0.0 : v); }
32
+ #endif
33
+
27
34
  /* BOOL2VAL: bool -> Ruby (Qtrue/Qfalse). Used as ruby_scalar wrapper
28
35
  for the all/any flat-reduction Ruby surface so `a.all` / `a.any`
29
36
  return real true/false, not Integer 0/1. (BOOL2OBJ already exists
@@ -61,21 +68,21 @@ sort_index_ki_native_i8 (VALUE self, CArray *ca, int axis, int do_stable, int ma
61
68
  rc = ca_iter_state_init_l2(&st_in, ca, CA_SLAB_AXES,
62
69
  slab_axes, 1, 0);
63
70
  if ( rc != CA_ITER_OK ) {
64
- rb_raise(rb_eRuntimeError, "sort_index_ki: input init failed rc=%d", rc);
71
+ rb_raise(rb_eRuntimeError, "%s: input init failed rc=%d", ca_calling_method_name(), rc);
65
72
  }
66
- rc = ca_iter_state_init_l2(&st_out, co, CA_SLAB_AXES,
73
+ rc = ca_iter_state_init_l2_paired(&st_out, &st_in, co, CA_SLAB_AXES,
67
74
  slab_axes, 1, CA_KERNEL_WRITE);
68
75
  if ( rc != CA_ITER_OK ) {
69
76
  ca_iter_state_finish(&st_in);
70
- rb_raise(rb_eRuntimeError, "sort_index_ki: output init failed rc=%d", rc);
77
+ rb_raise(rb_eRuntimeError, "%s: output init failed rc=%d", ca_calling_method_name(), rc);
71
78
  }
72
79
 
73
80
  ca_size_t fiber_n = st_in.slab_elements;
74
- sort_index_pair_i8 *buf =
75
- xmalloc(sizeof(sort_index_pair_i8) * (size_t) fiber_n);
81
+ volatile VALUE hbuf = 0, haux = 0;
82
+ sort_index_pair_i8 *buf = ALLOCV_N(sort_index_pair_i8, hbuf, fiber_n);
76
83
  sort_index_pair_i8 *aux = NULL;
77
84
  if ( do_stable ) {
78
- aux = xmalloc(sizeof(sort_index_pair_i8) * (size_t) fiber_n);
85
+ aux = ALLOCV_N(sort_index_pair_i8, haux, fiber_n);
79
86
  }
80
87
 
81
88
  char *pi, *po;
@@ -146,8 +153,8 @@ sort_index_ki_native_i8 (VALUE self, CArray *ca, int axis, int do_stable, int ma
146
153
  }
147
154
  ca_iter_state_sync_slab(&st_out);
148
155
  }
149
- xfree(buf);
150
- if ( aux ) xfree(aux);
156
+ ALLOCV_END(hbuf);
157
+ ALLOCV_END(haux);
151
158
  ca_iter_state_finish(&st_in);
152
159
  ca_iter_state_finish(&st_out);
153
160
  return vout;
@@ -176,21 +183,21 @@ sort_index_ki_native_u8 (VALUE self, CArray *ca, int axis, int do_stable, int ma
176
183
  rc = ca_iter_state_init_l2(&st_in, ca, CA_SLAB_AXES,
177
184
  slab_axes, 1, 0);
178
185
  if ( rc != CA_ITER_OK ) {
179
- rb_raise(rb_eRuntimeError, "sort_index_ki: input init failed rc=%d", rc);
186
+ rb_raise(rb_eRuntimeError, "%s: input init failed rc=%d", ca_calling_method_name(), rc);
180
187
  }
181
- rc = ca_iter_state_init_l2(&st_out, co, CA_SLAB_AXES,
188
+ rc = ca_iter_state_init_l2_paired(&st_out, &st_in, co, CA_SLAB_AXES,
182
189
  slab_axes, 1, CA_KERNEL_WRITE);
183
190
  if ( rc != CA_ITER_OK ) {
184
191
  ca_iter_state_finish(&st_in);
185
- rb_raise(rb_eRuntimeError, "sort_index_ki: output init failed rc=%d", rc);
192
+ rb_raise(rb_eRuntimeError, "%s: output init failed rc=%d", ca_calling_method_name(), rc);
186
193
  }
187
194
 
188
195
  ca_size_t fiber_n = st_in.slab_elements;
189
- sort_index_pair_u8 *buf =
190
- xmalloc(sizeof(sort_index_pair_u8) * (size_t) fiber_n);
196
+ volatile VALUE hbuf = 0, haux = 0;
197
+ sort_index_pair_u8 *buf = ALLOCV_N(sort_index_pair_u8, hbuf, fiber_n);
191
198
  sort_index_pair_u8 *aux = NULL;
192
199
  if ( do_stable ) {
193
- aux = xmalloc(sizeof(sort_index_pair_u8) * (size_t) fiber_n);
200
+ aux = ALLOCV_N(sort_index_pair_u8, haux, fiber_n);
194
201
  }
195
202
 
196
203
  char *pi, *po;
@@ -261,8 +268,8 @@ sort_index_ki_native_u8 (VALUE self, CArray *ca, int axis, int do_stable, int ma
261
268
  }
262
269
  ca_iter_state_sync_slab(&st_out);
263
270
  }
264
- xfree(buf);
265
- if ( aux ) xfree(aux);
271
+ ALLOCV_END(hbuf);
272
+ ALLOCV_END(haux);
266
273
  ca_iter_state_finish(&st_in);
267
274
  ca_iter_state_finish(&st_out);
268
275
  return vout;
@@ -291,21 +298,21 @@ sort_index_ki_native_i16 (VALUE self, CArray *ca, int axis, int do_stable, int m
291
298
  rc = ca_iter_state_init_l2(&st_in, ca, CA_SLAB_AXES,
292
299
  slab_axes, 1, 0);
293
300
  if ( rc != CA_ITER_OK ) {
294
- rb_raise(rb_eRuntimeError, "sort_index_ki: input init failed rc=%d", rc);
301
+ rb_raise(rb_eRuntimeError, "%s: input init failed rc=%d", ca_calling_method_name(), rc);
295
302
  }
296
- rc = ca_iter_state_init_l2(&st_out, co, CA_SLAB_AXES,
303
+ rc = ca_iter_state_init_l2_paired(&st_out, &st_in, co, CA_SLAB_AXES,
297
304
  slab_axes, 1, CA_KERNEL_WRITE);
298
305
  if ( rc != CA_ITER_OK ) {
299
306
  ca_iter_state_finish(&st_in);
300
- rb_raise(rb_eRuntimeError, "sort_index_ki: output init failed rc=%d", rc);
307
+ rb_raise(rb_eRuntimeError, "%s: output init failed rc=%d", ca_calling_method_name(), rc);
301
308
  }
302
309
 
303
310
  ca_size_t fiber_n = st_in.slab_elements;
304
- sort_index_pair_i16 *buf =
305
- xmalloc(sizeof(sort_index_pair_i16) * (size_t) fiber_n);
311
+ volatile VALUE hbuf = 0, haux = 0;
312
+ sort_index_pair_i16 *buf = ALLOCV_N(sort_index_pair_i16, hbuf, fiber_n);
306
313
  sort_index_pair_i16 *aux = NULL;
307
314
  if ( do_stable ) {
308
- aux = xmalloc(sizeof(sort_index_pair_i16) * (size_t) fiber_n);
315
+ aux = ALLOCV_N(sort_index_pair_i16, haux, fiber_n);
309
316
  }
310
317
 
311
318
  char *pi, *po;
@@ -376,8 +383,8 @@ sort_index_ki_native_i16 (VALUE self, CArray *ca, int axis, int do_stable, int m
376
383
  }
377
384
  ca_iter_state_sync_slab(&st_out);
378
385
  }
379
- xfree(buf);
380
- if ( aux ) xfree(aux);
386
+ ALLOCV_END(hbuf);
387
+ ALLOCV_END(haux);
381
388
  ca_iter_state_finish(&st_in);
382
389
  ca_iter_state_finish(&st_out);
383
390
  return vout;
@@ -406,21 +413,21 @@ sort_index_ki_native_u16 (VALUE self, CArray *ca, int axis, int do_stable, int m
406
413
  rc = ca_iter_state_init_l2(&st_in, ca, CA_SLAB_AXES,
407
414
  slab_axes, 1, 0);
408
415
  if ( rc != CA_ITER_OK ) {
409
- rb_raise(rb_eRuntimeError, "sort_index_ki: input init failed rc=%d", rc);
416
+ rb_raise(rb_eRuntimeError, "%s: input init failed rc=%d", ca_calling_method_name(), rc);
410
417
  }
411
- rc = ca_iter_state_init_l2(&st_out, co, CA_SLAB_AXES,
418
+ rc = ca_iter_state_init_l2_paired(&st_out, &st_in, co, CA_SLAB_AXES,
412
419
  slab_axes, 1, CA_KERNEL_WRITE);
413
420
  if ( rc != CA_ITER_OK ) {
414
421
  ca_iter_state_finish(&st_in);
415
- rb_raise(rb_eRuntimeError, "sort_index_ki: output init failed rc=%d", rc);
422
+ rb_raise(rb_eRuntimeError, "%s: output init failed rc=%d", ca_calling_method_name(), rc);
416
423
  }
417
424
 
418
425
  ca_size_t fiber_n = st_in.slab_elements;
419
- sort_index_pair_u16 *buf =
420
- xmalloc(sizeof(sort_index_pair_u16) * (size_t) fiber_n);
426
+ volatile VALUE hbuf = 0, haux = 0;
427
+ sort_index_pair_u16 *buf = ALLOCV_N(sort_index_pair_u16, hbuf, fiber_n);
421
428
  sort_index_pair_u16 *aux = NULL;
422
429
  if ( do_stable ) {
423
- aux = xmalloc(sizeof(sort_index_pair_u16) * (size_t) fiber_n);
430
+ aux = ALLOCV_N(sort_index_pair_u16, haux, fiber_n);
424
431
  }
425
432
 
426
433
  char *pi, *po;
@@ -491,8 +498,8 @@ sort_index_ki_native_u16 (VALUE self, CArray *ca, int axis, int do_stable, int m
491
498
  }
492
499
  ca_iter_state_sync_slab(&st_out);
493
500
  }
494
- xfree(buf);
495
- if ( aux ) xfree(aux);
501
+ ALLOCV_END(hbuf);
502
+ ALLOCV_END(haux);
496
503
  ca_iter_state_finish(&st_in);
497
504
  ca_iter_state_finish(&st_out);
498
505
  return vout;
@@ -521,21 +528,21 @@ sort_index_ki_native_i32 (VALUE self, CArray *ca, int axis, int do_stable, int m
521
528
  rc = ca_iter_state_init_l2(&st_in, ca, CA_SLAB_AXES,
522
529
  slab_axes, 1, 0);
523
530
  if ( rc != CA_ITER_OK ) {
524
- rb_raise(rb_eRuntimeError, "sort_index_ki: input init failed rc=%d", rc);
531
+ rb_raise(rb_eRuntimeError, "%s: input init failed rc=%d", ca_calling_method_name(), rc);
525
532
  }
526
- rc = ca_iter_state_init_l2(&st_out, co, CA_SLAB_AXES,
533
+ rc = ca_iter_state_init_l2_paired(&st_out, &st_in, co, CA_SLAB_AXES,
527
534
  slab_axes, 1, CA_KERNEL_WRITE);
528
535
  if ( rc != CA_ITER_OK ) {
529
536
  ca_iter_state_finish(&st_in);
530
- rb_raise(rb_eRuntimeError, "sort_index_ki: output init failed rc=%d", rc);
537
+ rb_raise(rb_eRuntimeError, "%s: output init failed rc=%d", ca_calling_method_name(), rc);
531
538
  }
532
539
 
533
540
  ca_size_t fiber_n = st_in.slab_elements;
534
- sort_index_pair_i32 *buf =
535
- xmalloc(sizeof(sort_index_pair_i32) * (size_t) fiber_n);
541
+ volatile VALUE hbuf = 0, haux = 0;
542
+ sort_index_pair_i32 *buf = ALLOCV_N(sort_index_pair_i32, hbuf, fiber_n);
536
543
  sort_index_pair_i32 *aux = NULL;
537
544
  if ( do_stable ) {
538
- aux = xmalloc(sizeof(sort_index_pair_i32) * (size_t) fiber_n);
545
+ aux = ALLOCV_N(sort_index_pair_i32, haux, fiber_n);
539
546
  }
540
547
 
541
548
  char *pi, *po;
@@ -606,8 +613,8 @@ sort_index_ki_native_i32 (VALUE self, CArray *ca, int axis, int do_stable, int m
606
613
  }
607
614
  ca_iter_state_sync_slab(&st_out);
608
615
  }
609
- xfree(buf);
610
- if ( aux ) xfree(aux);
616
+ ALLOCV_END(hbuf);
617
+ ALLOCV_END(haux);
611
618
  ca_iter_state_finish(&st_in);
612
619
  ca_iter_state_finish(&st_out);
613
620
  return vout;
@@ -636,21 +643,21 @@ sort_index_ki_native_u32 (VALUE self, CArray *ca, int axis, int do_stable, int m
636
643
  rc = ca_iter_state_init_l2(&st_in, ca, CA_SLAB_AXES,
637
644
  slab_axes, 1, 0);
638
645
  if ( rc != CA_ITER_OK ) {
639
- rb_raise(rb_eRuntimeError, "sort_index_ki: input init failed rc=%d", rc);
646
+ rb_raise(rb_eRuntimeError, "%s: input init failed rc=%d", ca_calling_method_name(), rc);
640
647
  }
641
- rc = ca_iter_state_init_l2(&st_out, co, CA_SLAB_AXES,
648
+ rc = ca_iter_state_init_l2_paired(&st_out, &st_in, co, CA_SLAB_AXES,
642
649
  slab_axes, 1, CA_KERNEL_WRITE);
643
650
  if ( rc != CA_ITER_OK ) {
644
651
  ca_iter_state_finish(&st_in);
645
- rb_raise(rb_eRuntimeError, "sort_index_ki: output init failed rc=%d", rc);
652
+ rb_raise(rb_eRuntimeError, "%s: output init failed rc=%d", ca_calling_method_name(), rc);
646
653
  }
647
654
 
648
655
  ca_size_t fiber_n = st_in.slab_elements;
649
- sort_index_pair_u32 *buf =
650
- xmalloc(sizeof(sort_index_pair_u32) * (size_t) fiber_n);
656
+ volatile VALUE hbuf = 0, haux = 0;
657
+ sort_index_pair_u32 *buf = ALLOCV_N(sort_index_pair_u32, hbuf, fiber_n);
651
658
  sort_index_pair_u32 *aux = NULL;
652
659
  if ( do_stable ) {
653
- aux = xmalloc(sizeof(sort_index_pair_u32) * (size_t) fiber_n);
660
+ aux = ALLOCV_N(sort_index_pair_u32, haux, fiber_n);
654
661
  }
655
662
 
656
663
  char *pi, *po;
@@ -721,8 +728,8 @@ sort_index_ki_native_u32 (VALUE self, CArray *ca, int axis, int do_stable, int m
721
728
  }
722
729
  ca_iter_state_sync_slab(&st_out);
723
730
  }
724
- xfree(buf);
725
- if ( aux ) xfree(aux);
731
+ ALLOCV_END(hbuf);
732
+ ALLOCV_END(haux);
726
733
  ca_iter_state_finish(&st_in);
727
734
  ca_iter_state_finish(&st_out);
728
735
  return vout;
@@ -751,21 +758,21 @@ sort_index_ki_native_i64 (VALUE self, CArray *ca, int axis, int do_stable, int m
751
758
  rc = ca_iter_state_init_l2(&st_in, ca, CA_SLAB_AXES,
752
759
  slab_axes, 1, 0);
753
760
  if ( rc != CA_ITER_OK ) {
754
- rb_raise(rb_eRuntimeError, "sort_index_ki: input init failed rc=%d", rc);
761
+ rb_raise(rb_eRuntimeError, "%s: input init failed rc=%d", ca_calling_method_name(), rc);
755
762
  }
756
- rc = ca_iter_state_init_l2(&st_out, co, CA_SLAB_AXES,
763
+ rc = ca_iter_state_init_l2_paired(&st_out, &st_in, co, CA_SLAB_AXES,
757
764
  slab_axes, 1, CA_KERNEL_WRITE);
758
765
  if ( rc != CA_ITER_OK ) {
759
766
  ca_iter_state_finish(&st_in);
760
- rb_raise(rb_eRuntimeError, "sort_index_ki: output init failed rc=%d", rc);
767
+ rb_raise(rb_eRuntimeError, "%s: output init failed rc=%d", ca_calling_method_name(), rc);
761
768
  }
762
769
 
763
770
  ca_size_t fiber_n = st_in.slab_elements;
764
- sort_index_pair_i64 *buf =
765
- xmalloc(sizeof(sort_index_pair_i64) * (size_t) fiber_n);
771
+ volatile VALUE hbuf = 0, haux = 0;
772
+ sort_index_pair_i64 *buf = ALLOCV_N(sort_index_pair_i64, hbuf, fiber_n);
766
773
  sort_index_pair_i64 *aux = NULL;
767
774
  if ( do_stable ) {
768
- aux = xmalloc(sizeof(sort_index_pair_i64) * (size_t) fiber_n);
775
+ aux = ALLOCV_N(sort_index_pair_i64, haux, fiber_n);
769
776
  }
770
777
 
771
778
  char *pi, *po;
@@ -836,8 +843,8 @@ sort_index_ki_native_i64 (VALUE self, CArray *ca, int axis, int do_stable, int m
836
843
  }
837
844
  ca_iter_state_sync_slab(&st_out);
838
845
  }
839
- xfree(buf);
840
- if ( aux ) xfree(aux);
846
+ ALLOCV_END(hbuf);
847
+ ALLOCV_END(haux);
841
848
  ca_iter_state_finish(&st_in);
842
849
  ca_iter_state_finish(&st_out);
843
850
  return vout;
@@ -866,21 +873,21 @@ sort_index_ki_native_u64 (VALUE self, CArray *ca, int axis, int do_stable, int m
866
873
  rc = ca_iter_state_init_l2(&st_in, ca, CA_SLAB_AXES,
867
874
  slab_axes, 1, 0);
868
875
  if ( rc != CA_ITER_OK ) {
869
- rb_raise(rb_eRuntimeError, "sort_index_ki: input init failed rc=%d", rc);
876
+ rb_raise(rb_eRuntimeError, "%s: input init failed rc=%d", ca_calling_method_name(), rc);
870
877
  }
871
- rc = ca_iter_state_init_l2(&st_out, co, CA_SLAB_AXES,
878
+ rc = ca_iter_state_init_l2_paired(&st_out, &st_in, co, CA_SLAB_AXES,
872
879
  slab_axes, 1, CA_KERNEL_WRITE);
873
880
  if ( rc != CA_ITER_OK ) {
874
881
  ca_iter_state_finish(&st_in);
875
- rb_raise(rb_eRuntimeError, "sort_index_ki: output init failed rc=%d", rc);
882
+ rb_raise(rb_eRuntimeError, "%s: output init failed rc=%d", ca_calling_method_name(), rc);
876
883
  }
877
884
 
878
885
  ca_size_t fiber_n = st_in.slab_elements;
879
- sort_index_pair_u64 *buf =
880
- xmalloc(sizeof(sort_index_pair_u64) * (size_t) fiber_n);
886
+ volatile VALUE hbuf = 0, haux = 0;
887
+ sort_index_pair_u64 *buf = ALLOCV_N(sort_index_pair_u64, hbuf, fiber_n);
881
888
  sort_index_pair_u64 *aux = NULL;
882
889
  if ( do_stable ) {
883
- aux = xmalloc(sizeof(sort_index_pair_u64) * (size_t) fiber_n);
890
+ aux = ALLOCV_N(sort_index_pair_u64, haux, fiber_n);
884
891
  }
885
892
 
886
893
  char *pi, *po;
@@ -951,8 +958,8 @@ sort_index_ki_native_u64 (VALUE self, CArray *ca, int axis, int do_stable, int m
951
958
  }
952
959
  ca_iter_state_sync_slab(&st_out);
953
960
  }
954
- xfree(buf);
955
- if ( aux ) xfree(aux);
961
+ ALLOCV_END(hbuf);
962
+ ALLOCV_END(haux);
956
963
  ca_iter_state_finish(&st_in);
957
964
  ca_iter_state_finish(&st_out);
958
965
  return vout;
@@ -981,21 +988,21 @@ sort_index_ki_native_f32 (VALUE self, CArray *ca, int axis, int do_stable, int m
981
988
  rc = ca_iter_state_init_l2(&st_in, ca, CA_SLAB_AXES,
982
989
  slab_axes, 1, 0);
983
990
  if ( rc != CA_ITER_OK ) {
984
- rb_raise(rb_eRuntimeError, "sort_index_ki: input init failed rc=%d", rc);
991
+ rb_raise(rb_eRuntimeError, "%s: input init failed rc=%d", ca_calling_method_name(), rc);
985
992
  }
986
- rc = ca_iter_state_init_l2(&st_out, co, CA_SLAB_AXES,
993
+ rc = ca_iter_state_init_l2_paired(&st_out, &st_in, co, CA_SLAB_AXES,
987
994
  slab_axes, 1, CA_KERNEL_WRITE);
988
995
  if ( rc != CA_ITER_OK ) {
989
996
  ca_iter_state_finish(&st_in);
990
- rb_raise(rb_eRuntimeError, "sort_index_ki: output init failed rc=%d", rc);
997
+ rb_raise(rb_eRuntimeError, "%s: output init failed rc=%d", ca_calling_method_name(), rc);
991
998
  }
992
999
 
993
1000
  ca_size_t fiber_n = st_in.slab_elements;
994
- sort_index_pair_f32 *buf =
995
- xmalloc(sizeof(sort_index_pair_f32) * (size_t) fiber_n);
1001
+ volatile VALUE hbuf = 0, haux = 0;
1002
+ sort_index_pair_f32 *buf = ALLOCV_N(sort_index_pair_f32, hbuf, fiber_n);
996
1003
  sort_index_pair_f32 *aux = NULL;
997
1004
  if ( do_stable ) {
998
- aux = xmalloc(sizeof(sort_index_pair_f32) * (size_t) fiber_n);
1005
+ aux = ALLOCV_N(sort_index_pair_f32, haux, fiber_n);
999
1006
  }
1000
1007
 
1001
1008
  char *pi, *po;
@@ -1070,8 +1077,8 @@ sort_index_ki_native_f32 (VALUE self, CArray *ca, int axis, int do_stable, int m
1070
1077
  }
1071
1078
  ca_iter_state_sync_slab(&st_out);
1072
1079
  }
1073
- xfree(buf);
1074
- if ( aux ) xfree(aux);
1080
+ ALLOCV_END(hbuf);
1081
+ ALLOCV_END(haux);
1075
1082
  ca_iter_state_finish(&st_in);
1076
1083
  ca_iter_state_finish(&st_out);
1077
1084
  return vout;
@@ -1100,21 +1107,21 @@ sort_index_ki_native_f64 (VALUE self, CArray *ca, int axis, int do_stable, int m
1100
1107
  rc = ca_iter_state_init_l2(&st_in, ca, CA_SLAB_AXES,
1101
1108
  slab_axes, 1, 0);
1102
1109
  if ( rc != CA_ITER_OK ) {
1103
- rb_raise(rb_eRuntimeError, "sort_index_ki: input init failed rc=%d", rc);
1110
+ rb_raise(rb_eRuntimeError, "%s: input init failed rc=%d", ca_calling_method_name(), rc);
1104
1111
  }
1105
- rc = ca_iter_state_init_l2(&st_out, co, CA_SLAB_AXES,
1112
+ rc = ca_iter_state_init_l2_paired(&st_out, &st_in, co, CA_SLAB_AXES,
1106
1113
  slab_axes, 1, CA_KERNEL_WRITE);
1107
1114
  if ( rc != CA_ITER_OK ) {
1108
1115
  ca_iter_state_finish(&st_in);
1109
- rb_raise(rb_eRuntimeError, "sort_index_ki: output init failed rc=%d", rc);
1116
+ rb_raise(rb_eRuntimeError, "%s: output init failed rc=%d", ca_calling_method_name(), rc);
1110
1117
  }
1111
1118
 
1112
1119
  ca_size_t fiber_n = st_in.slab_elements;
1113
- sort_index_pair_f64 *buf =
1114
- xmalloc(sizeof(sort_index_pair_f64) * (size_t) fiber_n);
1120
+ volatile VALUE hbuf = 0, haux = 0;
1121
+ sort_index_pair_f64 *buf = ALLOCV_N(sort_index_pair_f64, hbuf, fiber_n);
1115
1122
  sort_index_pair_f64 *aux = NULL;
1116
1123
  if ( do_stable ) {
1117
- aux = xmalloc(sizeof(sort_index_pair_f64) * (size_t) fiber_n);
1124
+ aux = ALLOCV_N(sort_index_pair_f64, haux, fiber_n);
1118
1125
  }
1119
1126
 
1120
1127
  char *pi, *po;
@@ -1189,8 +1196,8 @@ sort_index_ki_native_f64 (VALUE self, CArray *ca, int axis, int do_stable, int m
1189
1196
  }
1190
1197
  ca_iter_state_sync_slab(&st_out);
1191
1198
  }
1192
- xfree(buf);
1193
- if ( aux ) xfree(aux);
1199
+ ALLOCV_END(hbuf);
1200
+ ALLOCV_END(haux);
1194
1201
  ca_iter_state_finish(&st_in);
1195
1202
  ca_iter_state_finish(&st_out);
1196
1203
  return vout;
@@ -1219,21 +1226,21 @@ sort_index_ki_native_bool (VALUE self, CArray *ca, int axis, int do_stable, int
1219
1226
  rc = ca_iter_state_init_l2(&st_in, ca, CA_SLAB_AXES,
1220
1227
  slab_axes, 1, 0);
1221
1228
  if ( rc != CA_ITER_OK ) {
1222
- rb_raise(rb_eRuntimeError, "sort_index_ki: input init failed rc=%d", rc);
1229
+ rb_raise(rb_eRuntimeError, "%s: input init failed rc=%d", ca_calling_method_name(), rc);
1223
1230
  }
1224
- rc = ca_iter_state_init_l2(&st_out, co, CA_SLAB_AXES,
1231
+ rc = ca_iter_state_init_l2_paired(&st_out, &st_in, co, CA_SLAB_AXES,
1225
1232
  slab_axes, 1, CA_KERNEL_WRITE);
1226
1233
  if ( rc != CA_ITER_OK ) {
1227
1234
  ca_iter_state_finish(&st_in);
1228
- rb_raise(rb_eRuntimeError, "sort_index_ki: output init failed rc=%d", rc);
1235
+ rb_raise(rb_eRuntimeError, "%s: output init failed rc=%d", ca_calling_method_name(), rc);
1229
1236
  }
1230
1237
 
1231
1238
  ca_size_t fiber_n = st_in.slab_elements;
1232
- sort_index_pair_bool *buf =
1233
- xmalloc(sizeof(sort_index_pair_bool) * (size_t) fiber_n);
1239
+ volatile VALUE hbuf = 0, haux = 0;
1240
+ sort_index_pair_bool *buf = ALLOCV_N(sort_index_pair_bool, hbuf, fiber_n);
1234
1241
  sort_index_pair_bool *aux = NULL;
1235
1242
  if ( do_stable ) {
1236
- aux = xmalloc(sizeof(sort_index_pair_bool) * (size_t) fiber_n);
1243
+ aux = ALLOCV_N(sort_index_pair_bool, haux, fiber_n);
1237
1244
  }
1238
1245
 
1239
1246
  char *pi, *po;
@@ -1304,8 +1311,8 @@ sort_index_ki_native_bool (VALUE self, CArray *ca, int axis, int do_stable, int
1304
1311
  }
1305
1312
  ca_iter_state_sync_slab(&st_out);
1306
1313
  }
1307
- xfree(buf);
1308
- if ( aux ) xfree(aux);
1314
+ ALLOCV_END(hbuf);
1315
+ ALLOCV_END(haux);
1309
1316
  ca_iter_state_finish(&st_in);
1310
1317
  ca_iter_state_finish(&st_out);
1311
1318
  return vout;
@@ -1321,23 +1328,42 @@ sort_index_cmp_object (const void *a, const void *b)
1321
1328
  {
1322
1329
  const sort_index_pair_object *pa = (const sort_index_pair_object *) a;
1323
1330
  const sort_index_pair_object *pb = (const sort_index_pair_object *) b;
1324
- VALUE r = rb_funcall(pa->v, rb_intern("<=>"), 1, pb->v);
1325
- if ( NIL_P(r) ) {
1326
- rb_raise(rb_eArgError, "sort_index_ki: comparison of %s with %s failed",
1327
- rb_obj_classname(pa->v), rb_obj_classname(pb->v));
1328
- }
1329
- int c = NUM2INT(r);
1330
- if ( c > 0 ) return 1;
1331
- if ( c < 0 ) return -1;
1331
+ int c = ca_obj_order_cmp(pa->v, pb->v);
1332
+ if ( c != 0 ) return c;
1332
1333
  /* Stable tie-break by original fiber-local index. */
1333
1334
  if ( pa->i < pb->i ) return -1;
1334
1335
  if ( pa->i > pb->i ) return 1;
1335
1336
  return 0;
1336
1337
  }
1337
1338
 
1339
+ /* The object lane calls Ruby for every cell, so a walk can raise part
1340
+ way. Its states live in the caller's frame, and ca_iter_ensure
1341
+ finishes them however the body leaves. */
1342
+ typedef struct {
1343
+ ca_iter_state states[2];
1344
+ VALUE self;
1345
+ CArray *ca;
1346
+ int axis;
1347
+ int do_stable;
1348
+ int masked_last;
1349
+ } sort_index_ki_native_object_frame;
1350
+
1338
1351
  static VALUE
1339
- sort_index_ki_native_object (VALUE self, CArray *ca, int axis, int do_stable, int masked_last)
1340
- {
1352
+ sort_index_ki_native_object_body (VALUE arg)
1353
+ {
1354
+ sort_index_ki_native_object_frame *fr = (sort_index_ki_native_object_frame *) arg;
1355
+ VALUE self = fr->self;
1356
+ (void) self;
1357
+ CArray *ca = fr->ca;
1358
+ (void) ca;
1359
+ int axis = fr->axis;
1360
+ (void) axis;
1361
+ int do_stable = fr->do_stable;
1362
+ (void) do_stable;
1363
+ int masked_last = fr->masked_last;
1364
+ (void) masked_last;
1365
+ #define st_in (fr->states[0])
1366
+ #define st_out (fr->states[1])
1341
1367
  VALUE vout = rb_ca_template_with_type(self, INT2NUM(CA_SIZE),
1342
1368
  INT2NUM(0));
1343
1369
  CArray *co;
@@ -1348,27 +1374,23 @@ sort_index_ki_native_object (VALUE self, CArray *ca, int axis, int do_stable, in
1348
1374
  positions align between iterators. */
1349
1375
  int8_t slab_axes[CA_RANK_MAX];
1350
1376
  slab_axes[0] = (int8_t) axis;
1351
- ca_iter_state st_in, st_out;
1352
1377
  int rc;
1353
1378
  rc = ca_iter_state_init_l2(&st_in, ca, CA_SLAB_AXES,
1354
1379
  slab_axes, 1, 0);
1355
1380
  if ( rc != CA_ITER_OK ) {
1356
- rb_raise(rb_eRuntimeError, "sort_index_ki: input init failed rc=%d", rc);
1381
+ rb_raise(rb_eRuntimeError, "%s: input init failed rc=%d", ca_calling_method_name(), rc);
1357
1382
  }
1358
- rc = ca_iter_state_init_l2(&st_out, co, CA_SLAB_AXES,
1383
+ rc = ca_iter_state_init_l2_paired(&st_out, &st_in, co, CA_SLAB_AXES,
1359
1384
  slab_axes, 1, CA_KERNEL_WRITE);
1360
1385
  if ( rc != CA_ITER_OK ) {
1361
1386
  ca_iter_state_finish(&st_in);
1362
- rb_raise(rb_eRuntimeError, "sort_index_ki: output init failed rc=%d", rc);
1387
+ rb_raise(rb_eRuntimeError, "%s: output init failed rc=%d", ca_calling_method_name(), rc);
1363
1388
  }
1364
1389
 
1365
1390
  ca_size_t fiber_n = st_in.slab_elements;
1366
- sort_index_pair_object *buf =
1367
- xmalloc(sizeof(sort_index_pair_object) * (size_t) fiber_n);
1368
- sort_index_pair_object *aux = NULL;
1369
- if ( do_stable ) {
1370
- aux = xmalloc(sizeof(sort_index_pair_object) * (size_t) fiber_n);
1371
- }
1391
+ volatile VALUE hbuf = 0, haux = 0;
1392
+ sort_index_pair_object *buf = ALLOCV_N(sort_index_pair_object, hbuf, fiber_n);
1393
+ sort_index_pair_object *aux = ALLOCV_N(sort_index_pair_object, haux, fiber_n);
1372
1394
 
1373
1395
  char *pi, *po;
1374
1396
  boolean8_t *mi, *mo;
@@ -1427,21 +1449,36 @@ sort_index_ki_native_object (VALUE self, CArray *ca, int axis, int do_stable, in
1427
1449
  selects introsort with mergesort escape (= AC1 1.88x payoff).
1428
1450
  Only the unmasked sub-range [sort_lo, sort_lo+sort_n) is
1429
1451
  sorted -- see emit_sort_mask_split_load in mkkernel.rb. */
1430
- (void) do_stable; /* CA_OBJECT: qsort handles both stable / quick */
1431
- qsort((buf + sort_lo), (size_t) sort_n, sizeof(sort_index_pair_object), sort_index_cmp_object);
1452
+ (void) do_stable; /* CA_OBJECT: one stable sort serves both kinds */
1453
+ ca_sort_merge_pair_object((ca_pair_object *) (buf + sort_lo), (ca_pair_object *) aux, (ca_size_t) sort_n, sort_index_cmp_object);
1432
1454
  /* Write sorted fiber-local indices to output fiber. */
1433
1455
  for ( ca_size_t k = 0; k < fiber_n; k++ ) {
1434
1456
  *(ca_size_t *) (po + k * ostride) = (ca_size_t) buf[k].i;
1435
1457
  }
1436
1458
  ca_iter_state_sync_slab(&st_out);
1437
1459
  }
1438
- xfree(buf);
1439
- if ( aux ) xfree(aux);
1460
+ ALLOCV_END(hbuf);
1461
+ ALLOCV_END(haux);
1440
1462
  ca_iter_state_finish(&st_in);
1441
1463
  ca_iter_state_finish(&st_out);
1442
1464
  return vout;
1465
+ #undef st_in
1466
+ #undef st_out
1443
1467
  }
1444
1468
 
1469
+ static VALUE
1470
+ sort_index_ki_native_object (VALUE self, CArray *ca, int axis, int do_stable, int masked_last)
1471
+ {
1472
+ sort_index_ki_native_object_frame fr;
1473
+ fr.self = self;
1474
+ fr.ca = ca;
1475
+ fr.axis = axis;
1476
+ fr.do_stable = do_stable;
1477
+ fr.masked_last = masked_last;
1478
+ return ca_iter_ensure(2, fr.states, sort_index_ki_native_object_body, (VALUE) &fr);
1479
+ }
1480
+
1481
+
1445
1482
  typedef struct {
1446
1483
  char *vp; /* pointer to the fiber cell (nb bytes wide) */
1447
1484
  ca_size_t nb; /* element byte width (= ca->bytes) */
@@ -1479,21 +1516,21 @@ sort_index_ki_native_fixlen (VALUE self, CArray *ca, int axis, int do_stable, in
1479
1516
  rc = ca_iter_state_init_l2(&st_in, ca, CA_SLAB_AXES,
1480
1517
  slab_axes, 1, 0);
1481
1518
  if ( rc != CA_ITER_OK ) {
1482
- rb_raise(rb_eRuntimeError, "sort_index_ki: input init failed rc=%d", rc);
1519
+ rb_raise(rb_eRuntimeError, "%s: input init failed rc=%d", ca_calling_method_name(), rc);
1483
1520
  }
1484
- rc = ca_iter_state_init_l2(&st_out, co, CA_SLAB_AXES,
1521
+ rc = ca_iter_state_init_l2_paired(&st_out, &st_in, co, CA_SLAB_AXES,
1485
1522
  slab_axes, 1, CA_KERNEL_WRITE);
1486
1523
  if ( rc != CA_ITER_OK ) {
1487
1524
  ca_iter_state_finish(&st_in);
1488
- rb_raise(rb_eRuntimeError, "sort_index_ki: output init failed rc=%d", rc);
1525
+ rb_raise(rb_eRuntimeError, "%s: output init failed rc=%d", ca_calling_method_name(), rc);
1489
1526
  }
1490
1527
 
1491
1528
  ca_size_t fiber_n = st_in.slab_elements;
1492
- sort_index_pair_fixlen *buf =
1493
- xmalloc(sizeof(sort_index_pair_fixlen) * (size_t) fiber_n);
1529
+ volatile VALUE hbuf = 0, haux = 0;
1530
+ sort_index_pair_fixlen *buf = ALLOCV_N(sort_index_pair_fixlen, hbuf, fiber_n);
1494
1531
  sort_index_pair_fixlen *aux = NULL;
1495
1532
  if ( do_stable ) {
1496
- aux = xmalloc(sizeof(sort_index_pair_fixlen) * (size_t) fiber_n);
1533
+ aux = ALLOCV_N(sort_index_pair_fixlen, haux, fiber_n);
1497
1534
  }
1498
1535
 
1499
1536
  char *pi, *po;
@@ -1565,8 +1602,8 @@ sort_index_ki_native_fixlen (VALUE self, CArray *ca, int axis, int do_stable, in
1565
1602
  }
1566
1603
  ca_iter_state_sync_slab(&st_out);
1567
1604
  }
1568
- xfree(buf);
1569
- if ( aux ) xfree(aux);
1605
+ ALLOCV_END(hbuf);
1606
+ ALLOCV_END(haux);
1570
1607
  ca_iter_state_finish(&st_in);
1571
1608
  ca_iter_state_finish(&st_out);
1572
1609
  return vout;
@@ -1577,46 +1614,39 @@ sort_index_ki_dispatch (VALUE self, VALUE vaxis, int do_stable, int masked_last)
1577
1614
  {
1578
1615
  CArray *src;
1579
1616
  GetCArray(self, src);
1580
- /* Face gate: descend to storage (fixlen -> memcmp default,
1581
- numeric -> ORDERABLE required). The strip also makes the
1582
- switch below and every index/addr template output build on
1583
- the plain storage, not a face-lifted (NULL-ptr) view. */
1617
+ if ( ca_is_lazy_view(src) ) {
1618
+ volatile VALUE evaluated = ca_lazy_evaluated(self);
1619
+ if ( ! NIL_P(evaluated) ) {
1620
+ self = evaluated;
1621
+ GetCArray(self, src);
1622
+ }
1623
+ }
1624
+ /* Face gate: run on the array the Face's order is read from.
1625
+ It is never a Face, so the switch below and every index/addr
1626
+ template output build on a plain array, not a face-lifted
1627
+ (NULL-ptr) view. */
1584
1628
  if ( ca_is_face(src) ) {
1585
- VALUE _face = self;
1586
- int _orderable = ca_test_flag(src, CA_FLAG_FACE_ORDERABLE_STORAGE);
1587
- VALUE _st = rb_ca_strip_face_value(self);
1588
- CArray *_sc;
1589
- GetCArray(_st, _sc);
1590
- if ( _sc->data_type != CA_FIXLEN && ! _orderable ) {
1591
- rb_raise(rb_eArgError,
1592
- "sort_index_ki: Face-typed input (%s) is not orderable "
1593
- "by storage; use ca.parent to descend to storage",
1594
- rb_obj_classname(_face));
1595
- }
1596
- self = _st;
1597
- src = _sc;
1598
- }
1599
- int axis = NUM2INT(vaxis);
1600
- if ( axis < 0 ) axis += src->ndim;
1601
- if ( axis < 0 || axis >= src->ndim ) {
1602
- rb_raise(rb_eArgError, "sort_index_ki: axis %d out of range for ndim %d", NUM2INT(vaxis), src->ndim);
1629
+ self = ca_order_key(self, ca_calling_method_name());
1630
+ GetCArray(self, src);
1603
1631
  }
1632
+ int axis = ca_normalize_axis_arg(vaxis, src->ndim, NULL);
1604
1633
  switch ( src->data_type ) {
1605
- case CA_INT8: return sort_index_ki_native_i8(self, src, axis, do_stable, masked_last);
1606
- case CA_UINT8: return sort_index_ki_native_u8(self, src, axis, do_stable, masked_last);
1607
- case CA_INT16: return sort_index_ki_native_i16(self, src, axis, do_stable, masked_last);
1608
- case CA_UINT16: return sort_index_ki_native_u16(self, src, axis, do_stable, masked_last);
1609
- case CA_INT32: return sort_index_ki_native_i32(self, src, axis, do_stable, masked_last);
1610
- case CA_UINT32: return sort_index_ki_native_u32(self, src, axis, do_stable, masked_last);
1611
- case CA_INT64: return sort_index_ki_native_i64(self, src, axis, do_stable, masked_last);
1612
- case CA_UINT64: return sort_index_ki_native_u64(self, src, axis, do_stable, masked_last);
1613
- case CA_FLOAT32: return sort_index_ki_native_f32(self, src, axis, do_stable, masked_last);
1614
- case CA_FLOAT64: return sort_index_ki_native_f64(self, src, axis, do_stable, masked_last);
1615
- case CA_BOOLEAN: return sort_index_ki_native_bool(self, src, axis, do_stable, masked_last);
1616
- case CA_OBJECT: return sort_index_ki_native_object(self, src, axis, do_stable, masked_last);
1617
- case CA_FIXLEN: return sort_index_ki_native_fixlen(self, src, axis, do_stable, masked_last);
1634
+ case CA_INT8: CA_RETURN_KEEPING(self, sort_index_ki_native_i8(self, src, axis, do_stable, masked_last));
1635
+ case CA_UINT8: CA_RETURN_KEEPING(self, sort_index_ki_native_u8(self, src, axis, do_stable, masked_last));
1636
+ case CA_INT16: CA_RETURN_KEEPING(self, sort_index_ki_native_i16(self, src, axis, do_stable, masked_last));
1637
+ case CA_UINT16: CA_RETURN_KEEPING(self, sort_index_ki_native_u16(self, src, axis, do_stable, masked_last));
1638
+ case CA_INT32: CA_RETURN_KEEPING(self, sort_index_ki_native_i32(self, src, axis, do_stable, masked_last));
1639
+ case CA_UINT32: CA_RETURN_KEEPING(self, sort_index_ki_native_u32(self, src, axis, do_stable, masked_last));
1640
+ case CA_INT64: CA_RETURN_KEEPING(self, sort_index_ki_native_i64(self, src, axis, do_stable, masked_last));
1641
+ case CA_UINT64: CA_RETURN_KEEPING(self, sort_index_ki_native_u64(self, src, axis, do_stable, masked_last));
1642
+ case CA_FLOAT32: CA_RETURN_KEEPING(self, sort_index_ki_native_f32(self, src, axis, do_stable, masked_last));
1643
+ case CA_FLOAT64: CA_RETURN_KEEPING(self, sort_index_ki_native_f64(self, src, axis, do_stable, masked_last));
1644
+ case CA_BOOLEAN: CA_RETURN_KEEPING(self, sort_index_ki_native_bool(self, src, axis, do_stable, masked_last));
1645
+ case CA_OBJECT: CA_RETURN_KEEPING(self, sort_index_ki_native_object(self, src, axis, do_stable, masked_last));
1646
+ case CA_FIXLEN: CA_RETURN_KEEPING(self, sort_index_ki_native_fixlen(self, src, axis, do_stable, masked_last));
1618
1647
  default:
1619
- rb_raise(rb_eCADataTypeError, "sort_index_ki: source data_type :%s not supported (expected one of: i8, u8, i16, u16, i32, u32, i64, u64, f32, f64, bool, object, fixlen)", ca_type_name[src->data_type]);
1648
+ ca_face_refuse_numeric(self, ca_calling_method_name(), rb_eCADataTypeError);
1649
+ rb_raise(rb_eCADataTypeError, "%s: source data_type :%s not supported (expected one of: int8, uint8, int16, uint16, int32, uint32, int64, uint64, float32, float64, boolean, object, fixlen)", ca_calling_method_name(), ca_type_name[src->data_type]);
1620
1650
  }
1621
1651
  return Qnil; /* unreachable */
1622
1652
  }
@@ -1647,34 +1677,19 @@ rb_ca_sort_index_kw (int argc, VALUE *argv, VALUE self)
1647
1677
  volatile VALUE rkind = Qnil;
1648
1678
  volatile VALUE rmasked_position = Qnil;
1649
1679
  rb_scan_options(ropt, "axis,kind,masked_position", &raxis, &rkind, &rmasked_position);
1650
- if ( NIL_P(raxis) ) raxis = INT2NUM(0); /* default axis */
1651
- int masked_last = 1; /* default masked_position: :last */
1652
- if ( ! NIL_P(rmasked_position) ) {
1653
- static ID sym_first = 0, sym_last = 0;
1654
- if ( ! sym_first ) sym_first = rb_intern("first");
1655
- if ( ! sym_last ) sym_last = rb_intern("last");
1656
- ID mp_id = SYM2ID(rmasked_position);
1657
- if ( mp_id == sym_last ) masked_last = 1;
1658
- else if ( mp_id == sym_first ) masked_last = 0;
1659
- else {
1660
- rb_raise(rb_eArgError, "sort_index: unknown masked_position %s (expected :first or :last)", rb_id2name(mp_id));
1680
+ if ( NIL_P(raxis) ) { /* no axis: the whole array */
1681
+ {
1682
+ volatile VALUE evaluated = ca_lazy_evaluated(self);
1683
+ if ( ! NIL_P(evaluated) ) self = evaluated;
1661
1684
  }
1685
+ self = rb_ca_flatten(self);
1686
+ raxis = INT2NUM(0);
1662
1687
  }
1688
+ int masked_last = NIL_P(rmasked_position) ? 1 : ca_symbol_choice(rmasked_position, "masked_position", "first", "last", NULL);
1663
1689
  if ( argc != 0 ) {
1664
1690
  rb_raise(rb_eArgError, "sort_index: positional args no longer accepted (given %d args); use axis:/kind: kwargs", argc);
1665
1691
  }
1666
- int do_stable = 0;
1667
- if ( ! NIL_P(rkind) ) {
1668
- static ID sym_quick = 0, sym_stable = 0;
1669
- if ( ! sym_quick ) sym_quick = rb_intern("quick");
1670
- if ( ! sym_stable ) sym_stable = rb_intern("stable");
1671
- ID kind_id = SYM2ID(rkind);
1672
- if ( kind_id == sym_quick ) do_stable = 0;
1673
- else if ( kind_id == sym_stable ) do_stable = 1;
1674
- else {
1675
- rb_raise(rb_eArgError, "sort_index: unknown kind %s (expected :quick or :stable)", rb_id2name(kind_id));
1676
- }
1677
- }
1692
+ int do_stable = NIL_P(rkind) ? 0 : ca_symbol_choice(rkind, "kind", "quick", "stable", NULL);
1678
1693
  return sort_index_ki_dispatch(self, raxis, do_stable, masked_last);
1679
1694
  }
1680
1695
 
@@ -1703,21 +1718,21 @@ sort_addr_ki_native_i8 (VALUE self, CArray *ca, int axis, int do_stable, int mas
1703
1718
  rc = ca_iter_state_init_l2(&st_in, ca, CA_SLAB_AXES,
1704
1719
  slab_axes, 1, 0);
1705
1720
  if ( rc != CA_ITER_OK ) {
1706
- rb_raise(rb_eRuntimeError, "sort_addr_ki: input init failed rc=%d", rc);
1721
+ rb_raise(rb_eRuntimeError, "%s: input init failed rc=%d", ca_calling_method_name(), rc);
1707
1722
  }
1708
- rc = ca_iter_state_init_l2(&st_out, co, CA_SLAB_AXES,
1723
+ rc = ca_iter_state_init_l2_paired(&st_out, &st_in, co, CA_SLAB_AXES,
1709
1724
  slab_axes, 1, CA_KERNEL_WRITE);
1710
1725
  if ( rc != CA_ITER_OK ) {
1711
1726
  ca_iter_state_finish(&st_in);
1712
- rb_raise(rb_eRuntimeError, "sort_addr_ki: output init failed rc=%d", rc);
1727
+ rb_raise(rb_eRuntimeError, "%s: output init failed rc=%d", ca_calling_method_name(), rc);
1713
1728
  }
1714
1729
 
1715
1730
  ca_size_t fiber_n = st_in.slab_elements;
1716
- sort_addr_pair_i8 *buf =
1717
- xmalloc(sizeof(sort_addr_pair_i8) * (size_t) fiber_n);
1731
+ volatile VALUE hbuf = 0, haux = 0;
1732
+ sort_addr_pair_i8 *buf = ALLOCV_N(sort_addr_pair_i8, hbuf, fiber_n);
1718
1733
  sort_addr_pair_i8 *aux = NULL;
1719
1734
  if ( do_stable ) {
1720
- aux = xmalloc(sizeof(sort_addr_pair_i8) * (size_t) fiber_n);
1735
+ aux = ALLOCV_N(sort_addr_pair_i8, haux, fiber_n);
1721
1736
  }
1722
1737
 
1723
1738
  /* :view_flat semantics: kernel computes view-flat address
@@ -1820,8 +1835,8 @@ sort_addr_ki_native_i8 (VALUE self, CArray *ca, int axis, int do_stable, int mas
1820
1835
  cur_outer_idx[m] = 0;
1821
1836
  }
1822
1837
  }
1823
- xfree(buf);
1824
- if ( aux ) xfree(aux);
1838
+ ALLOCV_END(hbuf);
1839
+ ALLOCV_END(haux);
1825
1840
  ca_iter_state_finish(&st_in);
1826
1841
  ca_iter_state_finish(&st_out);
1827
1842
  return vout;
@@ -1850,21 +1865,21 @@ sort_addr_ki_native_u8 (VALUE self, CArray *ca, int axis, int do_stable, int mas
1850
1865
  rc = ca_iter_state_init_l2(&st_in, ca, CA_SLAB_AXES,
1851
1866
  slab_axes, 1, 0);
1852
1867
  if ( rc != CA_ITER_OK ) {
1853
- rb_raise(rb_eRuntimeError, "sort_addr_ki: input init failed rc=%d", rc);
1868
+ rb_raise(rb_eRuntimeError, "%s: input init failed rc=%d", ca_calling_method_name(), rc);
1854
1869
  }
1855
- rc = ca_iter_state_init_l2(&st_out, co, CA_SLAB_AXES,
1870
+ rc = ca_iter_state_init_l2_paired(&st_out, &st_in, co, CA_SLAB_AXES,
1856
1871
  slab_axes, 1, CA_KERNEL_WRITE);
1857
1872
  if ( rc != CA_ITER_OK ) {
1858
1873
  ca_iter_state_finish(&st_in);
1859
- rb_raise(rb_eRuntimeError, "sort_addr_ki: output init failed rc=%d", rc);
1874
+ rb_raise(rb_eRuntimeError, "%s: output init failed rc=%d", ca_calling_method_name(), rc);
1860
1875
  }
1861
1876
 
1862
1877
  ca_size_t fiber_n = st_in.slab_elements;
1863
- sort_addr_pair_u8 *buf =
1864
- xmalloc(sizeof(sort_addr_pair_u8) * (size_t) fiber_n);
1878
+ volatile VALUE hbuf = 0, haux = 0;
1879
+ sort_addr_pair_u8 *buf = ALLOCV_N(sort_addr_pair_u8, hbuf, fiber_n);
1865
1880
  sort_addr_pair_u8 *aux = NULL;
1866
1881
  if ( do_stable ) {
1867
- aux = xmalloc(sizeof(sort_addr_pair_u8) * (size_t) fiber_n);
1882
+ aux = ALLOCV_N(sort_addr_pair_u8, haux, fiber_n);
1868
1883
  }
1869
1884
 
1870
1885
  /* :view_flat semantics: kernel computes view-flat address
@@ -1967,8 +1982,8 @@ sort_addr_ki_native_u8 (VALUE self, CArray *ca, int axis, int do_stable, int mas
1967
1982
  cur_outer_idx[m] = 0;
1968
1983
  }
1969
1984
  }
1970
- xfree(buf);
1971
- if ( aux ) xfree(aux);
1985
+ ALLOCV_END(hbuf);
1986
+ ALLOCV_END(haux);
1972
1987
  ca_iter_state_finish(&st_in);
1973
1988
  ca_iter_state_finish(&st_out);
1974
1989
  return vout;
@@ -1997,21 +2012,21 @@ sort_addr_ki_native_i16 (VALUE self, CArray *ca, int axis, int do_stable, int ma
1997
2012
  rc = ca_iter_state_init_l2(&st_in, ca, CA_SLAB_AXES,
1998
2013
  slab_axes, 1, 0);
1999
2014
  if ( rc != CA_ITER_OK ) {
2000
- rb_raise(rb_eRuntimeError, "sort_addr_ki: input init failed rc=%d", rc);
2015
+ rb_raise(rb_eRuntimeError, "%s: input init failed rc=%d", ca_calling_method_name(), rc);
2001
2016
  }
2002
- rc = ca_iter_state_init_l2(&st_out, co, CA_SLAB_AXES,
2017
+ rc = ca_iter_state_init_l2_paired(&st_out, &st_in, co, CA_SLAB_AXES,
2003
2018
  slab_axes, 1, CA_KERNEL_WRITE);
2004
2019
  if ( rc != CA_ITER_OK ) {
2005
2020
  ca_iter_state_finish(&st_in);
2006
- rb_raise(rb_eRuntimeError, "sort_addr_ki: output init failed rc=%d", rc);
2021
+ rb_raise(rb_eRuntimeError, "%s: output init failed rc=%d", ca_calling_method_name(), rc);
2007
2022
  }
2008
2023
 
2009
2024
  ca_size_t fiber_n = st_in.slab_elements;
2010
- sort_addr_pair_i16 *buf =
2011
- xmalloc(sizeof(sort_addr_pair_i16) * (size_t) fiber_n);
2025
+ volatile VALUE hbuf = 0, haux = 0;
2026
+ sort_addr_pair_i16 *buf = ALLOCV_N(sort_addr_pair_i16, hbuf, fiber_n);
2012
2027
  sort_addr_pair_i16 *aux = NULL;
2013
2028
  if ( do_stable ) {
2014
- aux = xmalloc(sizeof(sort_addr_pair_i16) * (size_t) fiber_n);
2029
+ aux = ALLOCV_N(sort_addr_pair_i16, haux, fiber_n);
2015
2030
  }
2016
2031
 
2017
2032
  /* :view_flat semantics: kernel computes view-flat address
@@ -2114,8 +2129,8 @@ sort_addr_ki_native_i16 (VALUE self, CArray *ca, int axis, int do_stable, int ma
2114
2129
  cur_outer_idx[m] = 0;
2115
2130
  }
2116
2131
  }
2117
- xfree(buf);
2118
- if ( aux ) xfree(aux);
2132
+ ALLOCV_END(hbuf);
2133
+ ALLOCV_END(haux);
2119
2134
  ca_iter_state_finish(&st_in);
2120
2135
  ca_iter_state_finish(&st_out);
2121
2136
  return vout;
@@ -2144,21 +2159,21 @@ sort_addr_ki_native_u16 (VALUE self, CArray *ca, int axis, int do_stable, int ma
2144
2159
  rc = ca_iter_state_init_l2(&st_in, ca, CA_SLAB_AXES,
2145
2160
  slab_axes, 1, 0);
2146
2161
  if ( rc != CA_ITER_OK ) {
2147
- rb_raise(rb_eRuntimeError, "sort_addr_ki: input init failed rc=%d", rc);
2162
+ rb_raise(rb_eRuntimeError, "%s: input init failed rc=%d", ca_calling_method_name(), rc);
2148
2163
  }
2149
- rc = ca_iter_state_init_l2(&st_out, co, CA_SLAB_AXES,
2164
+ rc = ca_iter_state_init_l2_paired(&st_out, &st_in, co, CA_SLAB_AXES,
2150
2165
  slab_axes, 1, CA_KERNEL_WRITE);
2151
2166
  if ( rc != CA_ITER_OK ) {
2152
2167
  ca_iter_state_finish(&st_in);
2153
- rb_raise(rb_eRuntimeError, "sort_addr_ki: output init failed rc=%d", rc);
2168
+ rb_raise(rb_eRuntimeError, "%s: output init failed rc=%d", ca_calling_method_name(), rc);
2154
2169
  }
2155
2170
 
2156
2171
  ca_size_t fiber_n = st_in.slab_elements;
2157
- sort_addr_pair_u16 *buf =
2158
- xmalloc(sizeof(sort_addr_pair_u16) * (size_t) fiber_n);
2172
+ volatile VALUE hbuf = 0, haux = 0;
2173
+ sort_addr_pair_u16 *buf = ALLOCV_N(sort_addr_pair_u16, hbuf, fiber_n);
2159
2174
  sort_addr_pair_u16 *aux = NULL;
2160
2175
  if ( do_stable ) {
2161
- aux = xmalloc(sizeof(sort_addr_pair_u16) * (size_t) fiber_n);
2176
+ aux = ALLOCV_N(sort_addr_pair_u16, haux, fiber_n);
2162
2177
  }
2163
2178
 
2164
2179
  /* :view_flat semantics: kernel computes view-flat address
@@ -2261,8 +2276,8 @@ sort_addr_ki_native_u16 (VALUE self, CArray *ca, int axis, int do_stable, int ma
2261
2276
  cur_outer_idx[m] = 0;
2262
2277
  }
2263
2278
  }
2264
- xfree(buf);
2265
- if ( aux ) xfree(aux);
2279
+ ALLOCV_END(hbuf);
2280
+ ALLOCV_END(haux);
2266
2281
  ca_iter_state_finish(&st_in);
2267
2282
  ca_iter_state_finish(&st_out);
2268
2283
  return vout;
@@ -2291,21 +2306,21 @@ sort_addr_ki_native_i32 (VALUE self, CArray *ca, int axis, int do_stable, int ma
2291
2306
  rc = ca_iter_state_init_l2(&st_in, ca, CA_SLAB_AXES,
2292
2307
  slab_axes, 1, 0);
2293
2308
  if ( rc != CA_ITER_OK ) {
2294
- rb_raise(rb_eRuntimeError, "sort_addr_ki: input init failed rc=%d", rc);
2309
+ rb_raise(rb_eRuntimeError, "%s: input init failed rc=%d", ca_calling_method_name(), rc);
2295
2310
  }
2296
- rc = ca_iter_state_init_l2(&st_out, co, CA_SLAB_AXES,
2311
+ rc = ca_iter_state_init_l2_paired(&st_out, &st_in, co, CA_SLAB_AXES,
2297
2312
  slab_axes, 1, CA_KERNEL_WRITE);
2298
2313
  if ( rc != CA_ITER_OK ) {
2299
2314
  ca_iter_state_finish(&st_in);
2300
- rb_raise(rb_eRuntimeError, "sort_addr_ki: output init failed rc=%d", rc);
2315
+ rb_raise(rb_eRuntimeError, "%s: output init failed rc=%d", ca_calling_method_name(), rc);
2301
2316
  }
2302
2317
 
2303
2318
  ca_size_t fiber_n = st_in.slab_elements;
2304
- sort_addr_pair_i32 *buf =
2305
- xmalloc(sizeof(sort_addr_pair_i32) * (size_t) fiber_n);
2319
+ volatile VALUE hbuf = 0, haux = 0;
2320
+ sort_addr_pair_i32 *buf = ALLOCV_N(sort_addr_pair_i32, hbuf, fiber_n);
2306
2321
  sort_addr_pair_i32 *aux = NULL;
2307
2322
  if ( do_stable ) {
2308
- aux = xmalloc(sizeof(sort_addr_pair_i32) * (size_t) fiber_n);
2323
+ aux = ALLOCV_N(sort_addr_pair_i32, haux, fiber_n);
2309
2324
  }
2310
2325
 
2311
2326
  /* :view_flat semantics: kernel computes view-flat address
@@ -2408,8 +2423,8 @@ sort_addr_ki_native_i32 (VALUE self, CArray *ca, int axis, int do_stable, int ma
2408
2423
  cur_outer_idx[m] = 0;
2409
2424
  }
2410
2425
  }
2411
- xfree(buf);
2412
- if ( aux ) xfree(aux);
2426
+ ALLOCV_END(hbuf);
2427
+ ALLOCV_END(haux);
2413
2428
  ca_iter_state_finish(&st_in);
2414
2429
  ca_iter_state_finish(&st_out);
2415
2430
  return vout;
@@ -2438,21 +2453,21 @@ sort_addr_ki_native_u32 (VALUE self, CArray *ca, int axis, int do_stable, int ma
2438
2453
  rc = ca_iter_state_init_l2(&st_in, ca, CA_SLAB_AXES,
2439
2454
  slab_axes, 1, 0);
2440
2455
  if ( rc != CA_ITER_OK ) {
2441
- rb_raise(rb_eRuntimeError, "sort_addr_ki: input init failed rc=%d", rc);
2456
+ rb_raise(rb_eRuntimeError, "%s: input init failed rc=%d", ca_calling_method_name(), rc);
2442
2457
  }
2443
- rc = ca_iter_state_init_l2(&st_out, co, CA_SLAB_AXES,
2458
+ rc = ca_iter_state_init_l2_paired(&st_out, &st_in, co, CA_SLAB_AXES,
2444
2459
  slab_axes, 1, CA_KERNEL_WRITE);
2445
2460
  if ( rc != CA_ITER_OK ) {
2446
2461
  ca_iter_state_finish(&st_in);
2447
- rb_raise(rb_eRuntimeError, "sort_addr_ki: output init failed rc=%d", rc);
2462
+ rb_raise(rb_eRuntimeError, "%s: output init failed rc=%d", ca_calling_method_name(), rc);
2448
2463
  }
2449
2464
 
2450
2465
  ca_size_t fiber_n = st_in.slab_elements;
2451
- sort_addr_pair_u32 *buf =
2452
- xmalloc(sizeof(sort_addr_pair_u32) * (size_t) fiber_n);
2466
+ volatile VALUE hbuf = 0, haux = 0;
2467
+ sort_addr_pair_u32 *buf = ALLOCV_N(sort_addr_pair_u32, hbuf, fiber_n);
2453
2468
  sort_addr_pair_u32 *aux = NULL;
2454
2469
  if ( do_stable ) {
2455
- aux = xmalloc(sizeof(sort_addr_pair_u32) * (size_t) fiber_n);
2470
+ aux = ALLOCV_N(sort_addr_pair_u32, haux, fiber_n);
2456
2471
  }
2457
2472
 
2458
2473
  /* :view_flat semantics: kernel computes view-flat address
@@ -2555,8 +2570,8 @@ sort_addr_ki_native_u32 (VALUE self, CArray *ca, int axis, int do_stable, int ma
2555
2570
  cur_outer_idx[m] = 0;
2556
2571
  }
2557
2572
  }
2558
- xfree(buf);
2559
- if ( aux ) xfree(aux);
2573
+ ALLOCV_END(hbuf);
2574
+ ALLOCV_END(haux);
2560
2575
  ca_iter_state_finish(&st_in);
2561
2576
  ca_iter_state_finish(&st_out);
2562
2577
  return vout;
@@ -2585,21 +2600,21 @@ sort_addr_ki_native_i64 (VALUE self, CArray *ca, int axis, int do_stable, int ma
2585
2600
  rc = ca_iter_state_init_l2(&st_in, ca, CA_SLAB_AXES,
2586
2601
  slab_axes, 1, 0);
2587
2602
  if ( rc != CA_ITER_OK ) {
2588
- rb_raise(rb_eRuntimeError, "sort_addr_ki: input init failed rc=%d", rc);
2603
+ rb_raise(rb_eRuntimeError, "%s: input init failed rc=%d", ca_calling_method_name(), rc);
2589
2604
  }
2590
- rc = ca_iter_state_init_l2(&st_out, co, CA_SLAB_AXES,
2605
+ rc = ca_iter_state_init_l2_paired(&st_out, &st_in, co, CA_SLAB_AXES,
2591
2606
  slab_axes, 1, CA_KERNEL_WRITE);
2592
2607
  if ( rc != CA_ITER_OK ) {
2593
2608
  ca_iter_state_finish(&st_in);
2594
- rb_raise(rb_eRuntimeError, "sort_addr_ki: output init failed rc=%d", rc);
2609
+ rb_raise(rb_eRuntimeError, "%s: output init failed rc=%d", ca_calling_method_name(), rc);
2595
2610
  }
2596
2611
 
2597
2612
  ca_size_t fiber_n = st_in.slab_elements;
2598
- sort_addr_pair_i64 *buf =
2599
- xmalloc(sizeof(sort_addr_pair_i64) * (size_t) fiber_n);
2613
+ volatile VALUE hbuf = 0, haux = 0;
2614
+ sort_addr_pair_i64 *buf = ALLOCV_N(sort_addr_pair_i64, hbuf, fiber_n);
2600
2615
  sort_addr_pair_i64 *aux = NULL;
2601
2616
  if ( do_stable ) {
2602
- aux = xmalloc(sizeof(sort_addr_pair_i64) * (size_t) fiber_n);
2617
+ aux = ALLOCV_N(sort_addr_pair_i64, haux, fiber_n);
2603
2618
  }
2604
2619
 
2605
2620
  /* :view_flat semantics: kernel computes view-flat address
@@ -2702,8 +2717,8 @@ sort_addr_ki_native_i64 (VALUE self, CArray *ca, int axis, int do_stable, int ma
2702
2717
  cur_outer_idx[m] = 0;
2703
2718
  }
2704
2719
  }
2705
- xfree(buf);
2706
- if ( aux ) xfree(aux);
2720
+ ALLOCV_END(hbuf);
2721
+ ALLOCV_END(haux);
2707
2722
  ca_iter_state_finish(&st_in);
2708
2723
  ca_iter_state_finish(&st_out);
2709
2724
  return vout;
@@ -2732,21 +2747,21 @@ sort_addr_ki_native_u64 (VALUE self, CArray *ca, int axis, int do_stable, int ma
2732
2747
  rc = ca_iter_state_init_l2(&st_in, ca, CA_SLAB_AXES,
2733
2748
  slab_axes, 1, 0);
2734
2749
  if ( rc != CA_ITER_OK ) {
2735
- rb_raise(rb_eRuntimeError, "sort_addr_ki: input init failed rc=%d", rc);
2750
+ rb_raise(rb_eRuntimeError, "%s: input init failed rc=%d", ca_calling_method_name(), rc);
2736
2751
  }
2737
- rc = ca_iter_state_init_l2(&st_out, co, CA_SLAB_AXES,
2752
+ rc = ca_iter_state_init_l2_paired(&st_out, &st_in, co, CA_SLAB_AXES,
2738
2753
  slab_axes, 1, CA_KERNEL_WRITE);
2739
2754
  if ( rc != CA_ITER_OK ) {
2740
2755
  ca_iter_state_finish(&st_in);
2741
- rb_raise(rb_eRuntimeError, "sort_addr_ki: output init failed rc=%d", rc);
2756
+ rb_raise(rb_eRuntimeError, "%s: output init failed rc=%d", ca_calling_method_name(), rc);
2742
2757
  }
2743
2758
 
2744
2759
  ca_size_t fiber_n = st_in.slab_elements;
2745
- sort_addr_pair_u64 *buf =
2746
- xmalloc(sizeof(sort_addr_pair_u64) * (size_t) fiber_n);
2760
+ volatile VALUE hbuf = 0, haux = 0;
2761
+ sort_addr_pair_u64 *buf = ALLOCV_N(sort_addr_pair_u64, hbuf, fiber_n);
2747
2762
  sort_addr_pair_u64 *aux = NULL;
2748
2763
  if ( do_stable ) {
2749
- aux = xmalloc(sizeof(sort_addr_pair_u64) * (size_t) fiber_n);
2764
+ aux = ALLOCV_N(sort_addr_pair_u64, haux, fiber_n);
2750
2765
  }
2751
2766
 
2752
2767
  /* :view_flat semantics: kernel computes view-flat address
@@ -2849,8 +2864,8 @@ sort_addr_ki_native_u64 (VALUE self, CArray *ca, int axis, int do_stable, int ma
2849
2864
  cur_outer_idx[m] = 0;
2850
2865
  }
2851
2866
  }
2852
- xfree(buf);
2853
- if ( aux ) xfree(aux);
2867
+ ALLOCV_END(hbuf);
2868
+ ALLOCV_END(haux);
2854
2869
  ca_iter_state_finish(&st_in);
2855
2870
  ca_iter_state_finish(&st_out);
2856
2871
  return vout;
@@ -2879,21 +2894,21 @@ sort_addr_ki_native_f32 (VALUE self, CArray *ca, int axis, int do_stable, int ma
2879
2894
  rc = ca_iter_state_init_l2(&st_in, ca, CA_SLAB_AXES,
2880
2895
  slab_axes, 1, 0);
2881
2896
  if ( rc != CA_ITER_OK ) {
2882
- rb_raise(rb_eRuntimeError, "sort_addr_ki: input init failed rc=%d", rc);
2897
+ rb_raise(rb_eRuntimeError, "%s: input init failed rc=%d", ca_calling_method_name(), rc);
2883
2898
  }
2884
- rc = ca_iter_state_init_l2(&st_out, co, CA_SLAB_AXES,
2899
+ rc = ca_iter_state_init_l2_paired(&st_out, &st_in, co, CA_SLAB_AXES,
2885
2900
  slab_axes, 1, CA_KERNEL_WRITE);
2886
2901
  if ( rc != CA_ITER_OK ) {
2887
2902
  ca_iter_state_finish(&st_in);
2888
- rb_raise(rb_eRuntimeError, "sort_addr_ki: output init failed rc=%d", rc);
2903
+ rb_raise(rb_eRuntimeError, "%s: output init failed rc=%d", ca_calling_method_name(), rc);
2889
2904
  }
2890
2905
 
2891
2906
  ca_size_t fiber_n = st_in.slab_elements;
2892
- sort_addr_pair_f32 *buf =
2893
- xmalloc(sizeof(sort_addr_pair_f32) * (size_t) fiber_n);
2907
+ volatile VALUE hbuf = 0, haux = 0;
2908
+ sort_addr_pair_f32 *buf = ALLOCV_N(sort_addr_pair_f32, hbuf, fiber_n);
2894
2909
  sort_addr_pair_f32 *aux = NULL;
2895
2910
  if ( do_stable ) {
2896
- aux = xmalloc(sizeof(sort_addr_pair_f32) * (size_t) fiber_n);
2911
+ aux = ALLOCV_N(sort_addr_pair_f32, haux, fiber_n);
2897
2912
  }
2898
2913
 
2899
2914
  /* :view_flat semantics: kernel computes view-flat address
@@ -3000,8 +3015,8 @@ sort_addr_ki_native_f32 (VALUE self, CArray *ca, int axis, int do_stable, int ma
3000
3015
  cur_outer_idx[m] = 0;
3001
3016
  }
3002
3017
  }
3003
- xfree(buf);
3004
- if ( aux ) xfree(aux);
3018
+ ALLOCV_END(hbuf);
3019
+ ALLOCV_END(haux);
3005
3020
  ca_iter_state_finish(&st_in);
3006
3021
  ca_iter_state_finish(&st_out);
3007
3022
  return vout;
@@ -3030,21 +3045,21 @@ sort_addr_ki_native_f64 (VALUE self, CArray *ca, int axis, int do_stable, int ma
3030
3045
  rc = ca_iter_state_init_l2(&st_in, ca, CA_SLAB_AXES,
3031
3046
  slab_axes, 1, 0);
3032
3047
  if ( rc != CA_ITER_OK ) {
3033
- rb_raise(rb_eRuntimeError, "sort_addr_ki: input init failed rc=%d", rc);
3048
+ rb_raise(rb_eRuntimeError, "%s: input init failed rc=%d", ca_calling_method_name(), rc);
3034
3049
  }
3035
- rc = ca_iter_state_init_l2(&st_out, co, CA_SLAB_AXES,
3050
+ rc = ca_iter_state_init_l2_paired(&st_out, &st_in, co, CA_SLAB_AXES,
3036
3051
  slab_axes, 1, CA_KERNEL_WRITE);
3037
3052
  if ( rc != CA_ITER_OK ) {
3038
3053
  ca_iter_state_finish(&st_in);
3039
- rb_raise(rb_eRuntimeError, "sort_addr_ki: output init failed rc=%d", rc);
3054
+ rb_raise(rb_eRuntimeError, "%s: output init failed rc=%d", ca_calling_method_name(), rc);
3040
3055
  }
3041
3056
 
3042
3057
  ca_size_t fiber_n = st_in.slab_elements;
3043
- sort_addr_pair_f64 *buf =
3044
- xmalloc(sizeof(sort_addr_pair_f64) * (size_t) fiber_n);
3058
+ volatile VALUE hbuf = 0, haux = 0;
3059
+ sort_addr_pair_f64 *buf = ALLOCV_N(sort_addr_pair_f64, hbuf, fiber_n);
3045
3060
  sort_addr_pair_f64 *aux = NULL;
3046
3061
  if ( do_stable ) {
3047
- aux = xmalloc(sizeof(sort_addr_pair_f64) * (size_t) fiber_n);
3062
+ aux = ALLOCV_N(sort_addr_pair_f64, haux, fiber_n);
3048
3063
  }
3049
3064
 
3050
3065
  /* :view_flat semantics: kernel computes view-flat address
@@ -3151,8 +3166,8 @@ sort_addr_ki_native_f64 (VALUE self, CArray *ca, int axis, int do_stable, int ma
3151
3166
  cur_outer_idx[m] = 0;
3152
3167
  }
3153
3168
  }
3154
- xfree(buf);
3155
- if ( aux ) xfree(aux);
3169
+ ALLOCV_END(hbuf);
3170
+ ALLOCV_END(haux);
3156
3171
  ca_iter_state_finish(&st_in);
3157
3172
  ca_iter_state_finish(&st_out);
3158
3173
  return vout;
@@ -3181,21 +3196,21 @@ sort_addr_ki_native_bool (VALUE self, CArray *ca, int axis, int do_stable, int m
3181
3196
  rc = ca_iter_state_init_l2(&st_in, ca, CA_SLAB_AXES,
3182
3197
  slab_axes, 1, 0);
3183
3198
  if ( rc != CA_ITER_OK ) {
3184
- rb_raise(rb_eRuntimeError, "sort_addr_ki: input init failed rc=%d", rc);
3199
+ rb_raise(rb_eRuntimeError, "%s: input init failed rc=%d", ca_calling_method_name(), rc);
3185
3200
  }
3186
- rc = ca_iter_state_init_l2(&st_out, co, CA_SLAB_AXES,
3201
+ rc = ca_iter_state_init_l2_paired(&st_out, &st_in, co, CA_SLAB_AXES,
3187
3202
  slab_axes, 1, CA_KERNEL_WRITE);
3188
3203
  if ( rc != CA_ITER_OK ) {
3189
3204
  ca_iter_state_finish(&st_in);
3190
- rb_raise(rb_eRuntimeError, "sort_addr_ki: output init failed rc=%d", rc);
3205
+ rb_raise(rb_eRuntimeError, "%s: output init failed rc=%d", ca_calling_method_name(), rc);
3191
3206
  }
3192
3207
 
3193
3208
  ca_size_t fiber_n = st_in.slab_elements;
3194
- sort_addr_pair_bool *buf =
3195
- xmalloc(sizeof(sort_addr_pair_bool) * (size_t) fiber_n);
3209
+ volatile VALUE hbuf = 0, haux = 0;
3210
+ sort_addr_pair_bool *buf = ALLOCV_N(sort_addr_pair_bool, hbuf, fiber_n);
3196
3211
  sort_addr_pair_bool *aux = NULL;
3197
3212
  if ( do_stable ) {
3198
- aux = xmalloc(sizeof(sort_addr_pair_bool) * (size_t) fiber_n);
3213
+ aux = ALLOCV_N(sort_addr_pair_bool, haux, fiber_n);
3199
3214
  }
3200
3215
 
3201
3216
  /* :view_flat semantics: kernel computes view-flat address
@@ -3298,8 +3313,8 @@ sort_addr_ki_native_bool (VALUE self, CArray *ca, int axis, int do_stable, int m
3298
3313
  cur_outer_idx[m] = 0;
3299
3314
  }
3300
3315
  }
3301
- xfree(buf);
3302
- if ( aux ) xfree(aux);
3316
+ ALLOCV_END(hbuf);
3317
+ ALLOCV_END(haux);
3303
3318
  ca_iter_state_finish(&st_in);
3304
3319
  ca_iter_state_finish(&st_out);
3305
3320
  return vout;
@@ -3315,23 +3330,42 @@ sort_addr_cmp_object (const void *a, const void *b)
3315
3330
  {
3316
3331
  const sort_addr_pair_object *pa = (const sort_addr_pair_object *) a;
3317
3332
  const sort_addr_pair_object *pb = (const sort_addr_pair_object *) b;
3318
- VALUE r = rb_funcall(pa->v, rb_intern("<=>"), 1, pb->v);
3319
- if ( NIL_P(r) ) {
3320
- rb_raise(rb_eArgError, "sort_addr_ki: comparison of %s with %s failed",
3321
- rb_obj_classname(pa->v), rb_obj_classname(pb->v));
3322
- }
3323
- int c = NUM2INT(r);
3324
- if ( c > 0 ) return 1;
3325
- if ( c < 0 ) return -1;
3333
+ int c = ca_obj_order_cmp(pa->v, pb->v);
3334
+ if ( c != 0 ) return c;
3326
3335
  /* Stable tie-break by original fiber-local index. */
3327
3336
  if ( pa->i < pb->i ) return -1;
3328
3337
  if ( pa->i > pb->i ) return 1;
3329
3338
  return 0;
3330
3339
  }
3331
3340
 
3341
+ /* The object lane calls Ruby for every cell, so a walk can raise part
3342
+ way. Its states live in the caller's frame, and ca_iter_ensure
3343
+ finishes them however the body leaves. */
3344
+ typedef struct {
3345
+ ca_iter_state states[2];
3346
+ VALUE self;
3347
+ CArray *ca;
3348
+ int axis;
3349
+ int do_stable;
3350
+ int masked_last;
3351
+ } sort_addr_ki_native_object_frame;
3352
+
3332
3353
  static VALUE
3333
- sort_addr_ki_native_object (VALUE self, CArray *ca, int axis, int do_stable, int masked_last)
3334
- {
3354
+ sort_addr_ki_native_object_body (VALUE arg)
3355
+ {
3356
+ sort_addr_ki_native_object_frame *fr = (sort_addr_ki_native_object_frame *) arg;
3357
+ VALUE self = fr->self;
3358
+ (void) self;
3359
+ CArray *ca = fr->ca;
3360
+ (void) ca;
3361
+ int axis = fr->axis;
3362
+ (void) axis;
3363
+ int do_stable = fr->do_stable;
3364
+ (void) do_stable;
3365
+ int masked_last = fr->masked_last;
3366
+ (void) masked_last;
3367
+ #define st_in (fr->states[0])
3368
+ #define st_out (fr->states[1])
3335
3369
  VALUE vout = rb_ca_template_with_type(self, INT2NUM(CA_SIZE),
3336
3370
  INT2NUM(0));
3337
3371
  CArray *co;
@@ -3342,27 +3376,23 @@ sort_addr_ki_native_object (VALUE self, CArray *ca, int axis, int do_stable, int
3342
3376
  positions align between iterators. */
3343
3377
  int8_t slab_axes[CA_RANK_MAX];
3344
3378
  slab_axes[0] = (int8_t) axis;
3345
- ca_iter_state st_in, st_out;
3346
3379
  int rc;
3347
3380
  rc = ca_iter_state_init_l2(&st_in, ca, CA_SLAB_AXES,
3348
3381
  slab_axes, 1, 0);
3349
3382
  if ( rc != CA_ITER_OK ) {
3350
- rb_raise(rb_eRuntimeError, "sort_addr_ki: input init failed rc=%d", rc);
3383
+ rb_raise(rb_eRuntimeError, "%s: input init failed rc=%d", ca_calling_method_name(), rc);
3351
3384
  }
3352
- rc = ca_iter_state_init_l2(&st_out, co, CA_SLAB_AXES,
3385
+ rc = ca_iter_state_init_l2_paired(&st_out, &st_in, co, CA_SLAB_AXES,
3353
3386
  slab_axes, 1, CA_KERNEL_WRITE);
3354
3387
  if ( rc != CA_ITER_OK ) {
3355
3388
  ca_iter_state_finish(&st_in);
3356
- rb_raise(rb_eRuntimeError, "sort_addr_ki: output init failed rc=%d", rc);
3389
+ rb_raise(rb_eRuntimeError, "%s: output init failed rc=%d", ca_calling_method_name(), rc);
3357
3390
  }
3358
3391
 
3359
3392
  ca_size_t fiber_n = st_in.slab_elements;
3360
- sort_addr_pair_object *buf =
3361
- xmalloc(sizeof(sort_addr_pair_object) * (size_t) fiber_n);
3362
- sort_addr_pair_object *aux = NULL;
3363
- if ( do_stable ) {
3364
- aux = xmalloc(sizeof(sort_addr_pair_object) * (size_t) fiber_n);
3365
- }
3393
+ volatile VALUE hbuf = 0, haux = 0;
3394
+ sort_addr_pair_object *buf = ALLOCV_N(sort_addr_pair_object, hbuf, fiber_n);
3395
+ sort_addr_pair_object *aux = ALLOCV_N(sort_addr_pair_object, haux, fiber_n);
3366
3396
 
3367
3397
  /* :view_flat semantics: kernel computes view-flat address
3368
3398
  (= row-major position in ca's shape, 0..elements-1) for
@@ -3448,8 +3478,8 @@ sort_addr_ki_native_object (VALUE self, CArray *ca, int axis, int do_stable, int
3448
3478
  selects introsort with mergesort escape (= AC1 1.88x payoff).
3449
3479
  Only the unmasked sub-range [sort_lo, sort_lo+sort_n) is
3450
3480
  sorted -- see emit_sort_mask_split_load in mkkernel.rb. */
3451
- (void) do_stable; /* CA_OBJECT: qsort handles both stable / quick */
3452
- qsort((buf + sort_lo), (size_t) sort_n, sizeof(sort_addr_pair_object), sort_addr_cmp_object);
3481
+ (void) do_stable; /* CA_OBJECT: one stable sort serves both kinds */
3482
+ ca_sort_merge_pair_object((ca_pair_object *) (buf + sort_lo), (ca_pair_object *) aux, (ca_size_t) sort_n, sort_addr_cmp_object);
3453
3483
  /* Write sorted view-flat addresses to output fiber. */
3454
3484
  for ( ca_size_t k = 0; k < fiber_n; k++ ) {
3455
3485
  *(ca_size_t *) (po + k * ostride) = (ca_size_t) buf[k].i;
@@ -3461,13 +3491,28 @@ sort_addr_ki_native_object (VALUE self, CArray *ca, int axis, int do_stable, int
3461
3491
  cur_outer_idx[m] = 0;
3462
3492
  }
3463
3493
  }
3464
- xfree(buf);
3465
- if ( aux ) xfree(aux);
3494
+ ALLOCV_END(hbuf);
3495
+ ALLOCV_END(haux);
3466
3496
  ca_iter_state_finish(&st_in);
3467
3497
  ca_iter_state_finish(&st_out);
3468
3498
  return vout;
3499
+ #undef st_in
3500
+ #undef st_out
3469
3501
  }
3470
3502
 
3503
+ static VALUE
3504
+ sort_addr_ki_native_object (VALUE self, CArray *ca, int axis, int do_stable, int masked_last)
3505
+ {
3506
+ sort_addr_ki_native_object_frame fr;
3507
+ fr.self = self;
3508
+ fr.ca = ca;
3509
+ fr.axis = axis;
3510
+ fr.do_stable = do_stable;
3511
+ fr.masked_last = masked_last;
3512
+ return ca_iter_ensure(2, fr.states, sort_addr_ki_native_object_body, (VALUE) &fr);
3513
+ }
3514
+
3515
+
3471
3516
  typedef struct {
3472
3517
  char *vp; /* pointer to the fiber cell (nb bytes wide) */
3473
3518
  ca_size_t nb; /* element byte width (= ca->bytes) */
@@ -3505,21 +3550,21 @@ sort_addr_ki_native_fixlen (VALUE self, CArray *ca, int axis, int do_stable, int
3505
3550
  rc = ca_iter_state_init_l2(&st_in, ca, CA_SLAB_AXES,
3506
3551
  slab_axes, 1, 0);
3507
3552
  if ( rc != CA_ITER_OK ) {
3508
- rb_raise(rb_eRuntimeError, "sort_addr_ki: input init failed rc=%d", rc);
3553
+ rb_raise(rb_eRuntimeError, "%s: input init failed rc=%d", ca_calling_method_name(), rc);
3509
3554
  }
3510
- rc = ca_iter_state_init_l2(&st_out, co, CA_SLAB_AXES,
3555
+ rc = ca_iter_state_init_l2_paired(&st_out, &st_in, co, CA_SLAB_AXES,
3511
3556
  slab_axes, 1, CA_KERNEL_WRITE);
3512
3557
  if ( rc != CA_ITER_OK ) {
3513
3558
  ca_iter_state_finish(&st_in);
3514
- rb_raise(rb_eRuntimeError, "sort_addr_ki: output init failed rc=%d", rc);
3559
+ rb_raise(rb_eRuntimeError, "%s: output init failed rc=%d", ca_calling_method_name(), rc);
3515
3560
  }
3516
3561
 
3517
3562
  ca_size_t fiber_n = st_in.slab_elements;
3518
- sort_addr_pair_fixlen *buf =
3519
- xmalloc(sizeof(sort_addr_pair_fixlen) * (size_t) fiber_n);
3563
+ volatile VALUE hbuf = 0, haux = 0;
3564
+ sort_addr_pair_fixlen *buf = ALLOCV_N(sort_addr_pair_fixlen, hbuf, fiber_n);
3520
3565
  sort_addr_pair_fixlen *aux = NULL;
3521
3566
  if ( do_stable ) {
3522
- aux = xmalloc(sizeof(sort_addr_pair_fixlen) * (size_t) fiber_n);
3567
+ aux = ALLOCV_N(sort_addr_pair_fixlen, haux, fiber_n);
3523
3568
  }
3524
3569
 
3525
3570
  /* :view_flat semantics: kernel computes view-flat address
@@ -3623,8 +3668,8 @@ sort_addr_ki_native_fixlen (VALUE self, CArray *ca, int axis, int do_stable, int
3623
3668
  cur_outer_idx[m] = 0;
3624
3669
  }
3625
3670
  }
3626
- xfree(buf);
3627
- if ( aux ) xfree(aux);
3671
+ ALLOCV_END(hbuf);
3672
+ ALLOCV_END(haux);
3628
3673
  ca_iter_state_finish(&st_in);
3629
3674
  ca_iter_state_finish(&st_out);
3630
3675
  return vout;
@@ -3635,68 +3680,43 @@ sort_addr_ki_dispatch (VALUE self, VALUE vaxis, int do_stable, int masked_last)
3635
3680
  {
3636
3681
  CArray *src;
3637
3682
  GetCArray(self, src);
3638
- /* Face gate: descend to storage (fixlen -> memcmp default,
3639
- numeric -> ORDERABLE required). The strip also makes the
3640
- switch below and every index/addr template output build on
3641
- the plain storage, not a face-lifted (NULL-ptr) view. */
3683
+ if ( ca_is_lazy_view(src) ) {
3684
+ volatile VALUE evaluated = ca_lazy_evaluated(self);
3685
+ if ( ! NIL_P(evaluated) ) {
3686
+ self = evaluated;
3687
+ GetCArray(self, src);
3688
+ }
3689
+ }
3690
+ /* Face gate: run on the array the Face's order is read from.
3691
+ It is never a Face, so the switch below and every index/addr
3692
+ template output build on a plain array, not a face-lifted
3693
+ (NULL-ptr) view. */
3642
3694
  if ( ca_is_face(src) ) {
3643
- VALUE _face = self;
3644
- int _orderable = ca_test_flag(src, CA_FLAG_FACE_ORDERABLE_STORAGE);
3645
- VALUE _st = rb_ca_strip_face_value(self);
3646
- CArray *_sc;
3647
- GetCArray(_st, _sc);
3648
- if ( _sc->data_type != CA_FIXLEN && ! _orderable ) {
3649
- rb_raise(rb_eArgError,
3650
- "sort_addr_ki: Face-typed input (%s) is not orderable "
3651
- "by storage; use ca.parent to descend to storage",
3652
- rb_obj_classname(_face));
3653
- }
3654
- self = _st;
3655
- src = _sc;
3656
- }
3657
- int axis = NUM2INT(vaxis);
3658
- if ( axis < 0 ) axis += src->ndim;
3659
- if ( axis < 0 || axis >= src->ndim ) {
3660
- rb_raise(rb_eArgError, "sort_addr_ki: axis %d out of range for ndim %d", NUM2INT(vaxis), src->ndim);
3695
+ self = ca_order_key(self, ca_calling_method_name());
3696
+ GetCArray(self, src);
3661
3697
  }
3698
+ int axis = ca_normalize_axis_arg(vaxis, src->ndim, NULL);
3662
3699
  switch ( src->data_type ) {
3663
- case CA_INT8: return sort_addr_ki_native_i8(self, src, axis, do_stable, masked_last);
3664
- case CA_UINT8: return sort_addr_ki_native_u8(self, src, axis, do_stable, masked_last);
3665
- case CA_INT16: return sort_addr_ki_native_i16(self, src, axis, do_stable, masked_last);
3666
- case CA_UINT16: return sort_addr_ki_native_u16(self, src, axis, do_stable, masked_last);
3667
- case CA_INT32: return sort_addr_ki_native_i32(self, src, axis, do_stable, masked_last);
3668
- case CA_UINT32: return sort_addr_ki_native_u32(self, src, axis, do_stable, masked_last);
3669
- case CA_INT64: return sort_addr_ki_native_i64(self, src, axis, do_stable, masked_last);
3670
- case CA_UINT64: return sort_addr_ki_native_u64(self, src, axis, do_stable, masked_last);
3671
- case CA_FLOAT32: return sort_addr_ki_native_f32(self, src, axis, do_stable, masked_last);
3672
- case CA_FLOAT64: return sort_addr_ki_native_f64(self, src, axis, do_stable, masked_last);
3673
- case CA_BOOLEAN: return sort_addr_ki_native_bool(self, src, axis, do_stable, masked_last);
3674
- case CA_OBJECT: return sort_addr_ki_native_object(self, src, axis, do_stable, masked_last);
3675
- case CA_FIXLEN: return sort_addr_ki_native_fixlen(self, src, axis, do_stable, masked_last);
3700
+ case CA_INT8: CA_RETURN_KEEPING(self, sort_addr_ki_native_i8(self, src, axis, do_stable, masked_last));
3701
+ case CA_UINT8: CA_RETURN_KEEPING(self, sort_addr_ki_native_u8(self, src, axis, do_stable, masked_last));
3702
+ case CA_INT16: CA_RETURN_KEEPING(self, sort_addr_ki_native_i16(self, src, axis, do_stable, masked_last));
3703
+ case CA_UINT16: CA_RETURN_KEEPING(self, sort_addr_ki_native_u16(self, src, axis, do_stable, masked_last));
3704
+ case CA_INT32: CA_RETURN_KEEPING(self, sort_addr_ki_native_i32(self, src, axis, do_stable, masked_last));
3705
+ case CA_UINT32: CA_RETURN_KEEPING(self, sort_addr_ki_native_u32(self, src, axis, do_stable, masked_last));
3706
+ case CA_INT64: CA_RETURN_KEEPING(self, sort_addr_ki_native_i64(self, src, axis, do_stable, masked_last));
3707
+ case CA_UINT64: CA_RETURN_KEEPING(self, sort_addr_ki_native_u64(self, src, axis, do_stable, masked_last));
3708
+ case CA_FLOAT32: CA_RETURN_KEEPING(self, sort_addr_ki_native_f32(self, src, axis, do_stable, masked_last));
3709
+ case CA_FLOAT64: CA_RETURN_KEEPING(self, sort_addr_ki_native_f64(self, src, axis, do_stable, masked_last));
3710
+ case CA_BOOLEAN: CA_RETURN_KEEPING(self, sort_addr_ki_native_bool(self, src, axis, do_stable, masked_last));
3711
+ case CA_OBJECT: CA_RETURN_KEEPING(self, sort_addr_ki_native_object(self, src, axis, do_stable, masked_last));
3712
+ case CA_FIXLEN: CA_RETURN_KEEPING(self, sort_addr_ki_native_fixlen(self, src, axis, do_stable, masked_last));
3676
3713
  default:
3677
- rb_raise(rb_eCADataTypeError, "sort_addr_ki: source data_type :%s not supported (expected one of: i8, u8, i16, u16, i32, u32, i64, u64, f32, f64, bool, object, fixlen)", ca_type_name[src->data_type]);
3714
+ ca_face_refuse_numeric(self, ca_calling_method_name(), rb_eCADataTypeError);
3715
+ rb_raise(rb_eCADataTypeError, "%s: source data_type :%s not supported (expected one of: int8, uint8, int16, uint16, int32, uint32, int64, uint64, float32, float64, boolean, object, fixlen)", ca_calling_method_name(), ca_type_name[src->data_type]);
3678
3716
  }
3679
3717
  return Qnil; /* unreachable */
3680
3718
  }
3681
3719
 
3682
- VALUE
3683
- rb_ca_sort_addr_ki_quick (VALUE self, VALUE vaxis)
3684
- {
3685
- return sort_addr_ki_dispatch(self, vaxis, 0, 1);
3686
- }
3687
-
3688
- VALUE
3689
- rb_ca_sort_addr_ki_stable (VALUE self, VALUE vaxis)
3690
- {
3691
- return sort_addr_ki_dispatch(self, vaxis, 1, 1);
3692
- }
3693
-
3694
- VALUE
3695
- rb_ca_sort_addr_ki (VALUE self, VALUE vaxis)
3696
- {
3697
- return rb_ca_sort_addr_ki_quick(self, vaxis);
3698
- }
3699
-
3700
3720
  VALUE
3701
3721
  rb_ca_sort_addr_ki_quick_mp (VALUE self, VALUE vaxis, int masked_last)
3702
3722
  {
@@ -3802,18 +3822,19 @@ partition_index_ki_native_i8 (VALUE self, CArray *ca, int axis, ca_size_t kth, i
3802
3822
  rc = ca_iter_state_init_l2(&st_in, ca, CA_SLAB_AXES,
3803
3823
  slab_axes, 1, 0);
3804
3824
  if ( rc != CA_ITER_OK ) {
3805
- rb_raise(rb_eRuntimeError, "partition_index_ki: input init failed rc=%d", rc);
3825
+ rb_raise(rb_eRuntimeError, "%s: input init failed rc=%d", ca_calling_method_name(), rc);
3806
3826
  }
3807
- rc = ca_iter_state_init_l2(&st_out, co, CA_SLAB_AXES,
3827
+ rc = ca_iter_state_init_l2_paired(&st_out, &st_in, co, CA_SLAB_AXES,
3808
3828
  slab_axes, 1, CA_KERNEL_WRITE);
3809
3829
  if ( rc != CA_ITER_OK ) {
3810
3830
  ca_iter_state_finish(&st_in);
3811
- rb_raise(rb_eRuntimeError, "partition_index_ki: output init failed rc=%d", rc);
3831
+ rb_raise(rb_eRuntimeError, "%s: output init failed rc=%d", ca_calling_method_name(), rc);
3812
3832
  }
3813
3833
 
3814
3834
  ca_size_t fiber_n = st_in.slab_elements;
3815
- partition_index_pair_i8 *buf =
3816
- xmalloc(sizeof(partition_index_pair_i8) * (size_t) fiber_n);
3835
+ volatile VALUE hbuf = 0, haux = 0;
3836
+ partition_index_pair_i8 *buf = ALLOCV_N(partition_index_pair_i8, hbuf, fiber_n);
3837
+ (void) haux;
3817
3838
 
3818
3839
  char *pi, *po;
3819
3840
  boolean8_t *mi, *mo;
@@ -3882,7 +3903,8 @@ partition_index_ki_native_i8 (VALUE self, CArray *ca, int axis, ca_size_t kth, i
3882
3903
  }
3883
3904
  ca_iter_state_sync_slab(&st_out);
3884
3905
  }
3885
- xfree(buf);
3906
+ ALLOCV_END(hbuf);
3907
+ ALLOCV_END(haux);
3886
3908
  ca_iter_state_finish(&st_in);
3887
3909
  ca_iter_state_finish(&st_out);
3888
3910
  return vout;
@@ -3979,18 +4001,19 @@ partition_index_ki_native_u8 (VALUE self, CArray *ca, int axis, ca_size_t kth, i
3979
4001
  rc = ca_iter_state_init_l2(&st_in, ca, CA_SLAB_AXES,
3980
4002
  slab_axes, 1, 0);
3981
4003
  if ( rc != CA_ITER_OK ) {
3982
- rb_raise(rb_eRuntimeError, "partition_index_ki: input init failed rc=%d", rc);
4004
+ rb_raise(rb_eRuntimeError, "%s: input init failed rc=%d", ca_calling_method_name(), rc);
3983
4005
  }
3984
- rc = ca_iter_state_init_l2(&st_out, co, CA_SLAB_AXES,
4006
+ rc = ca_iter_state_init_l2_paired(&st_out, &st_in, co, CA_SLAB_AXES,
3985
4007
  slab_axes, 1, CA_KERNEL_WRITE);
3986
4008
  if ( rc != CA_ITER_OK ) {
3987
4009
  ca_iter_state_finish(&st_in);
3988
- rb_raise(rb_eRuntimeError, "partition_index_ki: output init failed rc=%d", rc);
4010
+ rb_raise(rb_eRuntimeError, "%s: output init failed rc=%d", ca_calling_method_name(), rc);
3989
4011
  }
3990
4012
 
3991
4013
  ca_size_t fiber_n = st_in.slab_elements;
3992
- partition_index_pair_u8 *buf =
3993
- xmalloc(sizeof(partition_index_pair_u8) * (size_t) fiber_n);
4014
+ volatile VALUE hbuf = 0, haux = 0;
4015
+ partition_index_pair_u8 *buf = ALLOCV_N(partition_index_pair_u8, hbuf, fiber_n);
4016
+ (void) haux;
3994
4017
 
3995
4018
  char *pi, *po;
3996
4019
  boolean8_t *mi, *mo;
@@ -4059,7 +4082,8 @@ partition_index_ki_native_u8 (VALUE self, CArray *ca, int axis, ca_size_t kth, i
4059
4082
  }
4060
4083
  ca_iter_state_sync_slab(&st_out);
4061
4084
  }
4062
- xfree(buf);
4085
+ ALLOCV_END(hbuf);
4086
+ ALLOCV_END(haux);
4063
4087
  ca_iter_state_finish(&st_in);
4064
4088
  ca_iter_state_finish(&st_out);
4065
4089
  return vout;
@@ -4156,18 +4180,19 @@ partition_index_ki_native_i16 (VALUE self, CArray *ca, int axis, ca_size_t kth,
4156
4180
  rc = ca_iter_state_init_l2(&st_in, ca, CA_SLAB_AXES,
4157
4181
  slab_axes, 1, 0);
4158
4182
  if ( rc != CA_ITER_OK ) {
4159
- rb_raise(rb_eRuntimeError, "partition_index_ki: input init failed rc=%d", rc);
4183
+ rb_raise(rb_eRuntimeError, "%s: input init failed rc=%d", ca_calling_method_name(), rc);
4160
4184
  }
4161
- rc = ca_iter_state_init_l2(&st_out, co, CA_SLAB_AXES,
4185
+ rc = ca_iter_state_init_l2_paired(&st_out, &st_in, co, CA_SLAB_AXES,
4162
4186
  slab_axes, 1, CA_KERNEL_WRITE);
4163
4187
  if ( rc != CA_ITER_OK ) {
4164
4188
  ca_iter_state_finish(&st_in);
4165
- rb_raise(rb_eRuntimeError, "partition_index_ki: output init failed rc=%d", rc);
4189
+ rb_raise(rb_eRuntimeError, "%s: output init failed rc=%d", ca_calling_method_name(), rc);
4166
4190
  }
4167
4191
 
4168
4192
  ca_size_t fiber_n = st_in.slab_elements;
4169
- partition_index_pair_i16 *buf =
4170
- xmalloc(sizeof(partition_index_pair_i16) * (size_t) fiber_n);
4193
+ volatile VALUE hbuf = 0, haux = 0;
4194
+ partition_index_pair_i16 *buf = ALLOCV_N(partition_index_pair_i16, hbuf, fiber_n);
4195
+ (void) haux;
4171
4196
 
4172
4197
  char *pi, *po;
4173
4198
  boolean8_t *mi, *mo;
@@ -4236,7 +4261,8 @@ partition_index_ki_native_i16 (VALUE self, CArray *ca, int axis, ca_size_t kth,
4236
4261
  }
4237
4262
  ca_iter_state_sync_slab(&st_out);
4238
4263
  }
4239
- xfree(buf);
4264
+ ALLOCV_END(hbuf);
4265
+ ALLOCV_END(haux);
4240
4266
  ca_iter_state_finish(&st_in);
4241
4267
  ca_iter_state_finish(&st_out);
4242
4268
  return vout;
@@ -4333,18 +4359,19 @@ partition_index_ki_native_u16 (VALUE self, CArray *ca, int axis, ca_size_t kth,
4333
4359
  rc = ca_iter_state_init_l2(&st_in, ca, CA_SLAB_AXES,
4334
4360
  slab_axes, 1, 0);
4335
4361
  if ( rc != CA_ITER_OK ) {
4336
- rb_raise(rb_eRuntimeError, "partition_index_ki: input init failed rc=%d", rc);
4362
+ rb_raise(rb_eRuntimeError, "%s: input init failed rc=%d", ca_calling_method_name(), rc);
4337
4363
  }
4338
- rc = ca_iter_state_init_l2(&st_out, co, CA_SLAB_AXES,
4364
+ rc = ca_iter_state_init_l2_paired(&st_out, &st_in, co, CA_SLAB_AXES,
4339
4365
  slab_axes, 1, CA_KERNEL_WRITE);
4340
4366
  if ( rc != CA_ITER_OK ) {
4341
4367
  ca_iter_state_finish(&st_in);
4342
- rb_raise(rb_eRuntimeError, "partition_index_ki: output init failed rc=%d", rc);
4368
+ rb_raise(rb_eRuntimeError, "%s: output init failed rc=%d", ca_calling_method_name(), rc);
4343
4369
  }
4344
4370
 
4345
4371
  ca_size_t fiber_n = st_in.slab_elements;
4346
- partition_index_pair_u16 *buf =
4347
- xmalloc(sizeof(partition_index_pair_u16) * (size_t) fiber_n);
4372
+ volatile VALUE hbuf = 0, haux = 0;
4373
+ partition_index_pair_u16 *buf = ALLOCV_N(partition_index_pair_u16, hbuf, fiber_n);
4374
+ (void) haux;
4348
4375
 
4349
4376
  char *pi, *po;
4350
4377
  boolean8_t *mi, *mo;
@@ -4413,7 +4440,8 @@ partition_index_ki_native_u16 (VALUE self, CArray *ca, int axis, ca_size_t kth,
4413
4440
  }
4414
4441
  ca_iter_state_sync_slab(&st_out);
4415
4442
  }
4416
- xfree(buf);
4443
+ ALLOCV_END(hbuf);
4444
+ ALLOCV_END(haux);
4417
4445
  ca_iter_state_finish(&st_in);
4418
4446
  ca_iter_state_finish(&st_out);
4419
4447
  return vout;
@@ -4510,18 +4538,19 @@ partition_index_ki_native_i32 (VALUE self, CArray *ca, int axis, ca_size_t kth,
4510
4538
  rc = ca_iter_state_init_l2(&st_in, ca, CA_SLAB_AXES,
4511
4539
  slab_axes, 1, 0);
4512
4540
  if ( rc != CA_ITER_OK ) {
4513
- rb_raise(rb_eRuntimeError, "partition_index_ki: input init failed rc=%d", rc);
4541
+ rb_raise(rb_eRuntimeError, "%s: input init failed rc=%d", ca_calling_method_name(), rc);
4514
4542
  }
4515
- rc = ca_iter_state_init_l2(&st_out, co, CA_SLAB_AXES,
4543
+ rc = ca_iter_state_init_l2_paired(&st_out, &st_in, co, CA_SLAB_AXES,
4516
4544
  slab_axes, 1, CA_KERNEL_WRITE);
4517
4545
  if ( rc != CA_ITER_OK ) {
4518
4546
  ca_iter_state_finish(&st_in);
4519
- rb_raise(rb_eRuntimeError, "partition_index_ki: output init failed rc=%d", rc);
4547
+ rb_raise(rb_eRuntimeError, "%s: output init failed rc=%d", ca_calling_method_name(), rc);
4520
4548
  }
4521
4549
 
4522
4550
  ca_size_t fiber_n = st_in.slab_elements;
4523
- partition_index_pair_i32 *buf =
4524
- xmalloc(sizeof(partition_index_pair_i32) * (size_t) fiber_n);
4551
+ volatile VALUE hbuf = 0, haux = 0;
4552
+ partition_index_pair_i32 *buf = ALLOCV_N(partition_index_pair_i32, hbuf, fiber_n);
4553
+ (void) haux;
4525
4554
 
4526
4555
  char *pi, *po;
4527
4556
  boolean8_t *mi, *mo;
@@ -4590,7 +4619,8 @@ partition_index_ki_native_i32 (VALUE self, CArray *ca, int axis, ca_size_t kth,
4590
4619
  }
4591
4620
  ca_iter_state_sync_slab(&st_out);
4592
4621
  }
4593
- xfree(buf);
4622
+ ALLOCV_END(hbuf);
4623
+ ALLOCV_END(haux);
4594
4624
  ca_iter_state_finish(&st_in);
4595
4625
  ca_iter_state_finish(&st_out);
4596
4626
  return vout;
@@ -4687,18 +4717,19 @@ partition_index_ki_native_u32 (VALUE self, CArray *ca, int axis, ca_size_t kth,
4687
4717
  rc = ca_iter_state_init_l2(&st_in, ca, CA_SLAB_AXES,
4688
4718
  slab_axes, 1, 0);
4689
4719
  if ( rc != CA_ITER_OK ) {
4690
- rb_raise(rb_eRuntimeError, "partition_index_ki: input init failed rc=%d", rc);
4720
+ rb_raise(rb_eRuntimeError, "%s: input init failed rc=%d", ca_calling_method_name(), rc);
4691
4721
  }
4692
- rc = ca_iter_state_init_l2(&st_out, co, CA_SLAB_AXES,
4722
+ rc = ca_iter_state_init_l2_paired(&st_out, &st_in, co, CA_SLAB_AXES,
4693
4723
  slab_axes, 1, CA_KERNEL_WRITE);
4694
4724
  if ( rc != CA_ITER_OK ) {
4695
4725
  ca_iter_state_finish(&st_in);
4696
- rb_raise(rb_eRuntimeError, "partition_index_ki: output init failed rc=%d", rc);
4726
+ rb_raise(rb_eRuntimeError, "%s: output init failed rc=%d", ca_calling_method_name(), rc);
4697
4727
  }
4698
4728
 
4699
4729
  ca_size_t fiber_n = st_in.slab_elements;
4700
- partition_index_pair_u32 *buf =
4701
- xmalloc(sizeof(partition_index_pair_u32) * (size_t) fiber_n);
4730
+ volatile VALUE hbuf = 0, haux = 0;
4731
+ partition_index_pair_u32 *buf = ALLOCV_N(partition_index_pair_u32, hbuf, fiber_n);
4732
+ (void) haux;
4702
4733
 
4703
4734
  char *pi, *po;
4704
4735
  boolean8_t *mi, *mo;
@@ -4767,7 +4798,8 @@ partition_index_ki_native_u32 (VALUE self, CArray *ca, int axis, ca_size_t kth,
4767
4798
  }
4768
4799
  ca_iter_state_sync_slab(&st_out);
4769
4800
  }
4770
- xfree(buf);
4801
+ ALLOCV_END(hbuf);
4802
+ ALLOCV_END(haux);
4771
4803
  ca_iter_state_finish(&st_in);
4772
4804
  ca_iter_state_finish(&st_out);
4773
4805
  return vout;
@@ -4864,18 +4896,19 @@ partition_index_ki_native_i64 (VALUE self, CArray *ca, int axis, ca_size_t kth,
4864
4896
  rc = ca_iter_state_init_l2(&st_in, ca, CA_SLAB_AXES,
4865
4897
  slab_axes, 1, 0);
4866
4898
  if ( rc != CA_ITER_OK ) {
4867
- rb_raise(rb_eRuntimeError, "partition_index_ki: input init failed rc=%d", rc);
4899
+ rb_raise(rb_eRuntimeError, "%s: input init failed rc=%d", ca_calling_method_name(), rc);
4868
4900
  }
4869
- rc = ca_iter_state_init_l2(&st_out, co, CA_SLAB_AXES,
4901
+ rc = ca_iter_state_init_l2_paired(&st_out, &st_in, co, CA_SLAB_AXES,
4870
4902
  slab_axes, 1, CA_KERNEL_WRITE);
4871
4903
  if ( rc != CA_ITER_OK ) {
4872
4904
  ca_iter_state_finish(&st_in);
4873
- rb_raise(rb_eRuntimeError, "partition_index_ki: output init failed rc=%d", rc);
4905
+ rb_raise(rb_eRuntimeError, "%s: output init failed rc=%d", ca_calling_method_name(), rc);
4874
4906
  }
4875
4907
 
4876
4908
  ca_size_t fiber_n = st_in.slab_elements;
4877
- partition_index_pair_i64 *buf =
4878
- xmalloc(sizeof(partition_index_pair_i64) * (size_t) fiber_n);
4909
+ volatile VALUE hbuf = 0, haux = 0;
4910
+ partition_index_pair_i64 *buf = ALLOCV_N(partition_index_pair_i64, hbuf, fiber_n);
4911
+ (void) haux;
4879
4912
 
4880
4913
  char *pi, *po;
4881
4914
  boolean8_t *mi, *mo;
@@ -4944,7 +4977,8 @@ partition_index_ki_native_i64 (VALUE self, CArray *ca, int axis, ca_size_t kth,
4944
4977
  }
4945
4978
  ca_iter_state_sync_slab(&st_out);
4946
4979
  }
4947
- xfree(buf);
4980
+ ALLOCV_END(hbuf);
4981
+ ALLOCV_END(haux);
4948
4982
  ca_iter_state_finish(&st_in);
4949
4983
  ca_iter_state_finish(&st_out);
4950
4984
  return vout;
@@ -5041,18 +5075,19 @@ partition_index_ki_native_u64 (VALUE self, CArray *ca, int axis, ca_size_t kth,
5041
5075
  rc = ca_iter_state_init_l2(&st_in, ca, CA_SLAB_AXES,
5042
5076
  slab_axes, 1, 0);
5043
5077
  if ( rc != CA_ITER_OK ) {
5044
- rb_raise(rb_eRuntimeError, "partition_index_ki: input init failed rc=%d", rc);
5078
+ rb_raise(rb_eRuntimeError, "%s: input init failed rc=%d", ca_calling_method_name(), rc);
5045
5079
  }
5046
- rc = ca_iter_state_init_l2(&st_out, co, CA_SLAB_AXES,
5080
+ rc = ca_iter_state_init_l2_paired(&st_out, &st_in, co, CA_SLAB_AXES,
5047
5081
  slab_axes, 1, CA_KERNEL_WRITE);
5048
5082
  if ( rc != CA_ITER_OK ) {
5049
5083
  ca_iter_state_finish(&st_in);
5050
- rb_raise(rb_eRuntimeError, "partition_index_ki: output init failed rc=%d", rc);
5084
+ rb_raise(rb_eRuntimeError, "%s: output init failed rc=%d", ca_calling_method_name(), rc);
5051
5085
  }
5052
5086
 
5053
5087
  ca_size_t fiber_n = st_in.slab_elements;
5054
- partition_index_pair_u64 *buf =
5055
- xmalloc(sizeof(partition_index_pair_u64) * (size_t) fiber_n);
5088
+ volatile VALUE hbuf = 0, haux = 0;
5089
+ partition_index_pair_u64 *buf = ALLOCV_N(partition_index_pair_u64, hbuf, fiber_n);
5090
+ (void) haux;
5056
5091
 
5057
5092
  char *pi, *po;
5058
5093
  boolean8_t *mi, *mo;
@@ -5121,7 +5156,8 @@ partition_index_ki_native_u64 (VALUE self, CArray *ca, int axis, ca_size_t kth,
5121
5156
  }
5122
5157
  ca_iter_state_sync_slab(&st_out);
5123
5158
  }
5124
- xfree(buf);
5159
+ ALLOCV_END(hbuf);
5160
+ ALLOCV_END(haux);
5125
5161
  ca_iter_state_finish(&st_in);
5126
5162
  ca_iter_state_finish(&st_out);
5127
5163
  return vout;
@@ -5137,12 +5173,8 @@ partition_index_cmp_f32 (const void *a, const void *b)
5137
5173
  {
5138
5174
  const partition_index_pair_f32 *pa = (const partition_index_pair_f32 *) a;
5139
5175
  const partition_index_pair_f32 *pb = (const partition_index_pair_f32 *) b;
5140
- int nan_a = isnan(pa->v);
5141
- int nan_b = isnan(pb->v);
5142
- if ( nan_a && !nan_b ) return 1;
5143
- if ( nan_b && !nan_a ) return -1;
5144
- if ( pa->v > pb->v ) return 1;
5145
- if ( pa->v < pb->v ) return -1;
5176
+ int c = CA_FLOAT_ORDER_CMP(pa->v, pb->v);
5177
+ if ( c != 0 ) return c;
5146
5178
  /* Stable tie-break by original fiber-local index. */
5147
5179
  if ( pa->i < pb->i ) return -1;
5148
5180
  if ( pa->i > pb->i ) return 1;
@@ -5222,18 +5254,19 @@ partition_index_ki_native_f32 (VALUE self, CArray *ca, int axis, ca_size_t kth,
5222
5254
  rc = ca_iter_state_init_l2(&st_in, ca, CA_SLAB_AXES,
5223
5255
  slab_axes, 1, 0);
5224
5256
  if ( rc != CA_ITER_OK ) {
5225
- rb_raise(rb_eRuntimeError, "partition_index_ki: input init failed rc=%d", rc);
5257
+ rb_raise(rb_eRuntimeError, "%s: input init failed rc=%d", ca_calling_method_name(), rc);
5226
5258
  }
5227
- rc = ca_iter_state_init_l2(&st_out, co, CA_SLAB_AXES,
5259
+ rc = ca_iter_state_init_l2_paired(&st_out, &st_in, co, CA_SLAB_AXES,
5228
5260
  slab_axes, 1, CA_KERNEL_WRITE);
5229
5261
  if ( rc != CA_ITER_OK ) {
5230
5262
  ca_iter_state_finish(&st_in);
5231
- rb_raise(rb_eRuntimeError, "partition_index_ki: output init failed rc=%d", rc);
5263
+ rb_raise(rb_eRuntimeError, "%s: output init failed rc=%d", ca_calling_method_name(), rc);
5232
5264
  }
5233
5265
 
5234
5266
  ca_size_t fiber_n = st_in.slab_elements;
5235
- partition_index_pair_f32 *buf =
5236
- xmalloc(sizeof(partition_index_pair_f32) * (size_t) fiber_n);
5267
+ volatile VALUE hbuf = 0, haux = 0;
5268
+ partition_index_pair_f32 *buf = ALLOCV_N(partition_index_pair_f32, hbuf, fiber_n);
5269
+ (void) haux;
5237
5270
 
5238
5271
  char *pi, *po;
5239
5272
  boolean8_t *mi, *mo;
@@ -5302,7 +5335,8 @@ partition_index_ki_native_f32 (VALUE self, CArray *ca, int axis, ca_size_t kth,
5302
5335
  }
5303
5336
  ca_iter_state_sync_slab(&st_out);
5304
5337
  }
5305
- xfree(buf);
5338
+ ALLOCV_END(hbuf);
5339
+ ALLOCV_END(haux);
5306
5340
  ca_iter_state_finish(&st_in);
5307
5341
  ca_iter_state_finish(&st_out);
5308
5342
  return vout;
@@ -5318,12 +5352,8 @@ partition_index_cmp_f64 (const void *a, const void *b)
5318
5352
  {
5319
5353
  const partition_index_pair_f64 *pa = (const partition_index_pair_f64 *) a;
5320
5354
  const partition_index_pair_f64 *pb = (const partition_index_pair_f64 *) b;
5321
- int nan_a = isnan(pa->v);
5322
- int nan_b = isnan(pb->v);
5323
- if ( nan_a && !nan_b ) return 1;
5324
- if ( nan_b && !nan_a ) return -1;
5325
- if ( pa->v > pb->v ) return 1;
5326
- if ( pa->v < pb->v ) return -1;
5355
+ int c = CA_FLOAT_ORDER_CMP(pa->v, pb->v);
5356
+ if ( c != 0 ) return c;
5327
5357
  /* Stable tie-break by original fiber-local index. */
5328
5358
  if ( pa->i < pb->i ) return -1;
5329
5359
  if ( pa->i > pb->i ) return 1;
@@ -5403,18 +5433,19 @@ partition_index_ki_native_f64 (VALUE self, CArray *ca, int axis, ca_size_t kth,
5403
5433
  rc = ca_iter_state_init_l2(&st_in, ca, CA_SLAB_AXES,
5404
5434
  slab_axes, 1, 0);
5405
5435
  if ( rc != CA_ITER_OK ) {
5406
- rb_raise(rb_eRuntimeError, "partition_index_ki: input init failed rc=%d", rc);
5436
+ rb_raise(rb_eRuntimeError, "%s: input init failed rc=%d", ca_calling_method_name(), rc);
5407
5437
  }
5408
- rc = ca_iter_state_init_l2(&st_out, co, CA_SLAB_AXES,
5438
+ rc = ca_iter_state_init_l2_paired(&st_out, &st_in, co, CA_SLAB_AXES,
5409
5439
  slab_axes, 1, CA_KERNEL_WRITE);
5410
5440
  if ( rc != CA_ITER_OK ) {
5411
5441
  ca_iter_state_finish(&st_in);
5412
- rb_raise(rb_eRuntimeError, "partition_index_ki: output init failed rc=%d", rc);
5442
+ rb_raise(rb_eRuntimeError, "%s: output init failed rc=%d", ca_calling_method_name(), rc);
5413
5443
  }
5414
5444
 
5415
5445
  ca_size_t fiber_n = st_in.slab_elements;
5416
- partition_index_pair_f64 *buf =
5417
- xmalloc(sizeof(partition_index_pair_f64) * (size_t) fiber_n);
5446
+ volatile VALUE hbuf = 0, haux = 0;
5447
+ partition_index_pair_f64 *buf = ALLOCV_N(partition_index_pair_f64, hbuf, fiber_n);
5448
+ (void) haux;
5418
5449
 
5419
5450
  char *pi, *po;
5420
5451
  boolean8_t *mi, *mo;
@@ -5483,7 +5514,8 @@ partition_index_ki_native_f64 (VALUE self, CArray *ca, int axis, ca_size_t kth,
5483
5514
  }
5484
5515
  ca_iter_state_sync_slab(&st_out);
5485
5516
  }
5486
- xfree(buf);
5517
+ ALLOCV_END(hbuf);
5518
+ ALLOCV_END(haux);
5487
5519
  ca_iter_state_finish(&st_in);
5488
5520
  ca_iter_state_finish(&st_out);
5489
5521
  return vout;
@@ -5580,18 +5612,19 @@ partition_index_ki_native_bool (VALUE self, CArray *ca, int axis, ca_size_t kth,
5580
5612
  rc = ca_iter_state_init_l2(&st_in, ca, CA_SLAB_AXES,
5581
5613
  slab_axes, 1, 0);
5582
5614
  if ( rc != CA_ITER_OK ) {
5583
- rb_raise(rb_eRuntimeError, "partition_index_ki: input init failed rc=%d", rc);
5615
+ rb_raise(rb_eRuntimeError, "%s: input init failed rc=%d", ca_calling_method_name(), rc);
5584
5616
  }
5585
- rc = ca_iter_state_init_l2(&st_out, co, CA_SLAB_AXES,
5617
+ rc = ca_iter_state_init_l2_paired(&st_out, &st_in, co, CA_SLAB_AXES,
5586
5618
  slab_axes, 1, CA_KERNEL_WRITE);
5587
5619
  if ( rc != CA_ITER_OK ) {
5588
5620
  ca_iter_state_finish(&st_in);
5589
- rb_raise(rb_eRuntimeError, "partition_index_ki: output init failed rc=%d", rc);
5621
+ rb_raise(rb_eRuntimeError, "%s: output init failed rc=%d", ca_calling_method_name(), rc);
5590
5622
  }
5591
5623
 
5592
5624
  ca_size_t fiber_n = st_in.slab_elements;
5593
- partition_index_pair_bool *buf =
5594
- xmalloc(sizeof(partition_index_pair_bool) * (size_t) fiber_n);
5625
+ volatile VALUE hbuf = 0, haux = 0;
5626
+ partition_index_pair_bool *buf = ALLOCV_N(partition_index_pair_bool, hbuf, fiber_n);
5627
+ (void) haux;
5595
5628
 
5596
5629
  char *pi, *po;
5597
5630
  boolean8_t *mi, *mo;
@@ -5660,7 +5693,8 @@ partition_index_ki_native_bool (VALUE self, CArray *ca, int axis, ca_size_t kth,
5660
5693
  }
5661
5694
  ca_iter_state_sync_slab(&st_out);
5662
5695
  }
5663
- xfree(buf);
5696
+ ALLOCV_END(hbuf);
5697
+ ALLOCV_END(haux);
5664
5698
  ca_iter_state_finish(&st_in);
5665
5699
  ca_iter_state_finish(&st_out);
5666
5700
  return vout;
@@ -5676,14 +5710,8 @@ partition_index_cmp_object (const void *a, const void *b)
5676
5710
  {
5677
5711
  const partition_index_pair_object *pa = (const partition_index_pair_object *) a;
5678
5712
  const partition_index_pair_object *pb = (const partition_index_pair_object *) b;
5679
- VALUE r = rb_funcall(pa->v, rb_intern("<=>"), 1, pb->v);
5680
- if ( NIL_P(r) ) {
5681
- rb_raise(rb_eArgError, "partition_index_ki: comparison of %s with %s failed",
5682
- rb_obj_classname(pa->v), rb_obj_classname(pb->v));
5683
- }
5684
- int c = NUM2INT(r);
5685
- if ( c > 0 ) return 1;
5686
- if ( c < 0 ) return -1;
5713
+ int c = ca_obj_order_cmp(pa->v, pb->v);
5714
+ if ( c != 0 ) return c;
5687
5715
  /* Stable tie-break by original fiber-local index. */
5688
5716
  if ( pa->i < pb->i ) return -1;
5689
5717
  if ( pa->i > pb->i ) return 1;
@@ -5745,9 +5773,34 @@ partition_index_quickselect_object (partition_index_pair_object *arr,
5745
5773
  }
5746
5774
  }
5747
5775
 
5776
+ /* The object lane calls Ruby for every cell, so a walk can raise part
5777
+ way. Its states live in the caller's frame, and ca_iter_ensure
5778
+ finishes them however the body leaves. */
5779
+ typedef struct {
5780
+ ca_iter_state states[2];
5781
+ VALUE self;
5782
+ CArray *ca;
5783
+ int axis;
5784
+ ca_size_t kth;
5785
+ int masked_last;
5786
+ } partition_index_ki_native_object_frame;
5787
+
5748
5788
  static VALUE
5749
- partition_index_ki_native_object (VALUE self, CArray *ca, int axis, ca_size_t kth, int masked_last)
5750
- {
5789
+ partition_index_ki_native_object_body (VALUE arg)
5790
+ {
5791
+ partition_index_ki_native_object_frame *fr = (partition_index_ki_native_object_frame *) arg;
5792
+ VALUE self = fr->self;
5793
+ (void) self;
5794
+ CArray *ca = fr->ca;
5795
+ (void) ca;
5796
+ int axis = fr->axis;
5797
+ (void) axis;
5798
+ ca_size_t kth = fr->kth;
5799
+ (void) kth;
5800
+ int masked_last = fr->masked_last;
5801
+ (void) masked_last;
5802
+ #define st_in (fr->states[0])
5803
+ #define st_out (fr->states[1])
5751
5804
  VALUE vout = rb_ca_template_with_type(self, INT2NUM(CA_SIZE),
5752
5805
  INT2NUM(0));
5753
5806
  CArray *co;
@@ -5758,23 +5811,23 @@ partition_index_ki_native_object (VALUE self, CArray *ca, int axis, ca_size_t kt
5758
5811
  positions align between iterators. */
5759
5812
  int8_t slab_axes[CA_RANK_MAX];
5760
5813
  slab_axes[0] = (int8_t) axis;
5761
- ca_iter_state st_in, st_out;
5762
5814
  int rc;
5763
5815
  rc = ca_iter_state_init_l2(&st_in, ca, CA_SLAB_AXES,
5764
5816
  slab_axes, 1, 0);
5765
5817
  if ( rc != CA_ITER_OK ) {
5766
- rb_raise(rb_eRuntimeError, "partition_index_ki: input init failed rc=%d", rc);
5818
+ rb_raise(rb_eRuntimeError, "%s: input init failed rc=%d", ca_calling_method_name(), rc);
5767
5819
  }
5768
- rc = ca_iter_state_init_l2(&st_out, co, CA_SLAB_AXES,
5820
+ rc = ca_iter_state_init_l2_paired(&st_out, &st_in, co, CA_SLAB_AXES,
5769
5821
  slab_axes, 1, CA_KERNEL_WRITE);
5770
5822
  if ( rc != CA_ITER_OK ) {
5771
5823
  ca_iter_state_finish(&st_in);
5772
- rb_raise(rb_eRuntimeError, "partition_index_ki: output init failed rc=%d", rc);
5824
+ rb_raise(rb_eRuntimeError, "%s: output init failed rc=%d", ca_calling_method_name(), rc);
5773
5825
  }
5774
5826
 
5775
5827
  ca_size_t fiber_n = st_in.slab_elements;
5776
- partition_index_pair_object *buf =
5777
- xmalloc(sizeof(partition_index_pair_object) * (size_t) fiber_n);
5828
+ volatile VALUE hbuf = 0, haux = 0;
5829
+ partition_index_pair_object *buf = ALLOCV_N(partition_index_pair_object, hbuf, fiber_n);
5830
+ (void) haux;
5778
5831
 
5779
5832
  char *pi, *po;
5780
5833
  boolean8_t *mi, *mo;
@@ -5843,12 +5896,28 @@ partition_index_ki_native_object (VALUE self, CArray *ca, int axis, ca_size_t kt
5843
5896
  }
5844
5897
  ca_iter_state_sync_slab(&st_out);
5845
5898
  }
5846
- xfree(buf);
5899
+ ALLOCV_END(hbuf);
5900
+ ALLOCV_END(haux);
5847
5901
  ca_iter_state_finish(&st_in);
5848
5902
  ca_iter_state_finish(&st_out);
5849
5903
  return vout;
5904
+ #undef st_in
5905
+ #undef st_out
5850
5906
  }
5851
5907
 
5908
+ static VALUE
5909
+ partition_index_ki_native_object (VALUE self, CArray *ca, int axis, ca_size_t kth, int masked_last)
5910
+ {
5911
+ partition_index_ki_native_object_frame fr;
5912
+ fr.self = self;
5913
+ fr.ca = ca;
5914
+ fr.axis = axis;
5915
+ fr.kth = kth;
5916
+ fr.masked_last = masked_last;
5917
+ return ca_iter_ensure(2, fr.states, partition_index_ki_native_object_body, (VALUE) &fr);
5918
+ }
5919
+
5920
+
5852
5921
  typedef struct {
5853
5922
  char *vp; /* pointer to the fiber cell (nb bytes wide) */
5854
5923
  ca_size_t nb; /* element byte width (= ca->bytes) */
@@ -5941,18 +6010,19 @@ partition_index_ki_native_fixlen (VALUE self, CArray *ca, int axis, ca_size_t kt
5941
6010
  rc = ca_iter_state_init_l2(&st_in, ca, CA_SLAB_AXES,
5942
6011
  slab_axes, 1, 0);
5943
6012
  if ( rc != CA_ITER_OK ) {
5944
- rb_raise(rb_eRuntimeError, "partition_index_ki: input init failed rc=%d", rc);
6013
+ rb_raise(rb_eRuntimeError, "%s: input init failed rc=%d", ca_calling_method_name(), rc);
5945
6014
  }
5946
- rc = ca_iter_state_init_l2(&st_out, co, CA_SLAB_AXES,
6015
+ rc = ca_iter_state_init_l2_paired(&st_out, &st_in, co, CA_SLAB_AXES,
5947
6016
  slab_axes, 1, CA_KERNEL_WRITE);
5948
6017
  if ( rc != CA_ITER_OK ) {
5949
6018
  ca_iter_state_finish(&st_in);
5950
- rb_raise(rb_eRuntimeError, "partition_index_ki: output init failed rc=%d", rc);
6019
+ rb_raise(rb_eRuntimeError, "%s: output init failed rc=%d", ca_calling_method_name(), rc);
5951
6020
  }
5952
6021
 
5953
6022
  ca_size_t fiber_n = st_in.slab_elements;
5954
- partition_index_pair_fixlen *buf =
5955
- xmalloc(sizeof(partition_index_pair_fixlen) * (size_t) fiber_n);
6023
+ volatile VALUE hbuf = 0, haux = 0;
6024
+ partition_index_pair_fixlen *buf = ALLOCV_N(partition_index_pair_fixlen, hbuf, fiber_n);
6025
+ (void) haux;
5956
6026
 
5957
6027
  char *pi, *po;
5958
6028
  boolean8_t *mi, *mo;
@@ -6021,7 +6091,8 @@ partition_index_ki_native_fixlen (VALUE self, CArray *ca, int axis, ca_size_t kt
6021
6091
  }
6022
6092
  ca_iter_state_sync_slab(&st_out);
6023
6093
  }
6024
- xfree(buf);
6094
+ ALLOCV_END(hbuf);
6095
+ ALLOCV_END(haux);
6025
6096
  ca_iter_state_finish(&st_in);
6026
6097
  ca_iter_state_finish(&st_out);
6027
6098
  return vout;
@@ -6032,50 +6103,43 @@ partition_index_ki_dispatch (VALUE self, VALUE vaxis, VALUE vkth, int masked_las
6032
6103
  {
6033
6104
  CArray *src;
6034
6105
  GetCArray(self, src);
6035
- /* Face gate (partition): same as the sort dispatcher --
6036
- fixlen -> memcmp default, numeric -> ORDERABLE required. */
6106
+ if ( ca_is_lazy_view(src) ) {
6107
+ volatile VALUE evaluated = ca_lazy_evaluated(self);
6108
+ if ( ! NIL_P(evaluated) ) {
6109
+ self = evaluated;
6110
+ GetCArray(self, src);
6111
+ }
6112
+ }
6113
+ /* Face gate (partition): same as the sort dispatcher. */
6037
6114
  if ( ca_is_face(src) ) {
6038
- VALUE _face = self;
6039
- int _orderable = ca_test_flag(src, CA_FLAG_FACE_ORDERABLE_STORAGE);
6040
- VALUE _st = rb_ca_strip_face_value(self);
6041
- CArray *_sc;
6042
- GetCArray(_st, _sc);
6043
- if ( _sc->data_type != CA_FIXLEN && ! _orderable ) {
6044
- rb_raise(rb_eArgError,
6045
- "partition_index_ki: Face-typed input (%s) is not orderable "
6046
- "by storage; use ca.parent to descend to storage",
6047
- rb_obj_classname(_face));
6048
- }
6049
- self = _st;
6050
- src = _sc;
6051
- }
6052
- int axis = NUM2INT(vaxis);
6053
- if ( axis < 0 ) axis += src->ndim;
6054
- if ( axis < 0 || axis >= src->ndim ) {
6055
- rb_raise(rb_eArgError, "partition_index_ki: axis %d out of range for ndim %d", NUM2INT(vaxis), src->ndim);
6056
- }
6057
- ca_size_t kth = NUM2SIZE(vkth);
6115
+ self = ca_order_key(self, ca_calling_method_name());
6116
+ GetCArray(self, src);
6117
+ }
6118
+ int axis = ca_normalize_axis_arg(vaxis, src->ndim, NULL);
6119
+ ca_size_t kth_raw = (ca_size_t) ca_integer_arg(vkth, "kth", NULL);
6120
+ ca_size_t kth = kth_raw;
6058
6121
  ca_size_t axis_n = src->dim[axis];
6059
6122
  if ( kth < 0 ) kth += axis_n;
6060
6123
  if ( kth < 0 || kth >= axis_n ) {
6061
- rb_raise(rb_eArgError, "partition_index_ki: kth %lld out of range for axis size %lld", (long long) NUM2SIZE(vkth), (long long) axis_n);
6124
+ rb_raise(rb_eArgError, "%s: kth %lld out of range for length %lld", ca_calling_method_name(), (long long) kth_raw, (long long) axis_n);
6062
6125
  }
6063
6126
  switch ( src->data_type ) {
6064
- case CA_INT8: return partition_index_ki_native_i8(self, src, axis, kth, masked_last);
6065
- case CA_UINT8: return partition_index_ki_native_u8(self, src, axis, kth, masked_last);
6066
- case CA_INT16: return partition_index_ki_native_i16(self, src, axis, kth, masked_last);
6067
- case CA_UINT16: return partition_index_ki_native_u16(self, src, axis, kth, masked_last);
6068
- case CA_INT32: return partition_index_ki_native_i32(self, src, axis, kth, masked_last);
6069
- case CA_UINT32: return partition_index_ki_native_u32(self, src, axis, kth, masked_last);
6070
- case CA_INT64: return partition_index_ki_native_i64(self, src, axis, kth, masked_last);
6071
- case CA_UINT64: return partition_index_ki_native_u64(self, src, axis, kth, masked_last);
6072
- case CA_FLOAT32: return partition_index_ki_native_f32(self, src, axis, kth, masked_last);
6073
- case CA_FLOAT64: return partition_index_ki_native_f64(self, src, axis, kth, masked_last);
6074
- case CA_BOOLEAN: return partition_index_ki_native_bool(self, src, axis, kth, masked_last);
6075
- case CA_OBJECT: return partition_index_ki_native_object(self, src, axis, kth, masked_last);
6076
- case CA_FIXLEN: return partition_index_ki_native_fixlen(self, src, axis, kth, masked_last);
6127
+ case CA_INT8: CA_RETURN_KEEPING(self, partition_index_ki_native_i8(self, src, axis, kth, masked_last));
6128
+ case CA_UINT8: CA_RETURN_KEEPING(self, partition_index_ki_native_u8(self, src, axis, kth, masked_last));
6129
+ case CA_INT16: CA_RETURN_KEEPING(self, partition_index_ki_native_i16(self, src, axis, kth, masked_last));
6130
+ case CA_UINT16: CA_RETURN_KEEPING(self, partition_index_ki_native_u16(self, src, axis, kth, masked_last));
6131
+ case CA_INT32: CA_RETURN_KEEPING(self, partition_index_ki_native_i32(self, src, axis, kth, masked_last));
6132
+ case CA_UINT32: CA_RETURN_KEEPING(self, partition_index_ki_native_u32(self, src, axis, kth, masked_last));
6133
+ case CA_INT64: CA_RETURN_KEEPING(self, partition_index_ki_native_i64(self, src, axis, kth, masked_last));
6134
+ case CA_UINT64: CA_RETURN_KEEPING(self, partition_index_ki_native_u64(self, src, axis, kth, masked_last));
6135
+ case CA_FLOAT32: CA_RETURN_KEEPING(self, partition_index_ki_native_f32(self, src, axis, kth, masked_last));
6136
+ case CA_FLOAT64: CA_RETURN_KEEPING(self, partition_index_ki_native_f64(self, src, axis, kth, masked_last));
6137
+ case CA_BOOLEAN: CA_RETURN_KEEPING(self, partition_index_ki_native_bool(self, src, axis, kth, masked_last));
6138
+ case CA_OBJECT: CA_RETURN_KEEPING(self, partition_index_ki_native_object(self, src, axis, kth, masked_last));
6139
+ case CA_FIXLEN: CA_RETURN_KEEPING(self, partition_index_ki_native_fixlen(self, src, axis, kth, masked_last));
6077
6140
  default:
6078
- rb_raise(rb_eCADataTypeError, "partition_index_ki: source data_type :%s not supported (expected one of: i8, u8, i16, u16, i32, u32, i64, u64, f32, f64, bool, object, fixlen)", ca_type_name[src->data_type]);
6141
+ ca_face_refuse_numeric(self, ca_calling_method_name(), rb_eCADataTypeError);
6142
+ rb_raise(rb_eCADataTypeError, "%s: source data_type :%s not supported (expected one of: int8, uint8, int16, uint16, int32, uint32, int64, uint64, float32, float64, boolean, object, fixlen)", ca_calling_method_name(), ca_type_name[src->data_type]);
6079
6143
  }
6080
6144
  return Qnil; /* unreachable */
6081
6145
  }
@@ -6093,19 +6157,15 @@ rb_ca_partition_index_kw (int argc, VALUE *argv, VALUE self)
6093
6157
  volatile VALUE raxis = Qnil;
6094
6158
  volatile VALUE rmasked_position = Qnil;
6095
6159
  rb_scan_options(ropt, "axis,masked_position", &raxis, &rmasked_position);
6096
- if ( NIL_P(raxis) ) raxis = INT2NUM(0); /* default axis */
6097
- int masked_last = 1; /* default masked_position: :last */
6098
- if ( ! NIL_P(rmasked_position) ) {
6099
- static ID sym_first = 0, sym_last = 0;
6100
- if ( ! sym_first ) sym_first = rb_intern("first");
6101
- if ( ! sym_last ) sym_last = rb_intern("last");
6102
- ID mp_id = SYM2ID(rmasked_position);
6103
- if ( mp_id == sym_last ) masked_last = 1;
6104
- else if ( mp_id == sym_first ) masked_last = 0;
6105
- else {
6106
- rb_raise(rb_eArgError, "partition_index: unknown masked_position %s (expected :first or :last)", rb_id2name(mp_id));
6160
+ if ( NIL_P(raxis) ) { /* no axis: the whole array */
6161
+ {
6162
+ volatile VALUE evaluated = ca_lazy_evaluated(self);
6163
+ if ( ! NIL_P(evaluated) ) self = evaluated;
6107
6164
  }
6165
+ self = rb_ca_flatten(self);
6166
+ raxis = INT2NUM(0);
6108
6167
  }
6168
+ int masked_last = NIL_P(rmasked_position) ? 1 : ca_symbol_choice(rmasked_position, "masked_position", "first", "last", NULL);
6109
6169
  if ( argc != 1 ) {
6110
6170
  rb_raise(rb_eArgError, "partition_index: wrong number of positional arguments (given %d, expected 1: kth)", argc);
6111
6171
  }
@@ -6205,18 +6265,19 @@ partition_addr_ki_native_i8 (VALUE self, CArray *ca, int axis, ca_size_t kth, in
6205
6265
  rc = ca_iter_state_init_l2(&st_in, ca, CA_SLAB_AXES,
6206
6266
  slab_axes, 1, 0);
6207
6267
  if ( rc != CA_ITER_OK ) {
6208
- rb_raise(rb_eRuntimeError, "partition_addr_ki: input init failed rc=%d", rc);
6268
+ rb_raise(rb_eRuntimeError, "%s: input init failed rc=%d", ca_calling_method_name(), rc);
6209
6269
  }
6210
- rc = ca_iter_state_init_l2(&st_out, co, CA_SLAB_AXES,
6270
+ rc = ca_iter_state_init_l2_paired(&st_out, &st_in, co, CA_SLAB_AXES,
6211
6271
  slab_axes, 1, CA_KERNEL_WRITE);
6212
6272
  if ( rc != CA_ITER_OK ) {
6213
6273
  ca_iter_state_finish(&st_in);
6214
- rb_raise(rb_eRuntimeError, "partition_addr_ki: output init failed rc=%d", rc);
6274
+ rb_raise(rb_eRuntimeError, "%s: output init failed rc=%d", ca_calling_method_name(), rc);
6215
6275
  }
6216
6276
 
6217
6277
  ca_size_t fiber_n = st_in.slab_elements;
6218
- partition_addr_pair_i8 *buf =
6219
- xmalloc(sizeof(partition_addr_pair_i8) * (size_t) fiber_n);
6278
+ volatile VALUE hbuf = 0, haux = 0;
6279
+ partition_addr_pair_i8 *buf = ALLOCV_N(partition_addr_pair_i8, hbuf, fiber_n);
6280
+ (void) haux;
6220
6281
 
6221
6282
  /* :view_flat semantics: kernel computes view-flat address
6222
6283
  (= row-major position in ca's shape, 0..elements-1) for
@@ -6317,7 +6378,8 @@ partition_addr_ki_native_i8 (VALUE self, CArray *ca, int axis, ca_size_t kth, in
6317
6378
  cur_outer_idx[m] = 0;
6318
6379
  }
6319
6380
  }
6320
- xfree(buf);
6381
+ ALLOCV_END(hbuf);
6382
+ ALLOCV_END(haux);
6321
6383
  ca_iter_state_finish(&st_in);
6322
6384
  ca_iter_state_finish(&st_out);
6323
6385
  return vout;
@@ -6414,18 +6476,19 @@ partition_addr_ki_native_u8 (VALUE self, CArray *ca, int axis, ca_size_t kth, in
6414
6476
  rc = ca_iter_state_init_l2(&st_in, ca, CA_SLAB_AXES,
6415
6477
  slab_axes, 1, 0);
6416
6478
  if ( rc != CA_ITER_OK ) {
6417
- rb_raise(rb_eRuntimeError, "partition_addr_ki: input init failed rc=%d", rc);
6479
+ rb_raise(rb_eRuntimeError, "%s: input init failed rc=%d", ca_calling_method_name(), rc);
6418
6480
  }
6419
- rc = ca_iter_state_init_l2(&st_out, co, CA_SLAB_AXES,
6481
+ rc = ca_iter_state_init_l2_paired(&st_out, &st_in, co, CA_SLAB_AXES,
6420
6482
  slab_axes, 1, CA_KERNEL_WRITE);
6421
6483
  if ( rc != CA_ITER_OK ) {
6422
6484
  ca_iter_state_finish(&st_in);
6423
- rb_raise(rb_eRuntimeError, "partition_addr_ki: output init failed rc=%d", rc);
6485
+ rb_raise(rb_eRuntimeError, "%s: output init failed rc=%d", ca_calling_method_name(), rc);
6424
6486
  }
6425
6487
 
6426
6488
  ca_size_t fiber_n = st_in.slab_elements;
6427
- partition_addr_pair_u8 *buf =
6428
- xmalloc(sizeof(partition_addr_pair_u8) * (size_t) fiber_n);
6489
+ volatile VALUE hbuf = 0, haux = 0;
6490
+ partition_addr_pair_u8 *buf = ALLOCV_N(partition_addr_pair_u8, hbuf, fiber_n);
6491
+ (void) haux;
6429
6492
 
6430
6493
  /* :view_flat semantics: kernel computes view-flat address
6431
6494
  (= row-major position in ca's shape, 0..elements-1) for
@@ -6526,7 +6589,8 @@ partition_addr_ki_native_u8 (VALUE self, CArray *ca, int axis, ca_size_t kth, in
6526
6589
  cur_outer_idx[m] = 0;
6527
6590
  }
6528
6591
  }
6529
- xfree(buf);
6592
+ ALLOCV_END(hbuf);
6593
+ ALLOCV_END(haux);
6530
6594
  ca_iter_state_finish(&st_in);
6531
6595
  ca_iter_state_finish(&st_out);
6532
6596
  return vout;
@@ -6623,18 +6687,19 @@ partition_addr_ki_native_i16 (VALUE self, CArray *ca, int axis, ca_size_t kth, i
6623
6687
  rc = ca_iter_state_init_l2(&st_in, ca, CA_SLAB_AXES,
6624
6688
  slab_axes, 1, 0);
6625
6689
  if ( rc != CA_ITER_OK ) {
6626
- rb_raise(rb_eRuntimeError, "partition_addr_ki: input init failed rc=%d", rc);
6690
+ rb_raise(rb_eRuntimeError, "%s: input init failed rc=%d", ca_calling_method_name(), rc);
6627
6691
  }
6628
- rc = ca_iter_state_init_l2(&st_out, co, CA_SLAB_AXES,
6692
+ rc = ca_iter_state_init_l2_paired(&st_out, &st_in, co, CA_SLAB_AXES,
6629
6693
  slab_axes, 1, CA_KERNEL_WRITE);
6630
6694
  if ( rc != CA_ITER_OK ) {
6631
6695
  ca_iter_state_finish(&st_in);
6632
- rb_raise(rb_eRuntimeError, "partition_addr_ki: output init failed rc=%d", rc);
6696
+ rb_raise(rb_eRuntimeError, "%s: output init failed rc=%d", ca_calling_method_name(), rc);
6633
6697
  }
6634
6698
 
6635
6699
  ca_size_t fiber_n = st_in.slab_elements;
6636
- partition_addr_pair_i16 *buf =
6637
- xmalloc(sizeof(partition_addr_pair_i16) * (size_t) fiber_n);
6700
+ volatile VALUE hbuf = 0, haux = 0;
6701
+ partition_addr_pair_i16 *buf = ALLOCV_N(partition_addr_pair_i16, hbuf, fiber_n);
6702
+ (void) haux;
6638
6703
 
6639
6704
  /* :view_flat semantics: kernel computes view-flat address
6640
6705
  (= row-major position in ca's shape, 0..elements-1) for
@@ -6735,7 +6800,8 @@ partition_addr_ki_native_i16 (VALUE self, CArray *ca, int axis, ca_size_t kth, i
6735
6800
  cur_outer_idx[m] = 0;
6736
6801
  }
6737
6802
  }
6738
- xfree(buf);
6803
+ ALLOCV_END(hbuf);
6804
+ ALLOCV_END(haux);
6739
6805
  ca_iter_state_finish(&st_in);
6740
6806
  ca_iter_state_finish(&st_out);
6741
6807
  return vout;
@@ -6832,18 +6898,19 @@ partition_addr_ki_native_u16 (VALUE self, CArray *ca, int axis, ca_size_t kth, i
6832
6898
  rc = ca_iter_state_init_l2(&st_in, ca, CA_SLAB_AXES,
6833
6899
  slab_axes, 1, 0);
6834
6900
  if ( rc != CA_ITER_OK ) {
6835
- rb_raise(rb_eRuntimeError, "partition_addr_ki: input init failed rc=%d", rc);
6901
+ rb_raise(rb_eRuntimeError, "%s: input init failed rc=%d", ca_calling_method_name(), rc);
6836
6902
  }
6837
- rc = ca_iter_state_init_l2(&st_out, co, CA_SLAB_AXES,
6903
+ rc = ca_iter_state_init_l2_paired(&st_out, &st_in, co, CA_SLAB_AXES,
6838
6904
  slab_axes, 1, CA_KERNEL_WRITE);
6839
6905
  if ( rc != CA_ITER_OK ) {
6840
6906
  ca_iter_state_finish(&st_in);
6841
- rb_raise(rb_eRuntimeError, "partition_addr_ki: output init failed rc=%d", rc);
6907
+ rb_raise(rb_eRuntimeError, "%s: output init failed rc=%d", ca_calling_method_name(), rc);
6842
6908
  }
6843
6909
 
6844
6910
  ca_size_t fiber_n = st_in.slab_elements;
6845
- partition_addr_pair_u16 *buf =
6846
- xmalloc(sizeof(partition_addr_pair_u16) * (size_t) fiber_n);
6911
+ volatile VALUE hbuf = 0, haux = 0;
6912
+ partition_addr_pair_u16 *buf = ALLOCV_N(partition_addr_pair_u16, hbuf, fiber_n);
6913
+ (void) haux;
6847
6914
 
6848
6915
  /* :view_flat semantics: kernel computes view-flat address
6849
6916
  (= row-major position in ca's shape, 0..elements-1) for
@@ -6944,7 +7011,8 @@ partition_addr_ki_native_u16 (VALUE self, CArray *ca, int axis, ca_size_t kth, i
6944
7011
  cur_outer_idx[m] = 0;
6945
7012
  }
6946
7013
  }
6947
- xfree(buf);
7014
+ ALLOCV_END(hbuf);
7015
+ ALLOCV_END(haux);
6948
7016
  ca_iter_state_finish(&st_in);
6949
7017
  ca_iter_state_finish(&st_out);
6950
7018
  return vout;
@@ -7041,18 +7109,19 @@ partition_addr_ki_native_i32 (VALUE self, CArray *ca, int axis, ca_size_t kth, i
7041
7109
  rc = ca_iter_state_init_l2(&st_in, ca, CA_SLAB_AXES,
7042
7110
  slab_axes, 1, 0);
7043
7111
  if ( rc != CA_ITER_OK ) {
7044
- rb_raise(rb_eRuntimeError, "partition_addr_ki: input init failed rc=%d", rc);
7112
+ rb_raise(rb_eRuntimeError, "%s: input init failed rc=%d", ca_calling_method_name(), rc);
7045
7113
  }
7046
- rc = ca_iter_state_init_l2(&st_out, co, CA_SLAB_AXES,
7114
+ rc = ca_iter_state_init_l2_paired(&st_out, &st_in, co, CA_SLAB_AXES,
7047
7115
  slab_axes, 1, CA_KERNEL_WRITE);
7048
7116
  if ( rc != CA_ITER_OK ) {
7049
7117
  ca_iter_state_finish(&st_in);
7050
- rb_raise(rb_eRuntimeError, "partition_addr_ki: output init failed rc=%d", rc);
7118
+ rb_raise(rb_eRuntimeError, "%s: output init failed rc=%d", ca_calling_method_name(), rc);
7051
7119
  }
7052
7120
 
7053
7121
  ca_size_t fiber_n = st_in.slab_elements;
7054
- partition_addr_pair_i32 *buf =
7055
- xmalloc(sizeof(partition_addr_pair_i32) * (size_t) fiber_n);
7122
+ volatile VALUE hbuf = 0, haux = 0;
7123
+ partition_addr_pair_i32 *buf = ALLOCV_N(partition_addr_pair_i32, hbuf, fiber_n);
7124
+ (void) haux;
7056
7125
 
7057
7126
  /* :view_flat semantics: kernel computes view-flat address
7058
7127
  (= row-major position in ca's shape, 0..elements-1) for
@@ -7153,7 +7222,8 @@ partition_addr_ki_native_i32 (VALUE self, CArray *ca, int axis, ca_size_t kth, i
7153
7222
  cur_outer_idx[m] = 0;
7154
7223
  }
7155
7224
  }
7156
- xfree(buf);
7225
+ ALLOCV_END(hbuf);
7226
+ ALLOCV_END(haux);
7157
7227
  ca_iter_state_finish(&st_in);
7158
7228
  ca_iter_state_finish(&st_out);
7159
7229
  return vout;
@@ -7250,18 +7320,19 @@ partition_addr_ki_native_u32 (VALUE self, CArray *ca, int axis, ca_size_t kth, i
7250
7320
  rc = ca_iter_state_init_l2(&st_in, ca, CA_SLAB_AXES,
7251
7321
  slab_axes, 1, 0);
7252
7322
  if ( rc != CA_ITER_OK ) {
7253
- rb_raise(rb_eRuntimeError, "partition_addr_ki: input init failed rc=%d", rc);
7323
+ rb_raise(rb_eRuntimeError, "%s: input init failed rc=%d", ca_calling_method_name(), rc);
7254
7324
  }
7255
- rc = ca_iter_state_init_l2(&st_out, co, CA_SLAB_AXES,
7325
+ rc = ca_iter_state_init_l2_paired(&st_out, &st_in, co, CA_SLAB_AXES,
7256
7326
  slab_axes, 1, CA_KERNEL_WRITE);
7257
7327
  if ( rc != CA_ITER_OK ) {
7258
7328
  ca_iter_state_finish(&st_in);
7259
- rb_raise(rb_eRuntimeError, "partition_addr_ki: output init failed rc=%d", rc);
7329
+ rb_raise(rb_eRuntimeError, "%s: output init failed rc=%d", ca_calling_method_name(), rc);
7260
7330
  }
7261
7331
 
7262
7332
  ca_size_t fiber_n = st_in.slab_elements;
7263
- partition_addr_pair_u32 *buf =
7264
- xmalloc(sizeof(partition_addr_pair_u32) * (size_t) fiber_n);
7333
+ volatile VALUE hbuf = 0, haux = 0;
7334
+ partition_addr_pair_u32 *buf = ALLOCV_N(partition_addr_pair_u32, hbuf, fiber_n);
7335
+ (void) haux;
7265
7336
 
7266
7337
  /* :view_flat semantics: kernel computes view-flat address
7267
7338
  (= row-major position in ca's shape, 0..elements-1) for
@@ -7362,7 +7433,8 @@ partition_addr_ki_native_u32 (VALUE self, CArray *ca, int axis, ca_size_t kth, i
7362
7433
  cur_outer_idx[m] = 0;
7363
7434
  }
7364
7435
  }
7365
- xfree(buf);
7436
+ ALLOCV_END(hbuf);
7437
+ ALLOCV_END(haux);
7366
7438
  ca_iter_state_finish(&st_in);
7367
7439
  ca_iter_state_finish(&st_out);
7368
7440
  return vout;
@@ -7459,18 +7531,19 @@ partition_addr_ki_native_i64 (VALUE self, CArray *ca, int axis, ca_size_t kth, i
7459
7531
  rc = ca_iter_state_init_l2(&st_in, ca, CA_SLAB_AXES,
7460
7532
  slab_axes, 1, 0);
7461
7533
  if ( rc != CA_ITER_OK ) {
7462
- rb_raise(rb_eRuntimeError, "partition_addr_ki: input init failed rc=%d", rc);
7534
+ rb_raise(rb_eRuntimeError, "%s: input init failed rc=%d", ca_calling_method_name(), rc);
7463
7535
  }
7464
- rc = ca_iter_state_init_l2(&st_out, co, CA_SLAB_AXES,
7536
+ rc = ca_iter_state_init_l2_paired(&st_out, &st_in, co, CA_SLAB_AXES,
7465
7537
  slab_axes, 1, CA_KERNEL_WRITE);
7466
7538
  if ( rc != CA_ITER_OK ) {
7467
7539
  ca_iter_state_finish(&st_in);
7468
- rb_raise(rb_eRuntimeError, "partition_addr_ki: output init failed rc=%d", rc);
7540
+ rb_raise(rb_eRuntimeError, "%s: output init failed rc=%d", ca_calling_method_name(), rc);
7469
7541
  }
7470
7542
 
7471
7543
  ca_size_t fiber_n = st_in.slab_elements;
7472
- partition_addr_pair_i64 *buf =
7473
- xmalloc(sizeof(partition_addr_pair_i64) * (size_t) fiber_n);
7544
+ volatile VALUE hbuf = 0, haux = 0;
7545
+ partition_addr_pair_i64 *buf = ALLOCV_N(partition_addr_pair_i64, hbuf, fiber_n);
7546
+ (void) haux;
7474
7547
 
7475
7548
  /* :view_flat semantics: kernel computes view-flat address
7476
7549
  (= row-major position in ca's shape, 0..elements-1) for
@@ -7571,7 +7644,8 @@ partition_addr_ki_native_i64 (VALUE self, CArray *ca, int axis, ca_size_t kth, i
7571
7644
  cur_outer_idx[m] = 0;
7572
7645
  }
7573
7646
  }
7574
- xfree(buf);
7647
+ ALLOCV_END(hbuf);
7648
+ ALLOCV_END(haux);
7575
7649
  ca_iter_state_finish(&st_in);
7576
7650
  ca_iter_state_finish(&st_out);
7577
7651
  return vout;
@@ -7668,18 +7742,19 @@ partition_addr_ki_native_u64 (VALUE self, CArray *ca, int axis, ca_size_t kth, i
7668
7742
  rc = ca_iter_state_init_l2(&st_in, ca, CA_SLAB_AXES,
7669
7743
  slab_axes, 1, 0);
7670
7744
  if ( rc != CA_ITER_OK ) {
7671
- rb_raise(rb_eRuntimeError, "partition_addr_ki: input init failed rc=%d", rc);
7745
+ rb_raise(rb_eRuntimeError, "%s: input init failed rc=%d", ca_calling_method_name(), rc);
7672
7746
  }
7673
- rc = ca_iter_state_init_l2(&st_out, co, CA_SLAB_AXES,
7747
+ rc = ca_iter_state_init_l2_paired(&st_out, &st_in, co, CA_SLAB_AXES,
7674
7748
  slab_axes, 1, CA_KERNEL_WRITE);
7675
7749
  if ( rc != CA_ITER_OK ) {
7676
7750
  ca_iter_state_finish(&st_in);
7677
- rb_raise(rb_eRuntimeError, "partition_addr_ki: output init failed rc=%d", rc);
7751
+ rb_raise(rb_eRuntimeError, "%s: output init failed rc=%d", ca_calling_method_name(), rc);
7678
7752
  }
7679
7753
 
7680
7754
  ca_size_t fiber_n = st_in.slab_elements;
7681
- partition_addr_pair_u64 *buf =
7682
- xmalloc(sizeof(partition_addr_pair_u64) * (size_t) fiber_n);
7755
+ volatile VALUE hbuf = 0, haux = 0;
7756
+ partition_addr_pair_u64 *buf = ALLOCV_N(partition_addr_pair_u64, hbuf, fiber_n);
7757
+ (void) haux;
7683
7758
 
7684
7759
  /* :view_flat semantics: kernel computes view-flat address
7685
7760
  (= row-major position in ca's shape, 0..elements-1) for
@@ -7780,7 +7855,8 @@ partition_addr_ki_native_u64 (VALUE self, CArray *ca, int axis, ca_size_t kth, i
7780
7855
  cur_outer_idx[m] = 0;
7781
7856
  }
7782
7857
  }
7783
- xfree(buf);
7858
+ ALLOCV_END(hbuf);
7859
+ ALLOCV_END(haux);
7784
7860
  ca_iter_state_finish(&st_in);
7785
7861
  ca_iter_state_finish(&st_out);
7786
7862
  return vout;
@@ -7796,12 +7872,8 @@ partition_addr_cmp_f32 (const void *a, const void *b)
7796
7872
  {
7797
7873
  const partition_addr_pair_f32 *pa = (const partition_addr_pair_f32 *) a;
7798
7874
  const partition_addr_pair_f32 *pb = (const partition_addr_pair_f32 *) b;
7799
- int nan_a = isnan(pa->v);
7800
- int nan_b = isnan(pb->v);
7801
- if ( nan_a && !nan_b ) return 1;
7802
- if ( nan_b && !nan_a ) return -1;
7803
- if ( pa->v > pb->v ) return 1;
7804
- if ( pa->v < pb->v ) return -1;
7875
+ int c = CA_FLOAT_ORDER_CMP(pa->v, pb->v);
7876
+ if ( c != 0 ) return c;
7805
7877
  /* Stable tie-break by original fiber-local index. */
7806
7878
  if ( pa->i < pb->i ) return -1;
7807
7879
  if ( pa->i > pb->i ) return 1;
@@ -7881,18 +7953,19 @@ partition_addr_ki_native_f32 (VALUE self, CArray *ca, int axis, ca_size_t kth, i
7881
7953
  rc = ca_iter_state_init_l2(&st_in, ca, CA_SLAB_AXES,
7882
7954
  slab_axes, 1, 0);
7883
7955
  if ( rc != CA_ITER_OK ) {
7884
- rb_raise(rb_eRuntimeError, "partition_addr_ki: input init failed rc=%d", rc);
7956
+ rb_raise(rb_eRuntimeError, "%s: input init failed rc=%d", ca_calling_method_name(), rc);
7885
7957
  }
7886
- rc = ca_iter_state_init_l2(&st_out, co, CA_SLAB_AXES,
7958
+ rc = ca_iter_state_init_l2_paired(&st_out, &st_in, co, CA_SLAB_AXES,
7887
7959
  slab_axes, 1, CA_KERNEL_WRITE);
7888
7960
  if ( rc != CA_ITER_OK ) {
7889
7961
  ca_iter_state_finish(&st_in);
7890
- rb_raise(rb_eRuntimeError, "partition_addr_ki: output init failed rc=%d", rc);
7962
+ rb_raise(rb_eRuntimeError, "%s: output init failed rc=%d", ca_calling_method_name(), rc);
7891
7963
  }
7892
7964
 
7893
7965
  ca_size_t fiber_n = st_in.slab_elements;
7894
- partition_addr_pair_f32 *buf =
7895
- xmalloc(sizeof(partition_addr_pair_f32) * (size_t) fiber_n);
7966
+ volatile VALUE hbuf = 0, haux = 0;
7967
+ partition_addr_pair_f32 *buf = ALLOCV_N(partition_addr_pair_f32, hbuf, fiber_n);
7968
+ (void) haux;
7896
7969
 
7897
7970
  /* :view_flat semantics: kernel computes view-flat address
7898
7971
  (= row-major position in ca's shape, 0..elements-1) for
@@ -7993,7 +8066,8 @@ partition_addr_ki_native_f32 (VALUE self, CArray *ca, int axis, ca_size_t kth, i
7993
8066
  cur_outer_idx[m] = 0;
7994
8067
  }
7995
8068
  }
7996
- xfree(buf);
8069
+ ALLOCV_END(hbuf);
8070
+ ALLOCV_END(haux);
7997
8071
  ca_iter_state_finish(&st_in);
7998
8072
  ca_iter_state_finish(&st_out);
7999
8073
  return vout;
@@ -8009,12 +8083,8 @@ partition_addr_cmp_f64 (const void *a, const void *b)
8009
8083
  {
8010
8084
  const partition_addr_pair_f64 *pa = (const partition_addr_pair_f64 *) a;
8011
8085
  const partition_addr_pair_f64 *pb = (const partition_addr_pair_f64 *) b;
8012
- int nan_a = isnan(pa->v);
8013
- int nan_b = isnan(pb->v);
8014
- if ( nan_a && !nan_b ) return 1;
8015
- if ( nan_b && !nan_a ) return -1;
8016
- if ( pa->v > pb->v ) return 1;
8017
- if ( pa->v < pb->v ) return -1;
8086
+ int c = CA_FLOAT_ORDER_CMP(pa->v, pb->v);
8087
+ if ( c != 0 ) return c;
8018
8088
  /* Stable tie-break by original fiber-local index. */
8019
8089
  if ( pa->i < pb->i ) return -1;
8020
8090
  if ( pa->i > pb->i ) return 1;
@@ -8094,18 +8164,19 @@ partition_addr_ki_native_f64 (VALUE self, CArray *ca, int axis, ca_size_t kth, i
8094
8164
  rc = ca_iter_state_init_l2(&st_in, ca, CA_SLAB_AXES,
8095
8165
  slab_axes, 1, 0);
8096
8166
  if ( rc != CA_ITER_OK ) {
8097
- rb_raise(rb_eRuntimeError, "partition_addr_ki: input init failed rc=%d", rc);
8167
+ rb_raise(rb_eRuntimeError, "%s: input init failed rc=%d", ca_calling_method_name(), rc);
8098
8168
  }
8099
- rc = ca_iter_state_init_l2(&st_out, co, CA_SLAB_AXES,
8169
+ rc = ca_iter_state_init_l2_paired(&st_out, &st_in, co, CA_SLAB_AXES,
8100
8170
  slab_axes, 1, CA_KERNEL_WRITE);
8101
8171
  if ( rc != CA_ITER_OK ) {
8102
8172
  ca_iter_state_finish(&st_in);
8103
- rb_raise(rb_eRuntimeError, "partition_addr_ki: output init failed rc=%d", rc);
8173
+ rb_raise(rb_eRuntimeError, "%s: output init failed rc=%d", ca_calling_method_name(), rc);
8104
8174
  }
8105
8175
 
8106
8176
  ca_size_t fiber_n = st_in.slab_elements;
8107
- partition_addr_pair_f64 *buf =
8108
- xmalloc(sizeof(partition_addr_pair_f64) * (size_t) fiber_n);
8177
+ volatile VALUE hbuf = 0, haux = 0;
8178
+ partition_addr_pair_f64 *buf = ALLOCV_N(partition_addr_pair_f64, hbuf, fiber_n);
8179
+ (void) haux;
8109
8180
 
8110
8181
  /* :view_flat semantics: kernel computes view-flat address
8111
8182
  (= row-major position in ca's shape, 0..elements-1) for
@@ -8206,7 +8277,8 @@ partition_addr_ki_native_f64 (VALUE self, CArray *ca, int axis, ca_size_t kth, i
8206
8277
  cur_outer_idx[m] = 0;
8207
8278
  }
8208
8279
  }
8209
- xfree(buf);
8280
+ ALLOCV_END(hbuf);
8281
+ ALLOCV_END(haux);
8210
8282
  ca_iter_state_finish(&st_in);
8211
8283
  ca_iter_state_finish(&st_out);
8212
8284
  return vout;
@@ -8303,18 +8375,19 @@ partition_addr_ki_native_bool (VALUE self, CArray *ca, int axis, ca_size_t kth,
8303
8375
  rc = ca_iter_state_init_l2(&st_in, ca, CA_SLAB_AXES,
8304
8376
  slab_axes, 1, 0);
8305
8377
  if ( rc != CA_ITER_OK ) {
8306
- rb_raise(rb_eRuntimeError, "partition_addr_ki: input init failed rc=%d", rc);
8378
+ rb_raise(rb_eRuntimeError, "%s: input init failed rc=%d", ca_calling_method_name(), rc);
8307
8379
  }
8308
- rc = ca_iter_state_init_l2(&st_out, co, CA_SLAB_AXES,
8380
+ rc = ca_iter_state_init_l2_paired(&st_out, &st_in, co, CA_SLAB_AXES,
8309
8381
  slab_axes, 1, CA_KERNEL_WRITE);
8310
8382
  if ( rc != CA_ITER_OK ) {
8311
8383
  ca_iter_state_finish(&st_in);
8312
- rb_raise(rb_eRuntimeError, "partition_addr_ki: output init failed rc=%d", rc);
8384
+ rb_raise(rb_eRuntimeError, "%s: output init failed rc=%d", ca_calling_method_name(), rc);
8313
8385
  }
8314
8386
 
8315
8387
  ca_size_t fiber_n = st_in.slab_elements;
8316
- partition_addr_pair_bool *buf =
8317
- xmalloc(sizeof(partition_addr_pair_bool) * (size_t) fiber_n);
8388
+ volatile VALUE hbuf = 0, haux = 0;
8389
+ partition_addr_pair_bool *buf = ALLOCV_N(partition_addr_pair_bool, hbuf, fiber_n);
8390
+ (void) haux;
8318
8391
 
8319
8392
  /* :view_flat semantics: kernel computes view-flat address
8320
8393
  (= row-major position in ca's shape, 0..elements-1) for
@@ -8415,7 +8488,8 @@ partition_addr_ki_native_bool (VALUE self, CArray *ca, int axis, ca_size_t kth,
8415
8488
  cur_outer_idx[m] = 0;
8416
8489
  }
8417
8490
  }
8418
- xfree(buf);
8491
+ ALLOCV_END(hbuf);
8492
+ ALLOCV_END(haux);
8419
8493
  ca_iter_state_finish(&st_in);
8420
8494
  ca_iter_state_finish(&st_out);
8421
8495
  return vout;
@@ -8431,14 +8505,8 @@ partition_addr_cmp_object (const void *a, const void *b)
8431
8505
  {
8432
8506
  const partition_addr_pair_object *pa = (const partition_addr_pair_object *) a;
8433
8507
  const partition_addr_pair_object *pb = (const partition_addr_pair_object *) b;
8434
- VALUE r = rb_funcall(pa->v, rb_intern("<=>"), 1, pb->v);
8435
- if ( NIL_P(r) ) {
8436
- rb_raise(rb_eArgError, "partition_addr_ki: comparison of %s with %s failed",
8437
- rb_obj_classname(pa->v), rb_obj_classname(pb->v));
8438
- }
8439
- int c = NUM2INT(r);
8440
- if ( c > 0 ) return 1;
8441
- if ( c < 0 ) return -1;
8508
+ int c = ca_obj_order_cmp(pa->v, pb->v);
8509
+ if ( c != 0 ) return c;
8442
8510
  /* Stable tie-break by original fiber-local index. */
8443
8511
  if ( pa->i < pb->i ) return -1;
8444
8512
  if ( pa->i > pb->i ) return 1;
@@ -8500,9 +8568,34 @@ partition_addr_quickselect_object (partition_addr_pair_object *arr,
8500
8568
  }
8501
8569
  }
8502
8570
 
8571
+ /* The object lane calls Ruby for every cell, so a walk can raise part
8572
+ way. Its states live in the caller's frame, and ca_iter_ensure
8573
+ finishes them however the body leaves. */
8574
+ typedef struct {
8575
+ ca_iter_state states[2];
8576
+ VALUE self;
8577
+ CArray *ca;
8578
+ int axis;
8579
+ ca_size_t kth;
8580
+ int masked_last;
8581
+ } partition_addr_ki_native_object_frame;
8582
+
8503
8583
  static VALUE
8504
- partition_addr_ki_native_object (VALUE self, CArray *ca, int axis, ca_size_t kth, int masked_last)
8505
- {
8584
+ partition_addr_ki_native_object_body (VALUE arg)
8585
+ {
8586
+ partition_addr_ki_native_object_frame *fr = (partition_addr_ki_native_object_frame *) arg;
8587
+ VALUE self = fr->self;
8588
+ (void) self;
8589
+ CArray *ca = fr->ca;
8590
+ (void) ca;
8591
+ int axis = fr->axis;
8592
+ (void) axis;
8593
+ ca_size_t kth = fr->kth;
8594
+ (void) kth;
8595
+ int masked_last = fr->masked_last;
8596
+ (void) masked_last;
8597
+ #define st_in (fr->states[0])
8598
+ #define st_out (fr->states[1])
8506
8599
  VALUE vout = rb_ca_template_with_type(self, INT2NUM(CA_SIZE),
8507
8600
  INT2NUM(0));
8508
8601
  CArray *co;
@@ -8513,23 +8606,23 @@ partition_addr_ki_native_object (VALUE self, CArray *ca, int axis, ca_size_t kth
8513
8606
  positions align between iterators. */
8514
8607
  int8_t slab_axes[CA_RANK_MAX];
8515
8608
  slab_axes[0] = (int8_t) axis;
8516
- ca_iter_state st_in, st_out;
8517
8609
  int rc;
8518
8610
  rc = ca_iter_state_init_l2(&st_in, ca, CA_SLAB_AXES,
8519
8611
  slab_axes, 1, 0);
8520
8612
  if ( rc != CA_ITER_OK ) {
8521
- rb_raise(rb_eRuntimeError, "partition_addr_ki: input init failed rc=%d", rc);
8613
+ rb_raise(rb_eRuntimeError, "%s: input init failed rc=%d", ca_calling_method_name(), rc);
8522
8614
  }
8523
- rc = ca_iter_state_init_l2(&st_out, co, CA_SLAB_AXES,
8615
+ rc = ca_iter_state_init_l2_paired(&st_out, &st_in, co, CA_SLAB_AXES,
8524
8616
  slab_axes, 1, CA_KERNEL_WRITE);
8525
8617
  if ( rc != CA_ITER_OK ) {
8526
8618
  ca_iter_state_finish(&st_in);
8527
- rb_raise(rb_eRuntimeError, "partition_addr_ki: output init failed rc=%d", rc);
8619
+ rb_raise(rb_eRuntimeError, "%s: output init failed rc=%d", ca_calling_method_name(), rc);
8528
8620
  }
8529
8621
 
8530
8622
  ca_size_t fiber_n = st_in.slab_elements;
8531
- partition_addr_pair_object *buf =
8532
- xmalloc(sizeof(partition_addr_pair_object) * (size_t) fiber_n);
8623
+ volatile VALUE hbuf = 0, haux = 0;
8624
+ partition_addr_pair_object *buf = ALLOCV_N(partition_addr_pair_object, hbuf, fiber_n);
8625
+ (void) haux;
8533
8626
 
8534
8627
  /* :view_flat semantics: kernel computes view-flat address
8535
8628
  (= row-major position in ca's shape, 0..elements-1) for
@@ -8630,12 +8723,28 @@ partition_addr_ki_native_object (VALUE self, CArray *ca, int axis, ca_size_t kth
8630
8723
  cur_outer_idx[m] = 0;
8631
8724
  }
8632
8725
  }
8633
- xfree(buf);
8726
+ ALLOCV_END(hbuf);
8727
+ ALLOCV_END(haux);
8634
8728
  ca_iter_state_finish(&st_in);
8635
8729
  ca_iter_state_finish(&st_out);
8636
8730
  return vout;
8731
+ #undef st_in
8732
+ #undef st_out
8637
8733
  }
8638
8734
 
8735
+ static VALUE
8736
+ partition_addr_ki_native_object (VALUE self, CArray *ca, int axis, ca_size_t kth, int masked_last)
8737
+ {
8738
+ partition_addr_ki_native_object_frame fr;
8739
+ fr.self = self;
8740
+ fr.ca = ca;
8741
+ fr.axis = axis;
8742
+ fr.kth = kth;
8743
+ fr.masked_last = masked_last;
8744
+ return ca_iter_ensure(2, fr.states, partition_addr_ki_native_object_body, (VALUE) &fr);
8745
+ }
8746
+
8747
+
8639
8748
  typedef struct {
8640
8749
  char *vp; /* pointer to the fiber cell (nb bytes wide) */
8641
8750
  ca_size_t nb; /* element byte width (= ca->bytes) */
@@ -8728,18 +8837,19 @@ partition_addr_ki_native_fixlen (VALUE self, CArray *ca, int axis, ca_size_t kth
8728
8837
  rc = ca_iter_state_init_l2(&st_in, ca, CA_SLAB_AXES,
8729
8838
  slab_axes, 1, 0);
8730
8839
  if ( rc != CA_ITER_OK ) {
8731
- rb_raise(rb_eRuntimeError, "partition_addr_ki: input init failed rc=%d", rc);
8840
+ rb_raise(rb_eRuntimeError, "%s: input init failed rc=%d", ca_calling_method_name(), rc);
8732
8841
  }
8733
- rc = ca_iter_state_init_l2(&st_out, co, CA_SLAB_AXES,
8842
+ rc = ca_iter_state_init_l2_paired(&st_out, &st_in, co, CA_SLAB_AXES,
8734
8843
  slab_axes, 1, CA_KERNEL_WRITE);
8735
8844
  if ( rc != CA_ITER_OK ) {
8736
8845
  ca_iter_state_finish(&st_in);
8737
- rb_raise(rb_eRuntimeError, "partition_addr_ki: output init failed rc=%d", rc);
8846
+ rb_raise(rb_eRuntimeError, "%s: output init failed rc=%d", ca_calling_method_name(), rc);
8738
8847
  }
8739
8848
 
8740
8849
  ca_size_t fiber_n = st_in.slab_elements;
8741
- partition_addr_pair_fixlen *buf =
8742
- xmalloc(sizeof(partition_addr_pair_fixlen) * (size_t) fiber_n);
8850
+ volatile VALUE hbuf = 0, haux = 0;
8851
+ partition_addr_pair_fixlen *buf = ALLOCV_N(partition_addr_pair_fixlen, hbuf, fiber_n);
8852
+ (void) haux;
8743
8853
 
8744
8854
  /* :view_flat semantics: kernel computes view-flat address
8745
8855
  (= row-major position in ca's shape, 0..elements-1) for
@@ -8840,7 +8950,8 @@ partition_addr_ki_native_fixlen (VALUE self, CArray *ca, int axis, ca_size_t kth
8840
8950
  cur_outer_idx[m] = 0;
8841
8951
  }
8842
8952
  }
8843
- xfree(buf);
8953
+ ALLOCV_END(hbuf);
8954
+ ALLOCV_END(haux);
8844
8955
  ca_iter_state_finish(&st_in);
8845
8956
  ca_iter_state_finish(&st_out);
8846
8957
  return vout;
@@ -8851,60 +8962,47 @@ partition_addr_ki_dispatch (VALUE self, VALUE vaxis, VALUE vkth, int masked_last
8851
8962
  {
8852
8963
  CArray *src;
8853
8964
  GetCArray(self, src);
8854
- /* Face gate (partition): same as the sort dispatcher --
8855
- fixlen -> memcmp default, numeric -> ORDERABLE required. */
8965
+ if ( ca_is_lazy_view(src) ) {
8966
+ volatile VALUE evaluated = ca_lazy_evaluated(self);
8967
+ if ( ! NIL_P(evaluated) ) {
8968
+ self = evaluated;
8969
+ GetCArray(self, src);
8970
+ }
8971
+ }
8972
+ /* Face gate (partition): same as the sort dispatcher. */
8856
8973
  if ( ca_is_face(src) ) {
8857
- VALUE _face = self;
8858
- int _orderable = ca_test_flag(src, CA_FLAG_FACE_ORDERABLE_STORAGE);
8859
- VALUE _st = rb_ca_strip_face_value(self);
8860
- CArray *_sc;
8861
- GetCArray(_st, _sc);
8862
- if ( _sc->data_type != CA_FIXLEN && ! _orderable ) {
8863
- rb_raise(rb_eArgError,
8864
- "partition_addr_ki: Face-typed input (%s) is not orderable "
8865
- "by storage; use ca.parent to descend to storage",
8866
- rb_obj_classname(_face));
8867
- }
8868
- self = _st;
8869
- src = _sc;
8870
- }
8871
- int axis = NUM2INT(vaxis);
8872
- if ( axis < 0 ) axis += src->ndim;
8873
- if ( axis < 0 || axis >= src->ndim ) {
8874
- rb_raise(rb_eArgError, "partition_addr_ki: axis %d out of range for ndim %d", NUM2INT(vaxis), src->ndim);
8875
- }
8876
- ca_size_t kth = NUM2SIZE(vkth);
8974
+ self = ca_order_key(self, ca_calling_method_name());
8975
+ GetCArray(self, src);
8976
+ }
8977
+ int axis = ca_normalize_axis_arg(vaxis, src->ndim, NULL);
8978
+ ca_size_t kth_raw = (ca_size_t) ca_integer_arg(vkth, "kth", NULL);
8979
+ ca_size_t kth = kth_raw;
8877
8980
  ca_size_t axis_n = src->dim[axis];
8878
8981
  if ( kth < 0 ) kth += axis_n;
8879
8982
  if ( kth < 0 || kth >= axis_n ) {
8880
- rb_raise(rb_eArgError, "partition_addr_ki: kth %lld out of range for axis size %lld", (long long) NUM2SIZE(vkth), (long long) axis_n);
8983
+ rb_raise(rb_eArgError, "%s: kth %lld out of range for length %lld", ca_calling_method_name(), (long long) kth_raw, (long long) axis_n);
8881
8984
  }
8882
8985
  switch ( src->data_type ) {
8883
- case CA_INT8: return partition_addr_ki_native_i8(self, src, axis, kth, masked_last);
8884
- case CA_UINT8: return partition_addr_ki_native_u8(self, src, axis, kth, masked_last);
8885
- case CA_INT16: return partition_addr_ki_native_i16(self, src, axis, kth, masked_last);
8886
- case CA_UINT16: return partition_addr_ki_native_u16(self, src, axis, kth, masked_last);
8887
- case CA_INT32: return partition_addr_ki_native_i32(self, src, axis, kth, masked_last);
8888
- case CA_UINT32: return partition_addr_ki_native_u32(self, src, axis, kth, masked_last);
8889
- case CA_INT64: return partition_addr_ki_native_i64(self, src, axis, kth, masked_last);
8890
- case CA_UINT64: return partition_addr_ki_native_u64(self, src, axis, kth, masked_last);
8891
- case CA_FLOAT32: return partition_addr_ki_native_f32(self, src, axis, kth, masked_last);
8892
- case CA_FLOAT64: return partition_addr_ki_native_f64(self, src, axis, kth, masked_last);
8893
- case CA_BOOLEAN: return partition_addr_ki_native_bool(self, src, axis, kth, masked_last);
8894
- case CA_OBJECT: return partition_addr_ki_native_object(self, src, axis, kth, masked_last);
8895
- case CA_FIXLEN: return partition_addr_ki_native_fixlen(self, src, axis, kth, masked_last);
8986
+ case CA_INT8: CA_RETURN_KEEPING(self, partition_addr_ki_native_i8(self, src, axis, kth, masked_last));
8987
+ case CA_UINT8: CA_RETURN_KEEPING(self, partition_addr_ki_native_u8(self, src, axis, kth, masked_last));
8988
+ case CA_INT16: CA_RETURN_KEEPING(self, partition_addr_ki_native_i16(self, src, axis, kth, masked_last));
8989
+ case CA_UINT16: CA_RETURN_KEEPING(self, partition_addr_ki_native_u16(self, src, axis, kth, masked_last));
8990
+ case CA_INT32: CA_RETURN_KEEPING(self, partition_addr_ki_native_i32(self, src, axis, kth, masked_last));
8991
+ case CA_UINT32: CA_RETURN_KEEPING(self, partition_addr_ki_native_u32(self, src, axis, kth, masked_last));
8992
+ case CA_INT64: CA_RETURN_KEEPING(self, partition_addr_ki_native_i64(self, src, axis, kth, masked_last));
8993
+ case CA_UINT64: CA_RETURN_KEEPING(self, partition_addr_ki_native_u64(self, src, axis, kth, masked_last));
8994
+ case CA_FLOAT32: CA_RETURN_KEEPING(self, partition_addr_ki_native_f32(self, src, axis, kth, masked_last));
8995
+ case CA_FLOAT64: CA_RETURN_KEEPING(self, partition_addr_ki_native_f64(self, src, axis, kth, masked_last));
8996
+ case CA_BOOLEAN: CA_RETURN_KEEPING(self, partition_addr_ki_native_bool(self, src, axis, kth, masked_last));
8997
+ case CA_OBJECT: CA_RETURN_KEEPING(self, partition_addr_ki_native_object(self, src, axis, kth, masked_last));
8998
+ case CA_FIXLEN: CA_RETURN_KEEPING(self, partition_addr_ki_native_fixlen(self, src, axis, kth, masked_last));
8896
8999
  default:
8897
- rb_raise(rb_eCADataTypeError, "partition_addr_ki: source data_type :%s not supported (expected one of: i8, u8, i16, u16, i32, u32, i64, u64, f32, f64, bool, object, fixlen)", ca_type_name[src->data_type]);
9000
+ ca_face_refuse_numeric(self, ca_calling_method_name(), rb_eCADataTypeError);
9001
+ rb_raise(rb_eCADataTypeError, "%s: source data_type :%s not supported (expected one of: int8, uint8, int16, uint16, int32, uint32, int64, uint64, float32, float64, boolean, object, fixlen)", ca_calling_method_name(), ca_type_name[src->data_type]);
8898
9002
  }
8899
9003
  return Qnil; /* unreachable */
8900
9004
  }
8901
9005
 
8902
- VALUE
8903
- rb_ca_partition_addr_ki (VALUE self, VALUE vaxis, VALUE vkth)
8904
- {
8905
- return partition_addr_ki_dispatch(self, vaxis, vkth, 1);
8906
- }
8907
-
8908
9006
  VALUE
8909
9007
  rb_ca_partition_addr_ki_mp (VALUE self, VALUE vaxis, VALUE vkth, int masked_last)
8910
9008
  {
@@ -8941,21 +9039,21 @@ rank_index_ki_native_i8 (VALUE self, CArray *ca, int axis, int do_stable, int de
8941
9039
  rc = ca_iter_state_init_l2(&st_in, ca, CA_SLAB_AXES,
8942
9040
  slab_axes, 1, 0);
8943
9041
  if ( rc != CA_ITER_OK ) {
8944
- rb_raise(rb_eRuntimeError, "rank_index_ki: input init failed rc=%d", rc);
9042
+ rb_raise(rb_eRuntimeError, "%s: input init failed rc=%d", ca_calling_method_name(), rc);
8945
9043
  }
8946
- rc = ca_iter_state_init_l2(&st_out, co, CA_SLAB_AXES,
9044
+ rc = ca_iter_state_init_l2_paired(&st_out, &st_in, co, CA_SLAB_AXES,
8947
9045
  slab_axes, 1, CA_KERNEL_WRITE);
8948
9046
  if ( rc != CA_ITER_OK ) {
8949
9047
  ca_iter_state_finish(&st_in);
8950
- rb_raise(rb_eRuntimeError, "rank_index_ki: output init failed rc=%d", rc);
9048
+ rb_raise(rb_eRuntimeError, "%s: output init failed rc=%d", ca_calling_method_name(), rc);
8951
9049
  }
8952
9050
 
8953
9051
  ca_size_t fiber_n = st_in.slab_elements;
8954
- rank_index_pair_i8 *buf =
8955
- xmalloc(sizeof(rank_index_pair_i8) * (size_t) fiber_n);
9052
+ volatile VALUE hbuf = 0, haux = 0;
9053
+ rank_index_pair_i8 *buf = ALLOCV_N(rank_index_pair_i8, hbuf, fiber_n);
8956
9054
  rank_index_pair_i8 *aux = NULL;
8957
9055
  if ( do_stable ) {
8958
- aux = xmalloc(sizeof(rank_index_pair_i8) * (size_t) fiber_n);
9056
+ aux = ALLOCV_N(rank_index_pair_i8, haux, fiber_n);
8959
9057
  }
8960
9058
 
8961
9059
  char *pi, *po;
@@ -8997,8 +9095,9 @@ rank_index_ki_native_i8 (VALUE self, CArray *ca, int axis, int do_stable, int de
8997
9095
  gaps ordinal would have spent on the tie are never used, so a
8998
9096
  later out_r stays <= the ordinal r at that position, preserving
8999
9097
  both the ascending order and (via the order() method's
9000
- (n-1)-out_r descending transform) the descending order. NaN
9001
- cells never tie with each other under dense (IEEE754 x != x). */
9098
+ descending transform) the descending order. NaN cells tie
9099
+ with each other, as sort_addr and the value-hash family treat
9100
+ every NaN as one value. */
9002
9101
  ca_size_t out_r = 0;
9003
9102
  for ( ca_size_t r = 0; r < k_unmasked; r++ ) {
9004
9103
  if ( r > 0 ) {
@@ -9010,14 +9109,25 @@ rank_index_ki_native_i8 (VALUE self, CArray *ca, int axis, int do_stable, int de
9010
9109
  }
9011
9110
  *(ca_size_t *) (po + buf[r].i * ostride) = (ca_size_t) out_r;
9012
9111
  }
9112
+ if ( dense == 2 && k_unmasked > 0 ) {
9113
+ ca_size_t nan_start = k_unmasked;
9114
+ ca_size_t top = nan_start > 0
9115
+ ? *(ca_size_t *) (po + buf[nan_start - 1].i * ostride) : (ca_size_t) -1;
9116
+ for ( ca_size_t r = 0; r < k_unmasked; r++ ) {
9117
+ ca_size_t *o = (ca_size_t *) (po + buf[r].i * ostride);
9118
+ *o = ( r < nan_start ) ? top - *o : top + 1;
9119
+ }
9120
+ }
9013
9121
  /* Mirror input mask to output (= UNDEF rank for masked
9014
- cells). Write via co->mask->ptr directly with the
9015
- output slab offset, because the iter's mo isn't
9016
- populated in CA_KERNEL_WRITE mode (= reduce form's
9017
- op_mask pattern). */
9122
+ cells). Write via co->mask->ptr directly, because a
9123
+ mask written through a WRITE walk's cursor does not
9124
+ reach the array. co is a fresh row-major entity and
9125
+ the walk cuts its slabs from a buffer laid out the
9126
+ same way, so the slab's offset from st_out.alias_ptr
9127
+ is its offset in co. */
9018
9128
  if ( mi && co->mask ) {
9019
9129
  boolean8_t *omask_base = (boolean8_t *) co->mask->ptr
9020
- + ((po - (char *) co->ptr) / (ca_size_t) co->bytes);
9130
+ + ((po - st_out.alias_ptr) / (ca_size_t) co->bytes);
9021
9131
  ca_size_t mostride = st_out.slab_mask_strides[0];
9022
9132
  for ( ca_size_t k = 0; k < fiber_n; k++ ) {
9023
9133
  if ( mi[k * mstride] ) omask_base[k * mostride] = 1;
@@ -9025,8 +9135,8 @@ rank_index_ki_native_i8 (VALUE self, CArray *ca, int axis, int do_stable, int de
9025
9135
  }
9026
9136
  ca_iter_state_sync_slab(&st_out);
9027
9137
  }
9028
- xfree(buf);
9029
- if ( aux ) xfree(aux);
9138
+ ALLOCV_END(hbuf);
9139
+ ALLOCV_END(haux);
9030
9140
  ca_iter_state_finish(&st_in);
9031
9141
  ca_iter_state_finish(&st_out);
9032
9142
  return vout;
@@ -9060,21 +9170,21 @@ rank_index_ki_native_u8 (VALUE self, CArray *ca, int axis, int do_stable, int de
9060
9170
  rc = ca_iter_state_init_l2(&st_in, ca, CA_SLAB_AXES,
9061
9171
  slab_axes, 1, 0);
9062
9172
  if ( rc != CA_ITER_OK ) {
9063
- rb_raise(rb_eRuntimeError, "rank_index_ki: input init failed rc=%d", rc);
9173
+ rb_raise(rb_eRuntimeError, "%s: input init failed rc=%d", ca_calling_method_name(), rc);
9064
9174
  }
9065
- rc = ca_iter_state_init_l2(&st_out, co, CA_SLAB_AXES,
9175
+ rc = ca_iter_state_init_l2_paired(&st_out, &st_in, co, CA_SLAB_AXES,
9066
9176
  slab_axes, 1, CA_KERNEL_WRITE);
9067
9177
  if ( rc != CA_ITER_OK ) {
9068
9178
  ca_iter_state_finish(&st_in);
9069
- rb_raise(rb_eRuntimeError, "rank_index_ki: output init failed rc=%d", rc);
9179
+ rb_raise(rb_eRuntimeError, "%s: output init failed rc=%d", ca_calling_method_name(), rc);
9070
9180
  }
9071
9181
 
9072
9182
  ca_size_t fiber_n = st_in.slab_elements;
9073
- rank_index_pair_u8 *buf =
9074
- xmalloc(sizeof(rank_index_pair_u8) * (size_t) fiber_n);
9183
+ volatile VALUE hbuf = 0, haux = 0;
9184
+ rank_index_pair_u8 *buf = ALLOCV_N(rank_index_pair_u8, hbuf, fiber_n);
9075
9185
  rank_index_pair_u8 *aux = NULL;
9076
9186
  if ( do_stable ) {
9077
- aux = xmalloc(sizeof(rank_index_pair_u8) * (size_t) fiber_n);
9187
+ aux = ALLOCV_N(rank_index_pair_u8, haux, fiber_n);
9078
9188
  }
9079
9189
 
9080
9190
  char *pi, *po;
@@ -9116,8 +9226,9 @@ rank_index_ki_native_u8 (VALUE self, CArray *ca, int axis, int do_stable, int de
9116
9226
  gaps ordinal would have spent on the tie are never used, so a
9117
9227
  later out_r stays <= the ordinal r at that position, preserving
9118
9228
  both the ascending order and (via the order() method's
9119
- (n-1)-out_r descending transform) the descending order. NaN
9120
- cells never tie with each other under dense (IEEE754 x != x). */
9229
+ descending transform) the descending order. NaN cells tie
9230
+ with each other, as sort_addr and the value-hash family treat
9231
+ every NaN as one value. */
9121
9232
  ca_size_t out_r = 0;
9122
9233
  for ( ca_size_t r = 0; r < k_unmasked; r++ ) {
9123
9234
  if ( r > 0 ) {
@@ -9129,14 +9240,25 @@ rank_index_ki_native_u8 (VALUE self, CArray *ca, int axis, int do_stable, int de
9129
9240
  }
9130
9241
  *(ca_size_t *) (po + buf[r].i * ostride) = (ca_size_t) out_r;
9131
9242
  }
9243
+ if ( dense == 2 && k_unmasked > 0 ) {
9244
+ ca_size_t nan_start = k_unmasked;
9245
+ ca_size_t top = nan_start > 0
9246
+ ? *(ca_size_t *) (po + buf[nan_start - 1].i * ostride) : (ca_size_t) -1;
9247
+ for ( ca_size_t r = 0; r < k_unmasked; r++ ) {
9248
+ ca_size_t *o = (ca_size_t *) (po + buf[r].i * ostride);
9249
+ *o = ( r < nan_start ) ? top - *o : top + 1;
9250
+ }
9251
+ }
9132
9252
  /* Mirror input mask to output (= UNDEF rank for masked
9133
- cells). Write via co->mask->ptr directly with the
9134
- output slab offset, because the iter's mo isn't
9135
- populated in CA_KERNEL_WRITE mode (= reduce form's
9136
- op_mask pattern). */
9253
+ cells). Write via co->mask->ptr directly, because a
9254
+ mask written through a WRITE walk's cursor does not
9255
+ reach the array. co is a fresh row-major entity and
9256
+ the walk cuts its slabs from a buffer laid out the
9257
+ same way, so the slab's offset from st_out.alias_ptr
9258
+ is its offset in co. */
9137
9259
  if ( mi && co->mask ) {
9138
9260
  boolean8_t *omask_base = (boolean8_t *) co->mask->ptr
9139
- + ((po - (char *) co->ptr) / (ca_size_t) co->bytes);
9261
+ + ((po - st_out.alias_ptr) / (ca_size_t) co->bytes);
9140
9262
  ca_size_t mostride = st_out.slab_mask_strides[0];
9141
9263
  for ( ca_size_t k = 0; k < fiber_n; k++ ) {
9142
9264
  if ( mi[k * mstride] ) omask_base[k * mostride] = 1;
@@ -9144,8 +9266,8 @@ rank_index_ki_native_u8 (VALUE self, CArray *ca, int axis, int do_stable, int de
9144
9266
  }
9145
9267
  ca_iter_state_sync_slab(&st_out);
9146
9268
  }
9147
- xfree(buf);
9148
- if ( aux ) xfree(aux);
9269
+ ALLOCV_END(hbuf);
9270
+ ALLOCV_END(haux);
9149
9271
  ca_iter_state_finish(&st_in);
9150
9272
  ca_iter_state_finish(&st_out);
9151
9273
  return vout;
@@ -9179,21 +9301,21 @@ rank_index_ki_native_i16 (VALUE self, CArray *ca, int axis, int do_stable, int d
9179
9301
  rc = ca_iter_state_init_l2(&st_in, ca, CA_SLAB_AXES,
9180
9302
  slab_axes, 1, 0);
9181
9303
  if ( rc != CA_ITER_OK ) {
9182
- rb_raise(rb_eRuntimeError, "rank_index_ki: input init failed rc=%d", rc);
9304
+ rb_raise(rb_eRuntimeError, "%s: input init failed rc=%d", ca_calling_method_name(), rc);
9183
9305
  }
9184
- rc = ca_iter_state_init_l2(&st_out, co, CA_SLAB_AXES,
9306
+ rc = ca_iter_state_init_l2_paired(&st_out, &st_in, co, CA_SLAB_AXES,
9185
9307
  slab_axes, 1, CA_KERNEL_WRITE);
9186
9308
  if ( rc != CA_ITER_OK ) {
9187
9309
  ca_iter_state_finish(&st_in);
9188
- rb_raise(rb_eRuntimeError, "rank_index_ki: output init failed rc=%d", rc);
9310
+ rb_raise(rb_eRuntimeError, "%s: output init failed rc=%d", ca_calling_method_name(), rc);
9189
9311
  }
9190
9312
 
9191
9313
  ca_size_t fiber_n = st_in.slab_elements;
9192
- rank_index_pair_i16 *buf =
9193
- xmalloc(sizeof(rank_index_pair_i16) * (size_t) fiber_n);
9314
+ volatile VALUE hbuf = 0, haux = 0;
9315
+ rank_index_pair_i16 *buf = ALLOCV_N(rank_index_pair_i16, hbuf, fiber_n);
9194
9316
  rank_index_pair_i16 *aux = NULL;
9195
9317
  if ( do_stable ) {
9196
- aux = xmalloc(sizeof(rank_index_pair_i16) * (size_t) fiber_n);
9318
+ aux = ALLOCV_N(rank_index_pair_i16, haux, fiber_n);
9197
9319
  }
9198
9320
 
9199
9321
  char *pi, *po;
@@ -9235,8 +9357,9 @@ rank_index_ki_native_i16 (VALUE self, CArray *ca, int axis, int do_stable, int d
9235
9357
  gaps ordinal would have spent on the tie are never used, so a
9236
9358
  later out_r stays <= the ordinal r at that position, preserving
9237
9359
  both the ascending order and (via the order() method's
9238
- (n-1)-out_r descending transform) the descending order. NaN
9239
- cells never tie with each other under dense (IEEE754 x != x). */
9360
+ descending transform) the descending order. NaN cells tie
9361
+ with each other, as sort_addr and the value-hash family treat
9362
+ every NaN as one value. */
9240
9363
  ca_size_t out_r = 0;
9241
9364
  for ( ca_size_t r = 0; r < k_unmasked; r++ ) {
9242
9365
  if ( r > 0 ) {
@@ -9248,14 +9371,25 @@ rank_index_ki_native_i16 (VALUE self, CArray *ca, int axis, int do_stable, int d
9248
9371
  }
9249
9372
  *(ca_size_t *) (po + buf[r].i * ostride) = (ca_size_t) out_r;
9250
9373
  }
9374
+ if ( dense == 2 && k_unmasked > 0 ) {
9375
+ ca_size_t nan_start = k_unmasked;
9376
+ ca_size_t top = nan_start > 0
9377
+ ? *(ca_size_t *) (po + buf[nan_start - 1].i * ostride) : (ca_size_t) -1;
9378
+ for ( ca_size_t r = 0; r < k_unmasked; r++ ) {
9379
+ ca_size_t *o = (ca_size_t *) (po + buf[r].i * ostride);
9380
+ *o = ( r < nan_start ) ? top - *o : top + 1;
9381
+ }
9382
+ }
9251
9383
  /* Mirror input mask to output (= UNDEF rank for masked
9252
- cells). Write via co->mask->ptr directly with the
9253
- output slab offset, because the iter's mo isn't
9254
- populated in CA_KERNEL_WRITE mode (= reduce form's
9255
- op_mask pattern). */
9384
+ cells). Write via co->mask->ptr directly, because a
9385
+ mask written through a WRITE walk's cursor does not
9386
+ reach the array. co is a fresh row-major entity and
9387
+ the walk cuts its slabs from a buffer laid out the
9388
+ same way, so the slab's offset from st_out.alias_ptr
9389
+ is its offset in co. */
9256
9390
  if ( mi && co->mask ) {
9257
9391
  boolean8_t *omask_base = (boolean8_t *) co->mask->ptr
9258
- + ((po - (char *) co->ptr) / (ca_size_t) co->bytes);
9392
+ + ((po - st_out.alias_ptr) / (ca_size_t) co->bytes);
9259
9393
  ca_size_t mostride = st_out.slab_mask_strides[0];
9260
9394
  for ( ca_size_t k = 0; k < fiber_n; k++ ) {
9261
9395
  if ( mi[k * mstride] ) omask_base[k * mostride] = 1;
@@ -9263,8 +9397,8 @@ rank_index_ki_native_i16 (VALUE self, CArray *ca, int axis, int do_stable, int d
9263
9397
  }
9264
9398
  ca_iter_state_sync_slab(&st_out);
9265
9399
  }
9266
- xfree(buf);
9267
- if ( aux ) xfree(aux);
9400
+ ALLOCV_END(hbuf);
9401
+ ALLOCV_END(haux);
9268
9402
  ca_iter_state_finish(&st_in);
9269
9403
  ca_iter_state_finish(&st_out);
9270
9404
  return vout;
@@ -9298,21 +9432,21 @@ rank_index_ki_native_u16 (VALUE self, CArray *ca, int axis, int do_stable, int d
9298
9432
  rc = ca_iter_state_init_l2(&st_in, ca, CA_SLAB_AXES,
9299
9433
  slab_axes, 1, 0);
9300
9434
  if ( rc != CA_ITER_OK ) {
9301
- rb_raise(rb_eRuntimeError, "rank_index_ki: input init failed rc=%d", rc);
9435
+ rb_raise(rb_eRuntimeError, "%s: input init failed rc=%d", ca_calling_method_name(), rc);
9302
9436
  }
9303
- rc = ca_iter_state_init_l2(&st_out, co, CA_SLAB_AXES,
9437
+ rc = ca_iter_state_init_l2_paired(&st_out, &st_in, co, CA_SLAB_AXES,
9304
9438
  slab_axes, 1, CA_KERNEL_WRITE);
9305
9439
  if ( rc != CA_ITER_OK ) {
9306
9440
  ca_iter_state_finish(&st_in);
9307
- rb_raise(rb_eRuntimeError, "rank_index_ki: output init failed rc=%d", rc);
9441
+ rb_raise(rb_eRuntimeError, "%s: output init failed rc=%d", ca_calling_method_name(), rc);
9308
9442
  }
9309
9443
 
9310
9444
  ca_size_t fiber_n = st_in.slab_elements;
9311
- rank_index_pair_u16 *buf =
9312
- xmalloc(sizeof(rank_index_pair_u16) * (size_t) fiber_n);
9445
+ volatile VALUE hbuf = 0, haux = 0;
9446
+ rank_index_pair_u16 *buf = ALLOCV_N(rank_index_pair_u16, hbuf, fiber_n);
9313
9447
  rank_index_pair_u16 *aux = NULL;
9314
9448
  if ( do_stable ) {
9315
- aux = xmalloc(sizeof(rank_index_pair_u16) * (size_t) fiber_n);
9449
+ aux = ALLOCV_N(rank_index_pair_u16, haux, fiber_n);
9316
9450
  }
9317
9451
 
9318
9452
  char *pi, *po;
@@ -9354,8 +9488,9 @@ rank_index_ki_native_u16 (VALUE self, CArray *ca, int axis, int do_stable, int d
9354
9488
  gaps ordinal would have spent on the tie are never used, so a
9355
9489
  later out_r stays <= the ordinal r at that position, preserving
9356
9490
  both the ascending order and (via the order() method's
9357
- (n-1)-out_r descending transform) the descending order. NaN
9358
- cells never tie with each other under dense (IEEE754 x != x). */
9491
+ descending transform) the descending order. NaN cells tie
9492
+ with each other, as sort_addr and the value-hash family treat
9493
+ every NaN as one value. */
9359
9494
  ca_size_t out_r = 0;
9360
9495
  for ( ca_size_t r = 0; r < k_unmasked; r++ ) {
9361
9496
  if ( r > 0 ) {
@@ -9367,14 +9502,25 @@ rank_index_ki_native_u16 (VALUE self, CArray *ca, int axis, int do_stable, int d
9367
9502
  }
9368
9503
  *(ca_size_t *) (po + buf[r].i * ostride) = (ca_size_t) out_r;
9369
9504
  }
9505
+ if ( dense == 2 && k_unmasked > 0 ) {
9506
+ ca_size_t nan_start = k_unmasked;
9507
+ ca_size_t top = nan_start > 0
9508
+ ? *(ca_size_t *) (po + buf[nan_start - 1].i * ostride) : (ca_size_t) -1;
9509
+ for ( ca_size_t r = 0; r < k_unmasked; r++ ) {
9510
+ ca_size_t *o = (ca_size_t *) (po + buf[r].i * ostride);
9511
+ *o = ( r < nan_start ) ? top - *o : top + 1;
9512
+ }
9513
+ }
9370
9514
  /* Mirror input mask to output (= UNDEF rank for masked
9371
- cells). Write via co->mask->ptr directly with the
9372
- output slab offset, because the iter's mo isn't
9373
- populated in CA_KERNEL_WRITE mode (= reduce form's
9374
- op_mask pattern). */
9515
+ cells). Write via co->mask->ptr directly, because a
9516
+ mask written through a WRITE walk's cursor does not
9517
+ reach the array. co is a fresh row-major entity and
9518
+ the walk cuts its slabs from a buffer laid out the
9519
+ same way, so the slab's offset from st_out.alias_ptr
9520
+ is its offset in co. */
9375
9521
  if ( mi && co->mask ) {
9376
9522
  boolean8_t *omask_base = (boolean8_t *) co->mask->ptr
9377
- + ((po - (char *) co->ptr) / (ca_size_t) co->bytes);
9523
+ + ((po - st_out.alias_ptr) / (ca_size_t) co->bytes);
9378
9524
  ca_size_t mostride = st_out.slab_mask_strides[0];
9379
9525
  for ( ca_size_t k = 0; k < fiber_n; k++ ) {
9380
9526
  if ( mi[k * mstride] ) omask_base[k * mostride] = 1;
@@ -9382,8 +9528,8 @@ rank_index_ki_native_u16 (VALUE self, CArray *ca, int axis, int do_stable, int d
9382
9528
  }
9383
9529
  ca_iter_state_sync_slab(&st_out);
9384
9530
  }
9385
- xfree(buf);
9386
- if ( aux ) xfree(aux);
9531
+ ALLOCV_END(hbuf);
9532
+ ALLOCV_END(haux);
9387
9533
  ca_iter_state_finish(&st_in);
9388
9534
  ca_iter_state_finish(&st_out);
9389
9535
  return vout;
@@ -9417,21 +9563,21 @@ rank_index_ki_native_i32 (VALUE self, CArray *ca, int axis, int do_stable, int d
9417
9563
  rc = ca_iter_state_init_l2(&st_in, ca, CA_SLAB_AXES,
9418
9564
  slab_axes, 1, 0);
9419
9565
  if ( rc != CA_ITER_OK ) {
9420
- rb_raise(rb_eRuntimeError, "rank_index_ki: input init failed rc=%d", rc);
9566
+ rb_raise(rb_eRuntimeError, "%s: input init failed rc=%d", ca_calling_method_name(), rc);
9421
9567
  }
9422
- rc = ca_iter_state_init_l2(&st_out, co, CA_SLAB_AXES,
9568
+ rc = ca_iter_state_init_l2_paired(&st_out, &st_in, co, CA_SLAB_AXES,
9423
9569
  slab_axes, 1, CA_KERNEL_WRITE);
9424
9570
  if ( rc != CA_ITER_OK ) {
9425
9571
  ca_iter_state_finish(&st_in);
9426
- rb_raise(rb_eRuntimeError, "rank_index_ki: output init failed rc=%d", rc);
9572
+ rb_raise(rb_eRuntimeError, "%s: output init failed rc=%d", ca_calling_method_name(), rc);
9427
9573
  }
9428
9574
 
9429
9575
  ca_size_t fiber_n = st_in.slab_elements;
9430
- rank_index_pair_i32 *buf =
9431
- xmalloc(sizeof(rank_index_pair_i32) * (size_t) fiber_n);
9576
+ volatile VALUE hbuf = 0, haux = 0;
9577
+ rank_index_pair_i32 *buf = ALLOCV_N(rank_index_pair_i32, hbuf, fiber_n);
9432
9578
  rank_index_pair_i32 *aux = NULL;
9433
9579
  if ( do_stable ) {
9434
- aux = xmalloc(sizeof(rank_index_pair_i32) * (size_t) fiber_n);
9580
+ aux = ALLOCV_N(rank_index_pair_i32, haux, fiber_n);
9435
9581
  }
9436
9582
 
9437
9583
  char *pi, *po;
@@ -9473,8 +9619,9 @@ rank_index_ki_native_i32 (VALUE self, CArray *ca, int axis, int do_stable, int d
9473
9619
  gaps ordinal would have spent on the tie are never used, so a
9474
9620
  later out_r stays <= the ordinal r at that position, preserving
9475
9621
  both the ascending order and (via the order() method's
9476
- (n-1)-out_r descending transform) the descending order. NaN
9477
- cells never tie with each other under dense (IEEE754 x != x). */
9622
+ descending transform) the descending order. NaN cells tie
9623
+ with each other, as sort_addr and the value-hash family treat
9624
+ every NaN as one value. */
9478
9625
  ca_size_t out_r = 0;
9479
9626
  for ( ca_size_t r = 0; r < k_unmasked; r++ ) {
9480
9627
  if ( r > 0 ) {
@@ -9486,14 +9633,25 @@ rank_index_ki_native_i32 (VALUE self, CArray *ca, int axis, int do_stable, int d
9486
9633
  }
9487
9634
  *(ca_size_t *) (po + buf[r].i * ostride) = (ca_size_t) out_r;
9488
9635
  }
9636
+ if ( dense == 2 && k_unmasked > 0 ) {
9637
+ ca_size_t nan_start = k_unmasked;
9638
+ ca_size_t top = nan_start > 0
9639
+ ? *(ca_size_t *) (po + buf[nan_start - 1].i * ostride) : (ca_size_t) -1;
9640
+ for ( ca_size_t r = 0; r < k_unmasked; r++ ) {
9641
+ ca_size_t *o = (ca_size_t *) (po + buf[r].i * ostride);
9642
+ *o = ( r < nan_start ) ? top - *o : top + 1;
9643
+ }
9644
+ }
9489
9645
  /* Mirror input mask to output (= UNDEF rank for masked
9490
- cells). Write via co->mask->ptr directly with the
9491
- output slab offset, because the iter's mo isn't
9492
- populated in CA_KERNEL_WRITE mode (= reduce form's
9493
- op_mask pattern). */
9646
+ cells). Write via co->mask->ptr directly, because a
9647
+ mask written through a WRITE walk's cursor does not
9648
+ reach the array. co is a fresh row-major entity and
9649
+ the walk cuts its slabs from a buffer laid out the
9650
+ same way, so the slab's offset from st_out.alias_ptr
9651
+ is its offset in co. */
9494
9652
  if ( mi && co->mask ) {
9495
9653
  boolean8_t *omask_base = (boolean8_t *) co->mask->ptr
9496
- + ((po - (char *) co->ptr) / (ca_size_t) co->bytes);
9654
+ + ((po - st_out.alias_ptr) / (ca_size_t) co->bytes);
9497
9655
  ca_size_t mostride = st_out.slab_mask_strides[0];
9498
9656
  for ( ca_size_t k = 0; k < fiber_n; k++ ) {
9499
9657
  if ( mi[k * mstride] ) omask_base[k * mostride] = 1;
@@ -9501,8 +9659,8 @@ rank_index_ki_native_i32 (VALUE self, CArray *ca, int axis, int do_stable, int d
9501
9659
  }
9502
9660
  ca_iter_state_sync_slab(&st_out);
9503
9661
  }
9504
- xfree(buf);
9505
- if ( aux ) xfree(aux);
9662
+ ALLOCV_END(hbuf);
9663
+ ALLOCV_END(haux);
9506
9664
  ca_iter_state_finish(&st_in);
9507
9665
  ca_iter_state_finish(&st_out);
9508
9666
  return vout;
@@ -9536,21 +9694,21 @@ rank_index_ki_native_u32 (VALUE self, CArray *ca, int axis, int do_stable, int d
9536
9694
  rc = ca_iter_state_init_l2(&st_in, ca, CA_SLAB_AXES,
9537
9695
  slab_axes, 1, 0);
9538
9696
  if ( rc != CA_ITER_OK ) {
9539
- rb_raise(rb_eRuntimeError, "rank_index_ki: input init failed rc=%d", rc);
9697
+ rb_raise(rb_eRuntimeError, "%s: input init failed rc=%d", ca_calling_method_name(), rc);
9540
9698
  }
9541
- rc = ca_iter_state_init_l2(&st_out, co, CA_SLAB_AXES,
9699
+ rc = ca_iter_state_init_l2_paired(&st_out, &st_in, co, CA_SLAB_AXES,
9542
9700
  slab_axes, 1, CA_KERNEL_WRITE);
9543
9701
  if ( rc != CA_ITER_OK ) {
9544
9702
  ca_iter_state_finish(&st_in);
9545
- rb_raise(rb_eRuntimeError, "rank_index_ki: output init failed rc=%d", rc);
9703
+ rb_raise(rb_eRuntimeError, "%s: output init failed rc=%d", ca_calling_method_name(), rc);
9546
9704
  }
9547
9705
 
9548
9706
  ca_size_t fiber_n = st_in.slab_elements;
9549
- rank_index_pair_u32 *buf =
9550
- xmalloc(sizeof(rank_index_pair_u32) * (size_t) fiber_n);
9707
+ volatile VALUE hbuf = 0, haux = 0;
9708
+ rank_index_pair_u32 *buf = ALLOCV_N(rank_index_pair_u32, hbuf, fiber_n);
9551
9709
  rank_index_pair_u32 *aux = NULL;
9552
9710
  if ( do_stable ) {
9553
- aux = xmalloc(sizeof(rank_index_pair_u32) * (size_t) fiber_n);
9711
+ aux = ALLOCV_N(rank_index_pair_u32, haux, fiber_n);
9554
9712
  }
9555
9713
 
9556
9714
  char *pi, *po;
@@ -9592,8 +9750,9 @@ rank_index_ki_native_u32 (VALUE self, CArray *ca, int axis, int do_stable, int d
9592
9750
  gaps ordinal would have spent on the tie are never used, so a
9593
9751
  later out_r stays <= the ordinal r at that position, preserving
9594
9752
  both the ascending order and (via the order() method's
9595
- (n-1)-out_r descending transform) the descending order. NaN
9596
- cells never tie with each other under dense (IEEE754 x != x). */
9753
+ descending transform) the descending order. NaN cells tie
9754
+ with each other, as sort_addr and the value-hash family treat
9755
+ every NaN as one value. */
9597
9756
  ca_size_t out_r = 0;
9598
9757
  for ( ca_size_t r = 0; r < k_unmasked; r++ ) {
9599
9758
  if ( r > 0 ) {
@@ -9605,14 +9764,25 @@ rank_index_ki_native_u32 (VALUE self, CArray *ca, int axis, int do_stable, int d
9605
9764
  }
9606
9765
  *(ca_size_t *) (po + buf[r].i * ostride) = (ca_size_t) out_r;
9607
9766
  }
9767
+ if ( dense == 2 && k_unmasked > 0 ) {
9768
+ ca_size_t nan_start = k_unmasked;
9769
+ ca_size_t top = nan_start > 0
9770
+ ? *(ca_size_t *) (po + buf[nan_start - 1].i * ostride) : (ca_size_t) -1;
9771
+ for ( ca_size_t r = 0; r < k_unmasked; r++ ) {
9772
+ ca_size_t *o = (ca_size_t *) (po + buf[r].i * ostride);
9773
+ *o = ( r < nan_start ) ? top - *o : top + 1;
9774
+ }
9775
+ }
9608
9776
  /* Mirror input mask to output (= UNDEF rank for masked
9609
- cells). Write via co->mask->ptr directly with the
9610
- output slab offset, because the iter's mo isn't
9611
- populated in CA_KERNEL_WRITE mode (= reduce form's
9612
- op_mask pattern). */
9777
+ cells). Write via co->mask->ptr directly, because a
9778
+ mask written through a WRITE walk's cursor does not
9779
+ reach the array. co is a fresh row-major entity and
9780
+ the walk cuts its slabs from a buffer laid out the
9781
+ same way, so the slab's offset from st_out.alias_ptr
9782
+ is its offset in co. */
9613
9783
  if ( mi && co->mask ) {
9614
9784
  boolean8_t *omask_base = (boolean8_t *) co->mask->ptr
9615
- + ((po - (char *) co->ptr) / (ca_size_t) co->bytes);
9785
+ + ((po - st_out.alias_ptr) / (ca_size_t) co->bytes);
9616
9786
  ca_size_t mostride = st_out.slab_mask_strides[0];
9617
9787
  for ( ca_size_t k = 0; k < fiber_n; k++ ) {
9618
9788
  if ( mi[k * mstride] ) omask_base[k * mostride] = 1;
@@ -9620,8 +9790,8 @@ rank_index_ki_native_u32 (VALUE self, CArray *ca, int axis, int do_stable, int d
9620
9790
  }
9621
9791
  ca_iter_state_sync_slab(&st_out);
9622
9792
  }
9623
- xfree(buf);
9624
- if ( aux ) xfree(aux);
9793
+ ALLOCV_END(hbuf);
9794
+ ALLOCV_END(haux);
9625
9795
  ca_iter_state_finish(&st_in);
9626
9796
  ca_iter_state_finish(&st_out);
9627
9797
  return vout;
@@ -9655,21 +9825,21 @@ rank_index_ki_native_i64 (VALUE self, CArray *ca, int axis, int do_stable, int d
9655
9825
  rc = ca_iter_state_init_l2(&st_in, ca, CA_SLAB_AXES,
9656
9826
  slab_axes, 1, 0);
9657
9827
  if ( rc != CA_ITER_OK ) {
9658
- rb_raise(rb_eRuntimeError, "rank_index_ki: input init failed rc=%d", rc);
9828
+ rb_raise(rb_eRuntimeError, "%s: input init failed rc=%d", ca_calling_method_name(), rc);
9659
9829
  }
9660
- rc = ca_iter_state_init_l2(&st_out, co, CA_SLAB_AXES,
9830
+ rc = ca_iter_state_init_l2_paired(&st_out, &st_in, co, CA_SLAB_AXES,
9661
9831
  slab_axes, 1, CA_KERNEL_WRITE);
9662
9832
  if ( rc != CA_ITER_OK ) {
9663
9833
  ca_iter_state_finish(&st_in);
9664
- rb_raise(rb_eRuntimeError, "rank_index_ki: output init failed rc=%d", rc);
9834
+ rb_raise(rb_eRuntimeError, "%s: output init failed rc=%d", ca_calling_method_name(), rc);
9665
9835
  }
9666
9836
 
9667
9837
  ca_size_t fiber_n = st_in.slab_elements;
9668
- rank_index_pair_i64 *buf =
9669
- xmalloc(sizeof(rank_index_pair_i64) * (size_t) fiber_n);
9838
+ volatile VALUE hbuf = 0, haux = 0;
9839
+ rank_index_pair_i64 *buf = ALLOCV_N(rank_index_pair_i64, hbuf, fiber_n);
9670
9840
  rank_index_pair_i64 *aux = NULL;
9671
9841
  if ( do_stable ) {
9672
- aux = xmalloc(sizeof(rank_index_pair_i64) * (size_t) fiber_n);
9842
+ aux = ALLOCV_N(rank_index_pair_i64, haux, fiber_n);
9673
9843
  }
9674
9844
 
9675
9845
  char *pi, *po;
@@ -9711,8 +9881,9 @@ rank_index_ki_native_i64 (VALUE self, CArray *ca, int axis, int do_stable, int d
9711
9881
  gaps ordinal would have spent on the tie are never used, so a
9712
9882
  later out_r stays <= the ordinal r at that position, preserving
9713
9883
  both the ascending order and (via the order() method's
9714
- (n-1)-out_r descending transform) the descending order. NaN
9715
- cells never tie with each other under dense (IEEE754 x != x). */
9884
+ descending transform) the descending order. NaN cells tie
9885
+ with each other, as sort_addr and the value-hash family treat
9886
+ every NaN as one value. */
9716
9887
  ca_size_t out_r = 0;
9717
9888
  for ( ca_size_t r = 0; r < k_unmasked; r++ ) {
9718
9889
  if ( r > 0 ) {
@@ -9724,14 +9895,25 @@ rank_index_ki_native_i64 (VALUE self, CArray *ca, int axis, int do_stable, int d
9724
9895
  }
9725
9896
  *(ca_size_t *) (po + buf[r].i * ostride) = (ca_size_t) out_r;
9726
9897
  }
9898
+ if ( dense == 2 && k_unmasked > 0 ) {
9899
+ ca_size_t nan_start = k_unmasked;
9900
+ ca_size_t top = nan_start > 0
9901
+ ? *(ca_size_t *) (po + buf[nan_start - 1].i * ostride) : (ca_size_t) -1;
9902
+ for ( ca_size_t r = 0; r < k_unmasked; r++ ) {
9903
+ ca_size_t *o = (ca_size_t *) (po + buf[r].i * ostride);
9904
+ *o = ( r < nan_start ) ? top - *o : top + 1;
9905
+ }
9906
+ }
9727
9907
  /* Mirror input mask to output (= UNDEF rank for masked
9728
- cells). Write via co->mask->ptr directly with the
9729
- output slab offset, because the iter's mo isn't
9730
- populated in CA_KERNEL_WRITE mode (= reduce form's
9731
- op_mask pattern). */
9908
+ cells). Write via co->mask->ptr directly, because a
9909
+ mask written through a WRITE walk's cursor does not
9910
+ reach the array. co is a fresh row-major entity and
9911
+ the walk cuts its slabs from a buffer laid out the
9912
+ same way, so the slab's offset from st_out.alias_ptr
9913
+ is its offset in co. */
9732
9914
  if ( mi && co->mask ) {
9733
9915
  boolean8_t *omask_base = (boolean8_t *) co->mask->ptr
9734
- + ((po - (char *) co->ptr) / (ca_size_t) co->bytes);
9916
+ + ((po - st_out.alias_ptr) / (ca_size_t) co->bytes);
9735
9917
  ca_size_t mostride = st_out.slab_mask_strides[0];
9736
9918
  for ( ca_size_t k = 0; k < fiber_n; k++ ) {
9737
9919
  if ( mi[k * mstride] ) omask_base[k * mostride] = 1;
@@ -9739,8 +9921,8 @@ rank_index_ki_native_i64 (VALUE self, CArray *ca, int axis, int do_stable, int d
9739
9921
  }
9740
9922
  ca_iter_state_sync_slab(&st_out);
9741
9923
  }
9742
- xfree(buf);
9743
- if ( aux ) xfree(aux);
9924
+ ALLOCV_END(hbuf);
9925
+ ALLOCV_END(haux);
9744
9926
  ca_iter_state_finish(&st_in);
9745
9927
  ca_iter_state_finish(&st_out);
9746
9928
  return vout;
@@ -9774,21 +9956,21 @@ rank_index_ki_native_u64 (VALUE self, CArray *ca, int axis, int do_stable, int d
9774
9956
  rc = ca_iter_state_init_l2(&st_in, ca, CA_SLAB_AXES,
9775
9957
  slab_axes, 1, 0);
9776
9958
  if ( rc != CA_ITER_OK ) {
9777
- rb_raise(rb_eRuntimeError, "rank_index_ki: input init failed rc=%d", rc);
9959
+ rb_raise(rb_eRuntimeError, "%s: input init failed rc=%d", ca_calling_method_name(), rc);
9778
9960
  }
9779
- rc = ca_iter_state_init_l2(&st_out, co, CA_SLAB_AXES,
9961
+ rc = ca_iter_state_init_l2_paired(&st_out, &st_in, co, CA_SLAB_AXES,
9780
9962
  slab_axes, 1, CA_KERNEL_WRITE);
9781
9963
  if ( rc != CA_ITER_OK ) {
9782
9964
  ca_iter_state_finish(&st_in);
9783
- rb_raise(rb_eRuntimeError, "rank_index_ki: output init failed rc=%d", rc);
9965
+ rb_raise(rb_eRuntimeError, "%s: output init failed rc=%d", ca_calling_method_name(), rc);
9784
9966
  }
9785
9967
 
9786
9968
  ca_size_t fiber_n = st_in.slab_elements;
9787
- rank_index_pair_u64 *buf =
9788
- xmalloc(sizeof(rank_index_pair_u64) * (size_t) fiber_n);
9969
+ volatile VALUE hbuf = 0, haux = 0;
9970
+ rank_index_pair_u64 *buf = ALLOCV_N(rank_index_pair_u64, hbuf, fiber_n);
9789
9971
  rank_index_pair_u64 *aux = NULL;
9790
9972
  if ( do_stable ) {
9791
- aux = xmalloc(sizeof(rank_index_pair_u64) * (size_t) fiber_n);
9973
+ aux = ALLOCV_N(rank_index_pair_u64, haux, fiber_n);
9792
9974
  }
9793
9975
 
9794
9976
  char *pi, *po;
@@ -9830,8 +10012,9 @@ rank_index_ki_native_u64 (VALUE self, CArray *ca, int axis, int do_stable, int d
9830
10012
  gaps ordinal would have spent on the tie are never used, so a
9831
10013
  later out_r stays <= the ordinal r at that position, preserving
9832
10014
  both the ascending order and (via the order() method's
9833
- (n-1)-out_r descending transform) the descending order. NaN
9834
- cells never tie with each other under dense (IEEE754 x != x). */
10015
+ descending transform) the descending order. NaN cells tie
10016
+ with each other, as sort_addr and the value-hash family treat
10017
+ every NaN as one value. */
9835
10018
  ca_size_t out_r = 0;
9836
10019
  for ( ca_size_t r = 0; r < k_unmasked; r++ ) {
9837
10020
  if ( r > 0 ) {
@@ -9843,14 +10026,25 @@ rank_index_ki_native_u64 (VALUE self, CArray *ca, int axis, int do_stable, int d
9843
10026
  }
9844
10027
  *(ca_size_t *) (po + buf[r].i * ostride) = (ca_size_t) out_r;
9845
10028
  }
10029
+ if ( dense == 2 && k_unmasked > 0 ) {
10030
+ ca_size_t nan_start = k_unmasked;
10031
+ ca_size_t top = nan_start > 0
10032
+ ? *(ca_size_t *) (po + buf[nan_start - 1].i * ostride) : (ca_size_t) -1;
10033
+ for ( ca_size_t r = 0; r < k_unmasked; r++ ) {
10034
+ ca_size_t *o = (ca_size_t *) (po + buf[r].i * ostride);
10035
+ *o = ( r < nan_start ) ? top - *o : top + 1;
10036
+ }
10037
+ }
9846
10038
  /* Mirror input mask to output (= UNDEF rank for masked
9847
- cells). Write via co->mask->ptr directly with the
9848
- output slab offset, because the iter's mo isn't
9849
- populated in CA_KERNEL_WRITE mode (= reduce form's
9850
- op_mask pattern). */
10039
+ cells). Write via co->mask->ptr directly, because a
10040
+ mask written through a WRITE walk's cursor does not
10041
+ reach the array. co is a fresh row-major entity and
10042
+ the walk cuts its slabs from a buffer laid out the
10043
+ same way, so the slab's offset from st_out.alias_ptr
10044
+ is its offset in co. */
9851
10045
  if ( mi && co->mask ) {
9852
10046
  boolean8_t *omask_base = (boolean8_t *) co->mask->ptr
9853
- + ((po - (char *) co->ptr) / (ca_size_t) co->bytes);
10047
+ + ((po - st_out.alias_ptr) / (ca_size_t) co->bytes);
9854
10048
  ca_size_t mostride = st_out.slab_mask_strides[0];
9855
10049
  for ( ca_size_t k = 0; k < fiber_n; k++ ) {
9856
10050
  if ( mi[k * mstride] ) omask_base[k * mostride] = 1;
@@ -9858,8 +10052,8 @@ rank_index_ki_native_u64 (VALUE self, CArray *ca, int axis, int do_stable, int d
9858
10052
  }
9859
10053
  ca_iter_state_sync_slab(&st_out);
9860
10054
  }
9861
- xfree(buf);
9862
- if ( aux ) xfree(aux);
10055
+ ALLOCV_END(hbuf);
10056
+ ALLOCV_END(haux);
9863
10057
  ca_iter_state_finish(&st_in);
9864
10058
  ca_iter_state_finish(&st_out);
9865
10059
  return vout;
@@ -9893,21 +10087,21 @@ rank_index_ki_native_f32 (VALUE self, CArray *ca, int axis, int do_stable, int d
9893
10087
  rc = ca_iter_state_init_l2(&st_in, ca, CA_SLAB_AXES,
9894
10088
  slab_axes, 1, 0);
9895
10089
  if ( rc != CA_ITER_OK ) {
9896
- rb_raise(rb_eRuntimeError, "rank_index_ki: input init failed rc=%d", rc);
10090
+ rb_raise(rb_eRuntimeError, "%s: input init failed rc=%d", ca_calling_method_name(), rc);
9897
10091
  }
9898
- rc = ca_iter_state_init_l2(&st_out, co, CA_SLAB_AXES,
10092
+ rc = ca_iter_state_init_l2_paired(&st_out, &st_in, co, CA_SLAB_AXES,
9899
10093
  slab_axes, 1, CA_KERNEL_WRITE);
9900
10094
  if ( rc != CA_ITER_OK ) {
9901
10095
  ca_iter_state_finish(&st_in);
9902
- rb_raise(rb_eRuntimeError, "rank_index_ki: output init failed rc=%d", rc);
10096
+ rb_raise(rb_eRuntimeError, "%s: output init failed rc=%d", ca_calling_method_name(), rc);
9903
10097
  }
9904
10098
 
9905
10099
  ca_size_t fiber_n = st_in.slab_elements;
9906
- rank_index_pair_f32 *buf =
9907
- xmalloc(sizeof(rank_index_pair_f32) * (size_t) fiber_n);
10100
+ volatile VALUE hbuf = 0, haux = 0;
10101
+ rank_index_pair_f32 *buf = ALLOCV_N(rank_index_pair_f32, hbuf, fiber_n);
9908
10102
  rank_index_pair_f32 *aux = NULL;
9909
10103
  if ( do_stable ) {
9910
- aux = xmalloc(sizeof(rank_index_pair_f32) * (size_t) fiber_n);
10104
+ aux = ALLOCV_N(rank_index_pair_f32, haux, fiber_n);
9911
10105
  }
9912
10106
 
9913
10107
  char *pi, *po;
@@ -9953,27 +10147,41 @@ rank_index_ki_native_f32 (VALUE self, CArray *ca, int axis, int do_stable, int d
9953
10147
  gaps ordinal would have spent on the tie are never used, so a
9954
10148
  later out_r stays <= the ordinal r at that position, preserving
9955
10149
  both the ascending order and (via the order() method's
9956
- (n-1)-out_r descending transform) the descending order. NaN
9957
- cells never tie with each other under dense (IEEE754 x != x). */
10150
+ descending transform) the descending order. NaN cells tie
10151
+ with each other, as sort_addr and the value-hash family treat
10152
+ every NaN as one value. */
9958
10153
  ca_size_t out_r = 0;
9959
10154
  for ( ca_size_t r = 0; r < k_unmasked; r++ ) {
9960
10155
  if ( r > 0 ) {
9961
10156
  if ( dense ) {
9962
- if ( buf[r].v != buf[r - 1].v ) out_r++;
10157
+ if ( buf[r].v != buf[r - 1].v
10158
+ && ! ( isnan(buf[r].v) && isnan(buf[r - 1].v) ) ) out_r++;
9963
10159
  } else {
9964
10160
  out_r = r;
9965
10161
  }
9966
10162
  }
9967
10163
  *(ca_size_t *) (po + buf[r].i * ostride) = (ca_size_t) out_r;
9968
10164
  }
10165
+ if ( dense == 2 && k_unmasked > 0 ) {
10166
+ ca_size_t nan_start = k_unmasked;
10167
+ while ( nan_start > 0 && isnan(buf[nan_start - 1].v) ) nan_start--;
10168
+ ca_size_t top = nan_start > 0
10169
+ ? *(ca_size_t *) (po + buf[nan_start - 1].i * ostride) : (ca_size_t) -1;
10170
+ for ( ca_size_t r = 0; r < k_unmasked; r++ ) {
10171
+ ca_size_t *o = (ca_size_t *) (po + buf[r].i * ostride);
10172
+ *o = ( r < nan_start ) ? top - *o : top + 1;
10173
+ }
10174
+ }
9969
10175
  /* Mirror input mask to output (= UNDEF rank for masked
9970
- cells). Write via co->mask->ptr directly with the
9971
- output slab offset, because the iter's mo isn't
9972
- populated in CA_KERNEL_WRITE mode (= reduce form's
9973
- op_mask pattern). */
10176
+ cells). Write via co->mask->ptr directly, because a
10177
+ mask written through a WRITE walk's cursor does not
10178
+ reach the array. co is a fresh row-major entity and
10179
+ the walk cuts its slabs from a buffer laid out the
10180
+ same way, so the slab's offset from st_out.alias_ptr
10181
+ is its offset in co. */
9974
10182
  if ( mi && co->mask ) {
9975
10183
  boolean8_t *omask_base = (boolean8_t *) co->mask->ptr
9976
- + ((po - (char *) co->ptr) / (ca_size_t) co->bytes);
10184
+ + ((po - st_out.alias_ptr) / (ca_size_t) co->bytes);
9977
10185
  ca_size_t mostride = st_out.slab_mask_strides[0];
9978
10186
  for ( ca_size_t k = 0; k < fiber_n; k++ ) {
9979
10187
  if ( mi[k * mstride] ) omask_base[k * mostride] = 1;
@@ -9981,8 +10189,8 @@ rank_index_ki_native_f32 (VALUE self, CArray *ca, int axis, int do_stable, int d
9981
10189
  }
9982
10190
  ca_iter_state_sync_slab(&st_out);
9983
10191
  }
9984
- xfree(buf);
9985
- if ( aux ) xfree(aux);
10192
+ ALLOCV_END(hbuf);
10193
+ ALLOCV_END(haux);
9986
10194
  ca_iter_state_finish(&st_in);
9987
10195
  ca_iter_state_finish(&st_out);
9988
10196
  return vout;
@@ -10016,21 +10224,21 @@ rank_index_ki_native_f64 (VALUE self, CArray *ca, int axis, int do_stable, int d
10016
10224
  rc = ca_iter_state_init_l2(&st_in, ca, CA_SLAB_AXES,
10017
10225
  slab_axes, 1, 0);
10018
10226
  if ( rc != CA_ITER_OK ) {
10019
- rb_raise(rb_eRuntimeError, "rank_index_ki: input init failed rc=%d", rc);
10227
+ rb_raise(rb_eRuntimeError, "%s: input init failed rc=%d", ca_calling_method_name(), rc);
10020
10228
  }
10021
- rc = ca_iter_state_init_l2(&st_out, co, CA_SLAB_AXES,
10229
+ rc = ca_iter_state_init_l2_paired(&st_out, &st_in, co, CA_SLAB_AXES,
10022
10230
  slab_axes, 1, CA_KERNEL_WRITE);
10023
10231
  if ( rc != CA_ITER_OK ) {
10024
10232
  ca_iter_state_finish(&st_in);
10025
- rb_raise(rb_eRuntimeError, "rank_index_ki: output init failed rc=%d", rc);
10233
+ rb_raise(rb_eRuntimeError, "%s: output init failed rc=%d", ca_calling_method_name(), rc);
10026
10234
  }
10027
10235
 
10028
10236
  ca_size_t fiber_n = st_in.slab_elements;
10029
- rank_index_pair_f64 *buf =
10030
- xmalloc(sizeof(rank_index_pair_f64) * (size_t) fiber_n);
10237
+ volatile VALUE hbuf = 0, haux = 0;
10238
+ rank_index_pair_f64 *buf = ALLOCV_N(rank_index_pair_f64, hbuf, fiber_n);
10031
10239
  rank_index_pair_f64 *aux = NULL;
10032
10240
  if ( do_stable ) {
10033
- aux = xmalloc(sizeof(rank_index_pair_f64) * (size_t) fiber_n);
10241
+ aux = ALLOCV_N(rank_index_pair_f64, haux, fiber_n);
10034
10242
  }
10035
10243
 
10036
10244
  char *pi, *po;
@@ -10076,27 +10284,41 @@ rank_index_ki_native_f64 (VALUE self, CArray *ca, int axis, int do_stable, int d
10076
10284
  gaps ordinal would have spent on the tie are never used, so a
10077
10285
  later out_r stays <= the ordinal r at that position, preserving
10078
10286
  both the ascending order and (via the order() method's
10079
- (n-1)-out_r descending transform) the descending order. NaN
10080
- cells never tie with each other under dense (IEEE754 x != x). */
10287
+ descending transform) the descending order. NaN cells tie
10288
+ with each other, as sort_addr and the value-hash family treat
10289
+ every NaN as one value. */
10081
10290
  ca_size_t out_r = 0;
10082
10291
  for ( ca_size_t r = 0; r < k_unmasked; r++ ) {
10083
10292
  if ( r > 0 ) {
10084
10293
  if ( dense ) {
10085
- if ( buf[r].v != buf[r - 1].v ) out_r++;
10294
+ if ( buf[r].v != buf[r - 1].v
10295
+ && ! ( isnan(buf[r].v) && isnan(buf[r - 1].v) ) ) out_r++;
10086
10296
  } else {
10087
10297
  out_r = r;
10088
10298
  }
10089
10299
  }
10090
10300
  *(ca_size_t *) (po + buf[r].i * ostride) = (ca_size_t) out_r;
10091
10301
  }
10302
+ if ( dense == 2 && k_unmasked > 0 ) {
10303
+ ca_size_t nan_start = k_unmasked;
10304
+ while ( nan_start > 0 && isnan(buf[nan_start - 1].v) ) nan_start--;
10305
+ ca_size_t top = nan_start > 0
10306
+ ? *(ca_size_t *) (po + buf[nan_start - 1].i * ostride) : (ca_size_t) -1;
10307
+ for ( ca_size_t r = 0; r < k_unmasked; r++ ) {
10308
+ ca_size_t *o = (ca_size_t *) (po + buf[r].i * ostride);
10309
+ *o = ( r < nan_start ) ? top - *o : top + 1;
10310
+ }
10311
+ }
10092
10312
  /* Mirror input mask to output (= UNDEF rank for masked
10093
- cells). Write via co->mask->ptr directly with the
10094
- output slab offset, because the iter's mo isn't
10095
- populated in CA_KERNEL_WRITE mode (= reduce form's
10096
- op_mask pattern). */
10313
+ cells). Write via co->mask->ptr directly, because a
10314
+ mask written through a WRITE walk's cursor does not
10315
+ reach the array. co is a fresh row-major entity and
10316
+ the walk cuts its slabs from a buffer laid out the
10317
+ same way, so the slab's offset from st_out.alias_ptr
10318
+ is its offset in co. */
10097
10319
  if ( mi && co->mask ) {
10098
10320
  boolean8_t *omask_base = (boolean8_t *) co->mask->ptr
10099
- + ((po - (char *) co->ptr) / (ca_size_t) co->bytes);
10321
+ + ((po - st_out.alias_ptr) / (ca_size_t) co->bytes);
10100
10322
  ca_size_t mostride = st_out.slab_mask_strides[0];
10101
10323
  for ( ca_size_t k = 0; k < fiber_n; k++ ) {
10102
10324
  if ( mi[k * mstride] ) omask_base[k * mostride] = 1;
@@ -10104,8 +10326,8 @@ rank_index_ki_native_f64 (VALUE self, CArray *ca, int axis, int do_stable, int d
10104
10326
  }
10105
10327
  ca_iter_state_sync_slab(&st_out);
10106
10328
  }
10107
- xfree(buf);
10108
- if ( aux ) xfree(aux);
10329
+ ALLOCV_END(hbuf);
10330
+ ALLOCV_END(haux);
10109
10331
  ca_iter_state_finish(&st_in);
10110
10332
  ca_iter_state_finish(&st_out);
10111
10333
  return vout;
@@ -10139,21 +10361,21 @@ rank_index_ki_native_bool (VALUE self, CArray *ca, int axis, int do_stable, int
10139
10361
  rc = ca_iter_state_init_l2(&st_in, ca, CA_SLAB_AXES,
10140
10362
  slab_axes, 1, 0);
10141
10363
  if ( rc != CA_ITER_OK ) {
10142
- rb_raise(rb_eRuntimeError, "rank_index_ki: input init failed rc=%d", rc);
10364
+ rb_raise(rb_eRuntimeError, "%s: input init failed rc=%d", ca_calling_method_name(), rc);
10143
10365
  }
10144
- rc = ca_iter_state_init_l2(&st_out, co, CA_SLAB_AXES,
10366
+ rc = ca_iter_state_init_l2_paired(&st_out, &st_in, co, CA_SLAB_AXES,
10145
10367
  slab_axes, 1, CA_KERNEL_WRITE);
10146
10368
  if ( rc != CA_ITER_OK ) {
10147
10369
  ca_iter_state_finish(&st_in);
10148
- rb_raise(rb_eRuntimeError, "rank_index_ki: output init failed rc=%d", rc);
10370
+ rb_raise(rb_eRuntimeError, "%s: output init failed rc=%d", ca_calling_method_name(), rc);
10149
10371
  }
10150
10372
 
10151
10373
  ca_size_t fiber_n = st_in.slab_elements;
10152
- rank_index_pair_bool *buf =
10153
- xmalloc(sizeof(rank_index_pair_bool) * (size_t) fiber_n);
10374
+ volatile VALUE hbuf = 0, haux = 0;
10375
+ rank_index_pair_bool *buf = ALLOCV_N(rank_index_pair_bool, hbuf, fiber_n);
10154
10376
  rank_index_pair_bool *aux = NULL;
10155
10377
  if ( do_stable ) {
10156
- aux = xmalloc(sizeof(rank_index_pair_bool) * (size_t) fiber_n);
10378
+ aux = ALLOCV_N(rank_index_pair_bool, haux, fiber_n);
10157
10379
  }
10158
10380
 
10159
10381
  char *pi, *po;
@@ -10195,8 +10417,9 @@ rank_index_ki_native_bool (VALUE self, CArray *ca, int axis, int do_stable, int
10195
10417
  gaps ordinal would have spent on the tie are never used, so a
10196
10418
  later out_r stays <= the ordinal r at that position, preserving
10197
10419
  both the ascending order and (via the order() method's
10198
- (n-1)-out_r descending transform) the descending order. NaN
10199
- cells never tie with each other under dense (IEEE754 x != x). */
10420
+ descending transform) the descending order. NaN cells tie
10421
+ with each other, as sort_addr and the value-hash family treat
10422
+ every NaN as one value. */
10200
10423
  ca_size_t out_r = 0;
10201
10424
  for ( ca_size_t r = 0; r < k_unmasked; r++ ) {
10202
10425
  if ( r > 0 ) {
@@ -10208,14 +10431,25 @@ rank_index_ki_native_bool (VALUE self, CArray *ca, int axis, int do_stable, int
10208
10431
  }
10209
10432
  *(ca_size_t *) (po + buf[r].i * ostride) = (ca_size_t) out_r;
10210
10433
  }
10434
+ if ( dense == 2 && k_unmasked > 0 ) {
10435
+ ca_size_t nan_start = k_unmasked;
10436
+ ca_size_t top = nan_start > 0
10437
+ ? *(ca_size_t *) (po + buf[nan_start - 1].i * ostride) : (ca_size_t) -1;
10438
+ for ( ca_size_t r = 0; r < k_unmasked; r++ ) {
10439
+ ca_size_t *o = (ca_size_t *) (po + buf[r].i * ostride);
10440
+ *o = ( r < nan_start ) ? top - *o : top + 1;
10441
+ }
10442
+ }
10211
10443
  /* Mirror input mask to output (= UNDEF rank for masked
10212
- cells). Write via co->mask->ptr directly with the
10213
- output slab offset, because the iter's mo isn't
10214
- populated in CA_KERNEL_WRITE mode (= reduce form's
10215
- op_mask pattern). */
10444
+ cells). Write via co->mask->ptr directly, because a
10445
+ mask written through a WRITE walk's cursor does not
10446
+ reach the array. co is a fresh row-major entity and
10447
+ the walk cuts its slabs from a buffer laid out the
10448
+ same way, so the slab's offset from st_out.alias_ptr
10449
+ is its offset in co. */
10216
10450
  if ( mi && co->mask ) {
10217
10451
  boolean8_t *omask_base = (boolean8_t *) co->mask->ptr
10218
- + ((po - (char *) co->ptr) / (ca_size_t) co->bytes);
10452
+ + ((po - st_out.alias_ptr) / (ca_size_t) co->bytes);
10219
10453
  ca_size_t mostride = st_out.slab_mask_strides[0];
10220
10454
  for ( ca_size_t k = 0; k < fiber_n; k++ ) {
10221
10455
  if ( mi[k * mstride] ) omask_base[k * mostride] = 1;
@@ -10223,8 +10457,8 @@ rank_index_ki_native_bool (VALUE self, CArray *ca, int axis, int do_stable, int
10223
10457
  }
10224
10458
  ca_iter_state_sync_slab(&st_out);
10225
10459
  }
10226
- xfree(buf);
10227
- if ( aux ) xfree(aux);
10460
+ ALLOCV_END(hbuf);
10461
+ ALLOCV_END(haux);
10228
10462
  ca_iter_state_finish(&st_in);
10229
10463
  ca_iter_state_finish(&st_out);
10230
10464
  return vout;
@@ -10240,23 +10474,42 @@ rank_index_cmp_object (const void *a, const void *b)
10240
10474
  {
10241
10475
  const rank_index_pair_object *pa = (const rank_index_pair_object *) a;
10242
10476
  const rank_index_pair_object *pb = (const rank_index_pair_object *) b;
10243
- VALUE r = rb_funcall(pa->v, rb_intern("<=>"), 1, pb->v);
10244
- if ( NIL_P(r) ) {
10245
- rb_raise(rb_eArgError, "rank_index_ki: comparison of %s with %s failed",
10246
- rb_obj_classname(pa->v), rb_obj_classname(pb->v));
10247
- }
10248
- int c = NUM2INT(r);
10249
- if ( c > 0 ) return 1;
10250
- if ( c < 0 ) return -1;
10477
+ int c = ca_obj_order_cmp(pa->v, pb->v);
10478
+ if ( c != 0 ) return c;
10251
10479
  /* Stable tie-break by original fiber-local index. */
10252
10480
  if ( pa->i < pb->i ) return -1;
10253
10481
  if ( pa->i > pb->i ) return 1;
10254
10482
  return 0;
10255
10483
  }
10256
10484
 
10485
+ /* The object lane calls Ruby for every cell, so a walk can raise part
10486
+ way. Its states live in the caller's frame, and ca_iter_ensure
10487
+ finishes them however the body leaves. */
10488
+ typedef struct {
10489
+ ca_iter_state states[2];
10490
+ VALUE self;
10491
+ CArray *ca;
10492
+ int axis;
10493
+ int do_stable;
10494
+ int dense;
10495
+ } rank_index_ki_native_object_frame;
10496
+
10257
10497
  static VALUE
10258
- rank_index_ki_native_object (VALUE self, CArray *ca, int axis, int do_stable, int dense)
10259
- {
10498
+ rank_index_ki_native_object_body (VALUE arg)
10499
+ {
10500
+ rank_index_ki_native_object_frame *fr = (rank_index_ki_native_object_frame *) arg;
10501
+ VALUE self = fr->self;
10502
+ (void) self;
10503
+ CArray *ca = fr->ca;
10504
+ (void) ca;
10505
+ int axis = fr->axis;
10506
+ (void) axis;
10507
+ int do_stable = fr->do_stable;
10508
+ (void) do_stable;
10509
+ int dense = fr->dense;
10510
+ (void) dense;
10511
+ #define st_in (fr->states[0])
10512
+ #define st_out (fr->states[1])
10260
10513
  VALUE vout = rb_ca_template_with_type(self, INT2NUM(CA_SIZE),
10261
10514
  INT2NUM(0));
10262
10515
  CArray *co;
@@ -10272,27 +10525,23 @@ rank_index_ki_native_object (VALUE self, CArray *ca, int axis, int do_stable, in
10272
10525
  next_slab_axes will then be non-NULL). Created up-front
10273
10526
  when input has any mask. */
10274
10527
  if ( ca_has_mask(ca) ) ca_create_mask(co);
10275
- ca_iter_state st_in, st_out;
10276
10528
  int rc;
10277
10529
  rc = ca_iter_state_init_l2(&st_in, ca, CA_SLAB_AXES,
10278
10530
  slab_axes, 1, 0);
10279
10531
  if ( rc != CA_ITER_OK ) {
10280
- rb_raise(rb_eRuntimeError, "rank_index_ki: input init failed rc=%d", rc);
10532
+ rb_raise(rb_eRuntimeError, "%s: input init failed rc=%d", ca_calling_method_name(), rc);
10281
10533
  }
10282
- rc = ca_iter_state_init_l2(&st_out, co, CA_SLAB_AXES,
10534
+ rc = ca_iter_state_init_l2_paired(&st_out, &st_in, co, CA_SLAB_AXES,
10283
10535
  slab_axes, 1, CA_KERNEL_WRITE);
10284
10536
  if ( rc != CA_ITER_OK ) {
10285
10537
  ca_iter_state_finish(&st_in);
10286
- rb_raise(rb_eRuntimeError, "rank_index_ki: output init failed rc=%d", rc);
10538
+ rb_raise(rb_eRuntimeError, "%s: output init failed rc=%d", ca_calling_method_name(), rc);
10287
10539
  }
10288
10540
 
10289
10541
  ca_size_t fiber_n = st_in.slab_elements;
10290
- rank_index_pair_object *buf =
10291
- xmalloc(sizeof(rank_index_pair_object) * (size_t) fiber_n);
10292
- rank_index_pair_object *aux = NULL;
10293
- if ( do_stable ) {
10294
- aux = xmalloc(sizeof(rank_index_pair_object) * (size_t) fiber_n);
10295
- }
10542
+ volatile VALUE hbuf = 0, haux = 0;
10543
+ rank_index_pair_object *buf = ALLOCV_N(rank_index_pair_object, hbuf, fiber_n);
10544
+ rank_index_pair_object *aux = ALLOCV_N(rank_index_pair_object, haux, fiber_n);
10296
10545
 
10297
10546
  char *pi, *po;
10298
10547
  boolean8_t *mi, *mo;
@@ -10317,8 +10566,8 @@ rank_index_ki_native_object (VALUE self, CArray *ca, int axis, int do_stable, in
10317
10566
  mergesort (= aux ping-pong, sorted-skip merge), do_stable: 0
10318
10567
  selects introsort with mergesort escape (= AC1 1.88x payoff).
10319
10568
  */
10320
- (void) do_stable; /* CA_OBJECT: qsort handles both stable / quick */
10321
- qsort(buf, (size_t) k_unmasked, sizeof(rank_index_pair_object), rank_index_cmp_object);
10569
+ (void) do_stable; /* CA_OBJECT: one stable sort serves both kinds */
10570
+ ca_sort_merge_pair_object((ca_pair_object *) buf, (ca_pair_object *) aux, (ca_size_t) k_unmasked, rank_index_cmp_object);
10322
10571
  /* Pre-clear output fiber to 0 (= sentinel at masked positions). */
10323
10572
  for ( ca_size_t k = 0; k < fiber_n; k++ ) {
10324
10573
  *(ca_size_t *) (po + k * ostride) = (ca_size_t) 0;
@@ -10330,27 +10579,41 @@ rank_index_ki_native_object (VALUE self, CArray *ca, int axis, int do_stable, in
10330
10579
  gaps ordinal would have spent on the tie are never used, so a
10331
10580
  later out_r stays <= the ordinal r at that position, preserving
10332
10581
  both the ascending order and (via the order() method's
10333
- (n-1)-out_r descending transform) the descending order. NaN
10334
- cells never tie with each other under dense (IEEE754 x != x). */
10582
+ descending transform) the descending order. NaN cells tie
10583
+ with each other, as sort_addr and the value-hash family treat
10584
+ every NaN as one value. */
10335
10585
  ca_size_t out_r = 0;
10336
10586
  for ( ca_size_t r = 0; r < k_unmasked; r++ ) {
10337
10587
  if ( r > 0 ) {
10338
10588
  if ( dense ) {
10339
- if ( ! rb_equal(buf[r].v, buf[r - 1].v) ) out_r++;
10589
+ if ( ! ( ( CA_OBJ_ISNAN(buf[r].v) && CA_OBJ_ISNAN(buf[r - 1].v) )
10590
+ || ca_obj_equal(buf[r].v, buf[r - 1].v) ) ) out_r++;
10340
10591
  } else {
10341
10592
  out_r = r;
10342
10593
  }
10343
10594
  }
10344
10595
  *(ca_size_t *) (po + buf[r].i * ostride) = (ca_size_t) out_r;
10345
10596
  }
10597
+ if ( dense == 2 && k_unmasked > 0 ) {
10598
+ ca_size_t nan_start = k_unmasked;
10599
+ while ( nan_start > 0 && CA_OBJ_ISNAN(buf[nan_start - 1].v) ) nan_start--;
10600
+ ca_size_t top = nan_start > 0
10601
+ ? *(ca_size_t *) (po + buf[nan_start - 1].i * ostride) : (ca_size_t) -1;
10602
+ for ( ca_size_t r = 0; r < k_unmasked; r++ ) {
10603
+ ca_size_t *o = (ca_size_t *) (po + buf[r].i * ostride);
10604
+ *o = ( r < nan_start ) ? top - *o : top + 1;
10605
+ }
10606
+ }
10346
10607
  /* Mirror input mask to output (= UNDEF rank for masked
10347
- cells). Write via co->mask->ptr directly with the
10348
- output slab offset, because the iter's mo isn't
10349
- populated in CA_KERNEL_WRITE mode (= reduce form's
10350
- op_mask pattern). */
10608
+ cells). Write via co->mask->ptr directly, because a
10609
+ mask written through a WRITE walk's cursor does not
10610
+ reach the array. co is a fresh row-major entity and
10611
+ the walk cuts its slabs from a buffer laid out the
10612
+ same way, so the slab's offset from st_out.alias_ptr
10613
+ is its offset in co. */
10351
10614
  if ( mi && co->mask ) {
10352
10615
  boolean8_t *omask_base = (boolean8_t *) co->mask->ptr
10353
- + ((po - (char *) co->ptr) / (ca_size_t) co->bytes);
10616
+ + ((po - st_out.alias_ptr) / (ca_size_t) co->bytes);
10354
10617
  ca_size_t mostride = st_out.slab_mask_strides[0];
10355
10618
  for ( ca_size_t k = 0; k < fiber_n; k++ ) {
10356
10619
  if ( mi[k * mstride] ) omask_base[k * mostride] = 1;
@@ -10358,13 +10621,28 @@ rank_index_ki_native_object (VALUE self, CArray *ca, int axis, int do_stable, in
10358
10621
  }
10359
10622
  ca_iter_state_sync_slab(&st_out);
10360
10623
  }
10361
- xfree(buf);
10362
- if ( aux ) xfree(aux);
10624
+ ALLOCV_END(hbuf);
10625
+ ALLOCV_END(haux);
10363
10626
  ca_iter_state_finish(&st_in);
10364
10627
  ca_iter_state_finish(&st_out);
10365
10628
  return vout;
10629
+ #undef st_in
10630
+ #undef st_out
10366
10631
  }
10367
10632
 
10633
+ static VALUE
10634
+ rank_index_ki_native_object (VALUE self, CArray *ca, int axis, int do_stable, int dense)
10635
+ {
10636
+ rank_index_ki_native_object_frame fr;
10637
+ fr.self = self;
10638
+ fr.ca = ca;
10639
+ fr.axis = axis;
10640
+ fr.do_stable = do_stable;
10641
+ fr.dense = dense;
10642
+ return ca_iter_ensure(2, fr.states, rank_index_ki_native_object_body, (VALUE) &fr);
10643
+ }
10644
+
10645
+
10368
10646
  typedef struct {
10369
10647
  char *vp; /* pointer to the fiber cell (nb bytes wide) */
10370
10648
  ca_size_t nb; /* element byte width (= ca->bytes) */
@@ -10407,21 +10685,21 @@ rank_index_ki_native_fixlen (VALUE self, CArray *ca, int axis, int do_stable, in
10407
10685
  rc = ca_iter_state_init_l2(&st_in, ca, CA_SLAB_AXES,
10408
10686
  slab_axes, 1, 0);
10409
10687
  if ( rc != CA_ITER_OK ) {
10410
- rb_raise(rb_eRuntimeError, "rank_index_ki: input init failed rc=%d", rc);
10688
+ rb_raise(rb_eRuntimeError, "%s: input init failed rc=%d", ca_calling_method_name(), rc);
10411
10689
  }
10412
- rc = ca_iter_state_init_l2(&st_out, co, CA_SLAB_AXES,
10690
+ rc = ca_iter_state_init_l2_paired(&st_out, &st_in, co, CA_SLAB_AXES,
10413
10691
  slab_axes, 1, CA_KERNEL_WRITE);
10414
10692
  if ( rc != CA_ITER_OK ) {
10415
10693
  ca_iter_state_finish(&st_in);
10416
- rb_raise(rb_eRuntimeError, "rank_index_ki: output init failed rc=%d", rc);
10694
+ rb_raise(rb_eRuntimeError, "%s: output init failed rc=%d", ca_calling_method_name(), rc);
10417
10695
  }
10418
10696
 
10419
10697
  ca_size_t fiber_n = st_in.slab_elements;
10420
- rank_index_pair_fixlen *buf =
10421
- xmalloc(sizeof(rank_index_pair_fixlen) * (size_t) fiber_n);
10698
+ volatile VALUE hbuf = 0, haux = 0;
10699
+ rank_index_pair_fixlen *buf = ALLOCV_N(rank_index_pair_fixlen, hbuf, fiber_n);
10422
10700
  rank_index_pair_fixlen *aux = NULL;
10423
10701
  if ( do_stable ) {
10424
- aux = xmalloc(sizeof(rank_index_pair_fixlen) * (size_t) fiber_n);
10702
+ aux = ALLOCV_N(rank_index_pair_fixlen, haux, fiber_n);
10425
10703
  }
10426
10704
 
10427
10705
  char *pi, *po;
@@ -10464,8 +10742,9 @@ rank_index_ki_native_fixlen (VALUE self, CArray *ca, int axis, int do_stable, in
10464
10742
  gaps ordinal would have spent on the tie are never used, so a
10465
10743
  later out_r stays <= the ordinal r at that position, preserving
10466
10744
  both the ascending order and (via the order() method's
10467
- (n-1)-out_r descending transform) the descending order. NaN
10468
- cells never tie with each other under dense (IEEE754 x != x). */
10745
+ descending transform) the descending order. NaN cells tie
10746
+ with each other, as sort_addr and the value-hash family treat
10747
+ every NaN as one value. */
10469
10748
  ca_size_t out_r = 0;
10470
10749
  for ( ca_size_t r = 0; r < k_unmasked; r++ ) {
10471
10750
  if ( r > 0 ) {
@@ -10477,14 +10756,25 @@ rank_index_ki_native_fixlen (VALUE self, CArray *ca, int axis, int do_stable, in
10477
10756
  }
10478
10757
  *(ca_size_t *) (po + buf[r].i * ostride) = (ca_size_t) out_r;
10479
10758
  }
10759
+ if ( dense == 2 && k_unmasked > 0 ) {
10760
+ ca_size_t nan_start = k_unmasked;
10761
+ ca_size_t top = nan_start > 0
10762
+ ? *(ca_size_t *) (po + buf[nan_start - 1].i * ostride) : (ca_size_t) -1;
10763
+ for ( ca_size_t r = 0; r < k_unmasked; r++ ) {
10764
+ ca_size_t *o = (ca_size_t *) (po + buf[r].i * ostride);
10765
+ *o = ( r < nan_start ) ? top - *o : top + 1;
10766
+ }
10767
+ }
10480
10768
  /* Mirror input mask to output (= UNDEF rank for masked
10481
- cells). Write via co->mask->ptr directly with the
10482
- output slab offset, because the iter's mo isn't
10483
- populated in CA_KERNEL_WRITE mode (= reduce form's
10484
- op_mask pattern). */
10769
+ cells). Write via co->mask->ptr directly, because a
10770
+ mask written through a WRITE walk's cursor does not
10771
+ reach the array. co is a fresh row-major entity and
10772
+ the walk cuts its slabs from a buffer laid out the
10773
+ same way, so the slab's offset from st_out.alias_ptr
10774
+ is its offset in co. */
10485
10775
  if ( mi && co->mask ) {
10486
10776
  boolean8_t *omask_base = (boolean8_t *) co->mask->ptr
10487
- + ((po - (char *) co->ptr) / (ca_size_t) co->bytes);
10777
+ + ((po - st_out.alias_ptr) / (ca_size_t) co->bytes);
10488
10778
  ca_size_t mostride = st_out.slab_mask_strides[0];
10489
10779
  for ( ca_size_t k = 0; k < fiber_n; k++ ) {
10490
10780
  if ( mi[k * mstride] ) omask_base[k * mostride] = 1;
@@ -10492,8 +10782,8 @@ rank_index_ki_native_fixlen (VALUE self, CArray *ca, int axis, int do_stable, in
10492
10782
  }
10493
10783
  ca_iter_state_sync_slab(&st_out);
10494
10784
  }
10495
- xfree(buf);
10496
- if ( aux ) xfree(aux);
10785
+ ALLOCV_END(hbuf);
10786
+ ALLOCV_END(haux);
10497
10787
  ca_iter_state_finish(&st_in);
10498
10788
  ca_iter_state_finish(&st_out);
10499
10789
  return vout;
@@ -10504,46 +10794,39 @@ rank_index_ki_dispatch (VALUE self, VALUE vaxis, int do_stable, int dense)
10504
10794
  {
10505
10795
  CArray *src;
10506
10796
  GetCArray(self, src);
10507
- /* Face gate: descend to storage (fixlen -> memcmp default,
10508
- numeric -> ORDERABLE required). The strip also makes the
10509
- switch below and every index/addr template output build on
10510
- the plain storage, not a face-lifted (NULL-ptr) view. */
10797
+ if ( ca_is_lazy_view(src) ) {
10798
+ volatile VALUE evaluated = ca_lazy_evaluated(self);
10799
+ if ( ! NIL_P(evaluated) ) {
10800
+ self = evaluated;
10801
+ GetCArray(self, src);
10802
+ }
10803
+ }
10804
+ /* Face gate: run on the array the Face's order is read from.
10805
+ It is never a Face, so the switch below and every index/addr
10806
+ template output build on a plain array, not a face-lifted
10807
+ (NULL-ptr) view. */
10511
10808
  if ( ca_is_face(src) ) {
10512
- VALUE _face = self;
10513
- int _orderable = ca_test_flag(src, CA_FLAG_FACE_ORDERABLE_STORAGE);
10514
- VALUE _st = rb_ca_strip_face_value(self);
10515
- CArray *_sc;
10516
- GetCArray(_st, _sc);
10517
- if ( _sc->data_type != CA_FIXLEN && ! _orderable ) {
10518
- rb_raise(rb_eArgError,
10519
- "rank_index_ki: Face-typed input (%s) is not orderable "
10520
- "by storage; use ca.parent to descend to storage",
10521
- rb_obj_classname(_face));
10522
- }
10523
- self = _st;
10524
- src = _sc;
10525
- }
10526
- int axis = NUM2INT(vaxis);
10527
- if ( axis < 0 ) axis += src->ndim;
10528
- if ( axis < 0 || axis >= src->ndim ) {
10529
- rb_raise(rb_eArgError, "rank_index_ki: axis %d out of range for ndim %d", NUM2INT(vaxis), src->ndim);
10809
+ self = ca_order_key(self, ca_calling_method_name());
10810
+ GetCArray(self, src);
10530
10811
  }
10812
+ int axis = ca_normalize_axis_arg(vaxis, src->ndim, NULL);
10531
10813
  switch ( src->data_type ) {
10532
- case CA_INT8: return rank_index_ki_native_i8(self, src, axis, do_stable, dense);
10533
- case CA_UINT8: return rank_index_ki_native_u8(self, src, axis, do_stable, dense);
10534
- case CA_INT16: return rank_index_ki_native_i16(self, src, axis, do_stable, dense);
10535
- case CA_UINT16: return rank_index_ki_native_u16(self, src, axis, do_stable, dense);
10536
- case CA_INT32: return rank_index_ki_native_i32(self, src, axis, do_stable, dense);
10537
- case CA_UINT32: return rank_index_ki_native_u32(self, src, axis, do_stable, dense);
10538
- case CA_INT64: return rank_index_ki_native_i64(self, src, axis, do_stable, dense);
10539
- case CA_UINT64: return rank_index_ki_native_u64(self, src, axis, do_stable, dense);
10540
- case CA_FLOAT32: return rank_index_ki_native_f32(self, src, axis, do_stable, dense);
10541
- case CA_FLOAT64: return rank_index_ki_native_f64(self, src, axis, do_stable, dense);
10542
- case CA_BOOLEAN: return rank_index_ki_native_bool(self, src, axis, do_stable, dense);
10543
- case CA_OBJECT: return rank_index_ki_native_object(self, src, axis, do_stable, dense);
10544
- case CA_FIXLEN: return rank_index_ki_native_fixlen(self, src, axis, do_stable, dense);
10814
+ case CA_INT8: CA_RETURN_KEEPING(self, rank_index_ki_native_i8(self, src, axis, do_stable, dense));
10815
+ case CA_UINT8: CA_RETURN_KEEPING(self, rank_index_ki_native_u8(self, src, axis, do_stable, dense));
10816
+ case CA_INT16: CA_RETURN_KEEPING(self, rank_index_ki_native_i16(self, src, axis, do_stable, dense));
10817
+ case CA_UINT16: CA_RETURN_KEEPING(self, rank_index_ki_native_u16(self, src, axis, do_stable, dense));
10818
+ case CA_INT32: CA_RETURN_KEEPING(self, rank_index_ki_native_i32(self, src, axis, do_stable, dense));
10819
+ case CA_UINT32: CA_RETURN_KEEPING(self, rank_index_ki_native_u32(self, src, axis, do_stable, dense));
10820
+ case CA_INT64: CA_RETURN_KEEPING(self, rank_index_ki_native_i64(self, src, axis, do_stable, dense));
10821
+ case CA_UINT64: CA_RETURN_KEEPING(self, rank_index_ki_native_u64(self, src, axis, do_stable, dense));
10822
+ case CA_FLOAT32: CA_RETURN_KEEPING(self, rank_index_ki_native_f32(self, src, axis, do_stable, dense));
10823
+ case CA_FLOAT64: CA_RETURN_KEEPING(self, rank_index_ki_native_f64(self, src, axis, do_stable, dense));
10824
+ case CA_BOOLEAN: CA_RETURN_KEEPING(self, rank_index_ki_native_bool(self, src, axis, do_stable, dense));
10825
+ case CA_OBJECT: CA_RETURN_KEEPING(self, rank_index_ki_native_object(self, src, axis, do_stable, dense));
10826
+ case CA_FIXLEN: CA_RETURN_KEEPING(self, rank_index_ki_native_fixlen(self, src, axis, do_stable, dense));
10545
10827
  default:
10546
- rb_raise(rb_eCADataTypeError, "rank_index_ki: source data_type :%s not supported (expected one of: i8, u8, i16, u16, i32, u32, i64, u64, f32, f64, bool, object, fixlen)", ca_type_name[src->data_type]);
10828
+ ca_face_refuse_numeric(self, ca_calling_method_name(), rb_eCADataTypeError);
10829
+ rb_raise(rb_eCADataTypeError, "%s: source data_type :%s not supported (expected one of: int8, uint8, int16, uint16, int32, uint32, int64, uint64, float32, float64, boolean, object, fixlen)", ca_calling_method_name(), ca_type_name[src->data_type]);
10547
10830
  }
10548
10831
  return Qnil; /* unreachable */
10549
10832
  }
@@ -10567,9 +10850,9 @@ rb_ca_rank_index_ki (VALUE self, VALUE vaxis)
10567
10850
  }
10568
10851
 
10569
10852
  VALUE
10570
- rb_ca_rank_index_ki_quick_dense (VALUE self, VALUE vaxis, int dense)
10853
+ rb_ca_rank_index_ki_kind_dense (VALUE self, VALUE vaxis, int stable, int dense)
10571
10854
  {
10572
- return rank_index_ki_dispatch(self, vaxis, 0, dense);
10855
+ return rank_index_ki_dispatch(self, vaxis, stable, dense);
10573
10856
  }
10574
10857
 
10575
10858
  static VALUE
@@ -10580,35 +10863,32 @@ rb_ca_rank_index_kw (int argc, VALUE *argv, VALUE self)
10580
10863
  volatile VALUE rkind = Qnil;
10581
10864
  volatile VALUE rmethod = Qnil;
10582
10865
  rb_scan_options(ropt, "axis,kind,method", &raxis, &rkind, &rmethod);
10583
- if ( NIL_P(raxis) ) raxis = INT2NUM(0); /* default axis */
10584
- int dense = 0; /* default method: :ordinal */
10585
- if ( ! NIL_P(rmethod) ) {
10586
- static ID sym_ordinal = 0, sym_dense = 0;
10587
- if ( ! sym_ordinal ) sym_ordinal = rb_intern("ordinal");
10588
- if ( ! sym_dense ) sym_dense = rb_intern("dense");
10589
- ID method_id = SYM2ID(rmethod);
10590
- if ( method_id == sym_ordinal ) dense = 0;
10591
- else if ( method_id == sym_dense ) dense = 1;
10592
- else {
10593
- rb_raise(rb_eArgError, "rank_index: unknown method %s (expected :ordinal or :dense)", rb_id2name(method_id));
10866
+ volatile VALUE whole_src = Qnil;
10867
+ if ( NIL_P(raxis) ) { /* no axis: the whole array */
10868
+ whole_src = self;
10869
+ {
10870
+ volatile VALUE evaluated = ca_lazy_evaluated(self);
10871
+ if ( ! NIL_P(evaluated) ) self = evaluated;
10594
10872
  }
10873
+ self = rb_ca_flatten(self);
10874
+ raxis = INT2NUM(0);
10595
10875
  }
10876
+ int dense = NIL_P(rmethod) ? 0 : ca_symbol_choice(rmethod, "method", "ordinal", "dense", NULL);
10596
10877
  if ( argc != 0 ) {
10597
10878
  rb_raise(rb_eArgError, "rank_index: positional args no longer accepted (given %d args); use axis:/kind: kwargs", argc);
10598
10879
  }
10599
- int do_stable = 0;
10600
- if ( ! NIL_P(rkind) ) {
10601
- static ID sym_quick = 0, sym_stable = 0;
10602
- if ( ! sym_quick ) sym_quick = rb_intern("quick");
10603
- if ( ! sym_stable ) sym_stable = rb_intern("stable");
10604
- ID kind_id = SYM2ID(rkind);
10605
- if ( kind_id == sym_quick ) do_stable = 0;
10606
- else if ( kind_id == sym_stable ) do_stable = 1;
10607
- else {
10608
- rb_raise(rb_eArgError, "rank_index: unknown kind %s (expected :quick or :stable)", rb_id2name(kind_id));
10880
+ int do_stable = NIL_P(rkind) ? 0 : ca_symbol_choice(rkind, "kind", "quick", "stable", NULL);
10881
+ VALUE out = rank_index_ki_dispatch(self, raxis, do_stable, dense);
10882
+ if ( ! NIL_P(whole_src) ) {
10883
+ CArray *src;
10884
+ VALUE shape_argv[CA_RANK_MAX];
10885
+ GetCArray(whole_src, src);
10886
+ for ( int k = 0; k < src->ndim; k++ ) {
10887
+ shape_argv[k] = SIZE2NUM(src->dim[k]);
10609
10888
  }
10889
+ out = rb_ca_reshape((int) src->ndim, shape_argv, out);
10610
10890
  }
10611
- return rank_index_ki_dispatch(self, raxis, do_stable, dense);
10891
+ return out;
10612
10892
  }
10613
10893
 
10614
10894
  void