carray 3.0.3 → 3.0.5

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 (221) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +469 -0
  3. data/README.md +3 -2
  4. data/carray.gemspec +1 -1
  5. data/ext/ca_array_pool.c +25 -1
  6. data/ext/ca_axis_descriptor.h +27 -24
  7. data/ext/ca_axis_dispatch.c +14 -26
  8. data/ext/ca_axis_group.c +27 -27
  9. data/ext/ca_binop_dispatch.c +1 -29
  10. data/ext/ca_binop_dispatch.h +0 -1
  11. data/ext/ca_categorical_iterator.c +231 -153
  12. data/ext/ca_compare.c +9 -20
  13. data/ext/ca_composite_dispatch.c +9 -9
  14. data/ext/ca_group_iter.c +16 -17
  15. data/ext/ca_kernel_iterator.c +1240 -2000
  16. data/ext/ca_kernel_iterator.h +502 -519
  17. data/ext/ca_monop_dispatch.c +82 -59
  18. data/ext/ca_monop_dispatch.h +1 -0
  19. data/ext/ca_obj_array.c +152 -45
  20. data/ext/ca_obj_bincmp.c +25 -21
  21. data/ext/ca_obj_binop.c +16 -26
  22. data/ext/ca_obj_bitarray.c +8 -3
  23. data/ext/ca_obj_bitfield.c +63 -49
  24. data/ext/ca_obj_block.c +18 -9
  25. data/ext/ca_obj_byte_swap.c +7 -7
  26. data/ext/ca_obj_const_string.c +188 -22
  27. data/ext/ca_obj_face.c +280 -58
  28. data/ext/ca_obj_face.h +19 -0
  29. data/ext/ca_obj_fake.c +15 -29
  30. data/ext/ca_obj_farray.c +6 -3
  31. data/ext/ca_obj_field.c +30 -8
  32. data/ext/ca_obj_fixlen_string.c +2 -3
  33. data/ext/ca_obj_grid.c +455 -134
  34. data/ext/ca_obj_meld.c +172 -34
  35. data/ext/ca_obj_moncmp.c +23 -8
  36. data/ext/ca_obj_monop.c +113 -67
  37. data/ext/ca_obj_object.c +210 -52
  38. data/ext/ca_obj_record.c +15 -7
  39. data/ext/ca_obj_reduce.c +17 -8
  40. data/ext/ca_obj_refer.c +42 -35
  41. data/ext/ca_obj_remap.c +30 -9
  42. data/ext/ca_obj_repeat.c +9 -10
  43. data/ext/ca_obj_roll.c +31 -10
  44. data/ext/ca_obj_select.c +48 -14
  45. data/ext/ca_obj_select_axis.c +15 -47
  46. data/ext/ca_obj_shift.c +41 -7
  47. data/ext/ca_obj_stack.c +180 -34
  48. data/ext/ca_obj_stride.c +190 -134
  49. data/ext/ca_obj_string.c +80 -4
  50. data/ext/ca_obj_tile.c +32 -11
  51. data/ext/ca_obj_time.c +22 -6
  52. data/ext/ca_obj_timedelta.c +22 -6
  53. data/ext/ca_obj_transpose.c +13 -7
  54. data/ext/ca_obj_triop.c +26 -32
  55. data/ext/ca_obj_window.c +267 -74
  56. data/ext/ca_op_ipower.c +4 -4
  57. data/ext/ca_sort_kernels.h +6 -3
  58. data/ext/ca_sweep_engine.c +2 -2
  59. data/ext/caframe_csv_reader.c +80 -1
  60. data/ext/carray.h +214 -75
  61. data/ext/carray_access.c +200 -99
  62. data/ext/carray_address_basis.c +343 -87
  63. data/ext/carray_attribute.c +113 -6
  64. data/ext/carray_bincount.c +10 -8
  65. data/ext/carray_broadcast.c +13 -8
  66. data/ext/carray_build_flags.h +3 -0
  67. data/ext/carray_call_cfunc.c +108 -0
  68. data/ext/carray_cast.c +375 -96
  69. data/ext/carray_cast_func.rb +90 -20
  70. data/ext/carray_class.c +4 -3
  71. data/ext/carray_config.h +28 -0
  72. data/ext/carray_conversion.c +18 -9
  73. data/ext/carray_copy.c +51 -14
  74. data/ext/carray_core.c +150 -55
  75. data/ext/carray_count.c +56 -20
  76. data/ext/carray_element.c +81 -33
  77. data/ext/carray_factorize.c +138 -84
  78. data/ext/carray_generate.c +199 -52
  79. data/ext/carray_histogram.c +63 -18
  80. data/ext/carray_hold.c +12 -15
  81. data/ext/carray_index_classifier.c +388 -176
  82. data/ext/carray_internal.h +348 -29
  83. data/ext/carray_kernels_bincmp.c +4453 -0
  84. data/ext/carray_kernels_binop.c +11368 -0
  85. data/ext/carray_kernels_init.c +1734 -0
  86. data/ext/carray_kernels_moncmp.c +2104 -0
  87. data/ext/carray_kernels_monop.c +18369 -0
  88. data/ext/carray_kernels_reduce_aggregate.c +23818 -0
  89. data/ext/carray_kernels_reduce_boolean.c +406 -0
  90. data/ext/carray_kernels_reduce_cumulative.c +10086 -0
  91. data/ext/carray_kernels_reduce_extreme.c +17634 -0
  92. data/ext/carray_kernels_reduce_variance.c +3849 -0
  93. data/ext/carray_kernels_scan.c +4302 -0
  94. data/ext/carray_kernels_search.c +33829 -0
  95. data/ext/carray_kernels_sort.c +10897 -0
  96. data/ext/carray_kernels_triop.c +1864 -0
  97. data/ext/carray_key_windows.c +220 -0
  98. data/ext/carray_lazy.c +170 -111
  99. data/ext/carray_mask.c +337 -94
  100. data/ext/carray_median_percentile.c +82 -28
  101. data/ext/carray_memory_view.c +669 -561
  102. data/ext/carray_operator.c +129 -52
  103. data/ext/carray_order.c +604 -77
  104. data/ext/carray_partition.c +28 -27
  105. data/ext/carray_random.c +85 -31
  106. data/ext/carray_round.c +273 -0
  107. data/ext/carray_scatter.c +87 -45
  108. data/ext/carray_segment.c +3 -3
  109. data/ext/carray_slab.c +123 -37
  110. data/ext/carray_sort.c +259 -51
  111. data/ext/carray_sort_kernel.c +5 -7
  112. data/ext/carray_struct.c +54 -61
  113. data/ext/carray_test.c +26 -5
  114. data/ext/carray_time_text.c +4 -2
  115. data/ext/carray_undef.c +4 -0
  116. data/ext/carray_utils.c +309 -66
  117. data/ext/mk_call_cfunc.rb +19 -2
  118. data/ext/mkkernel.rb +403 -350
  119. data/ext/ruby_carray.c +10 -4
  120. data/ext/version.h +4 -4
  121. data/lib/carray/attribute.rb +139 -22
  122. data/lib/carray/autoload_carray.rb +8 -31
  123. data/lib/carray/autoload_method_extension.rb +14 -5
  124. data/lib/carray/axis_group.rb +255 -106
  125. data/lib/carray/basics.rb +105 -17
  126. data/lib/carray/bincount_nd.rb +12 -2
  127. data/lib/carray/block_iterator.rb +126 -23
  128. data/lib/carray/boolean_reduce.rb +17 -17
  129. data/lib/carray/categorical.rb +195 -59
  130. data/lib/carray/categorical_iterator.rb +132 -29
  131. data/lib/carray/complex.rb +5 -5
  132. data/lib/carray/conditional.rb +67 -18
  133. data/lib/carray/const_string.rb +24 -5
  134. data/lib/carray/construct.rb +15 -11
  135. data/lib/carray/data_type_extension.rb +9 -1
  136. data/lib/carray/distance_limit.rb +77 -0
  137. data/lib/carray/fixlen_string.rb +48 -3
  138. data/lib/carray/frame/column_verbs.rb +71 -0
  139. data/lib/carray/frame/concat.rb +17 -3
  140. data/lib/carray/frame/convert.rb +69 -0
  141. data/lib/carray/frame/csv_parser.rb +111 -17
  142. data/lib/carray/frame/describe.rb +3 -2
  143. data/lib/carray/frame/frame.rb +252 -28
  144. data/lib/carray/frame/group.rb +603 -101
  145. data/lib/carray/frame/io.rb +117 -28
  146. data/lib/carray/frame/join.rb +312 -51
  147. data/lib/carray/frame/records.rb +83 -7
  148. data/lib/carray/frame/reshape.rb +42 -35
  149. data/lib/carray/frame/sort.rb +7 -19
  150. data/lib/carray/frame/verbs.rb +188 -48
  151. data/lib/carray/frame/windows.rb +117 -0
  152. data/lib/carray/frame.rb +2 -0
  153. data/lib/carray/fuse_source.rb +42 -6
  154. data/lib/carray/fusion.rb +40 -16
  155. data/lib/carray/histogram.rb +6 -1
  156. data/lib/carray/inspect.rb +31 -8
  157. data/lib/carray/iterator.rb +85 -7
  158. data/lib/carray/key_windows.rb +620 -0
  159. data/lib/carray/lazy.rb +217 -183
  160. data/lib/carray/mask_gap_fill.rb +63 -57
  161. data/lib/carray/meld_reduce.rb +12 -14
  162. data/lib/carray/methods/bin.rb +26 -18
  163. data/lib/carray/methods/bincount.rb +16 -0
  164. data/lib/carray/methods/bit_string.rb +1 -1
  165. data/lib/carray/methods/choose.rb +16 -3
  166. data/lib/carray/methods/composition.rb +3 -1
  167. data/lib/carray/methods/gather_nd.rb +14 -5
  168. data/lib/carray/methods/insert_block.rb +2 -2
  169. data/lib/carray/methods/is_between.rb +59 -0
  170. data/lib/carray/methods/is_in.rb +61 -37
  171. data/lib/carray/methods/join.rb +1 -6
  172. data/lib/carray/methods/lag.rb +49 -0
  173. data/lib/carray/methods/locate_addr.rb +47 -51
  174. data/lib/carray/methods/mode.rb +1 -1
  175. data/lib/carray/methods/nunique.rb +11 -6
  176. data/lib/carray/methods/resize.rb +3 -3
  177. data/lib/carray/methods/value_counts.rb +4 -11
  178. data/lib/carray/record_order.rb +3 -3
  179. data/lib/carray/rng.rb +3 -0
  180. data/lib/carray/runtime.rb +53 -8
  181. data/lib/carray/segment_iterator.rb +323 -88
  182. data/lib/carray/serialize.rb +45 -14
  183. data/lib/carray/slab_iterator.rb +50 -0
  184. data/lib/carray/stack.rb +15 -12
  185. data/lib/carray/string.rb +113 -5
  186. data/lib/carray/string_operation_extension.rb +21 -6
  187. data/lib/carray/struct.rb +9 -21
  188. data/lib/carray/struct_builder.rb +24 -11
  189. data/lib/carray/time.rb +819 -158
  190. data/lib/carray/window_iterator.rb +122 -16
  191. data/lib/carray.rb +7 -0
  192. data/yard-stubs/ca_obj_bitfield.rb +1 -1
  193. data/yard-stubs/ca_obj_field.rb +4 -0
  194. data/yard-stubs/ca_obj_grid.rb +21 -10
  195. data/yard-stubs/ca_obj_refer.rb +2 -1
  196. data/yard-stubs/ca_obj_roll.rb +2 -2
  197. data/yard-stubs/ca_obj_shift.rb +5 -1
  198. data/yard-stubs/ca_obj_tile.rb +1 -1
  199. data/yard-stubs/ca_obj_transpose.rb +5 -4
  200. data/yard-stubs/ca_obj_window.rb +8 -3
  201. data/yard-stubs/carray_access.rb +6 -1
  202. data/yard-stubs/carray_attribute.rb +3 -6
  203. data/yard-stubs/carray_cast.rb +12 -0
  204. data/yard-stubs/carray_conversion.rb +7 -2
  205. data/yard-stubs/carray_copy.rb +4 -2
  206. data/yard-stubs/carray_count.rb +4 -4
  207. data/yard-stubs/carray_element.rb +17 -10
  208. data/yard-stubs/carray_generate.rb +21 -1
  209. data/yard-stubs/carray_mask.rb +32 -3
  210. data/yard-stubs/carray_math.rb +41 -5
  211. data/yard-stubs/carray_median_percentile.rb +5 -2
  212. data/yard-stubs/carray_memory_view.rb +40 -23
  213. data/yard-stubs/carray_order.rb +129 -22
  214. data/yard-stubs/carray_random.rb +13 -4
  215. data/yard-stubs/carray_scatter.rb +17 -10
  216. data/yard-stubs/carray_slab.rb +9 -2
  217. data/yard-stubs/carray_sort.rb +6 -5
  218. data/yard-stubs/carray_test.rb +5 -3
  219. metadata +25 -3
  220. data/lib/carray/arrow_tensor.rb +0 -401
  221. data/lib/carray/table.rb +0 -108
data/ext/carray_order.c CHANGED
@@ -116,19 +116,23 @@ ca_project_loop (CArray *co, CArray *ca, CArray *ci, char *lfill, char *ufill)
116
116
  xfree(ma);
117
117
  }
118
118
 
119
- CArray *
119
+ /* The result is wrapped before anything is copied into it: for an object
120
+ array the gathered cells are VALUEs that only the result's mark keeps
121
+ alive once the source is detached. */
122
+ static VALUE
120
123
  ca_project (CArray *ca, CArray *ci, char *lfill, char *ufill)
121
124
  {
125
+ volatile VALUE obj;
122
126
  CArray *co;
123
127
 
124
- ca_attach_n(2, ca, ci); /* window: nothing raises inside */
128
+ obj = rb_carray_new(ca->data_type, ci->ndim, ci->dim, ca->bytes, NULL);
129
+ GetCArray(obj, co);
125
130
 
126
- co = carray_new(ca->data_type, ci->ndim, ci->dim, ca->bytes, NULL);
131
+ ca_attach_n(2, ca, ci); /* window: nothing raises inside */
127
132
  ca_project_loop(co, ca, ci, lfill, ufill);
128
-
129
133
  ca_detach_n(2, ca, ci); /* DETACH */
130
134
 
131
- return co;
135
+ return obj;
132
136
  }
133
137
 
134
138
  /* rk.__count_below__(q, inclusive) -- for each cell of q, how many values
@@ -238,7 +242,13 @@ rb_ca_project (int argc, VALUE *argv, VALUE self)
238
242
  char *lfval, *ufval;
239
243
  int self_is_face;
240
244
 
241
- rb_scan_args(argc, argv, "12", (VALUE *)&ridx, (VALUE *) &vlfval, (VALUE *) &vufval);
245
+ {
246
+ VALUE ropt = Qnil;
247
+ /* project takes no keyword: one given was read as a fill value. */
248
+ rb_scan_args(argc, argv, "12:", (VALUE *)&ridx, (VALUE *) &vlfval,
249
+ (VALUE *) &vufval, &ropt);
250
+ rb_scan_options(ropt, "");
251
+ }
242
252
 
243
253
  TypedData_Get_Struct(self, CArray, &carray_data_type, ca);
244
254
  self_is_face = ca_is_face(ca);
@@ -254,12 +264,12 @@ rb_ca_project (int argc, VALUE *argv, VALUE self)
254
264
  lfval = ALLOCV_N(char, hlfval, ca->bytes); /* converting a fill value */
255
265
  ufval = ALLOCV_N(char, hufval, ca->bytes); /* can raise; the unwind frees */
256
266
 
257
- if ( ! NIL_P(vlfval) ) {
267
+ if ( ! NIL_P(vlfval) && vlfval != CA_UNDEF ) {
258
268
  rb_ca_obj2ptr(self, vlfval, lfval);
259
269
  rb_ca_obj2ptr(self, vlfval, ufval);
260
270
  }
261
271
 
262
- if ( ! NIL_P(vufval) ) {
272
+ if ( ! NIL_P(vufval) && vufval != CA_UNDEF ) {
263
273
  rb_ca_obj2ptr(self, vufval, ufval);
264
274
  }
265
275
 
@@ -274,15 +284,19 @@ rb_ca_project (int argc, VALUE *argv, VALUE self)
274
284
  TypedData_Get_Struct(vstorage, CArray, &carray_data_type, ca);
275
285
  }
276
286
 
277
- co = ca_project(ca, ci,
278
- ( ! NIL_P(vlfval) ) ? lfval : NULL,
279
- ( ( ! NIL_P(vufval) ) || ( ! NIL_P(vlfval) ) ) ? ufval : NULL);
287
+ /* A fill of UNDEF is no fill: the cell is masked, as it is with none. */
288
+ {
289
+ VALUE vlower = vlfval;
290
+ VALUE vupper = NIL_P(vufval) ? vlfval : vufval;
291
+ obj = ca_project(ca, ci,
292
+ ( NIL_P(vlower) || vlower == CA_UNDEF ) ? NULL : lfval,
293
+ ( NIL_P(vupper) || vupper == CA_UNDEF ) ? NULL : ufval);
294
+ }
295
+ GetCArray(obj, co);
280
296
 
281
297
  ALLOCV_END(hlfval);
282
298
  ALLOCV_END(hufval);
283
299
 
284
- obj = ca_wrap_struct(co);
285
-
286
300
  if ( ! ca_is_any_masked(co) ) {
287
301
  obj = rb_ca_unmask_copy(obj);
288
302
  }
@@ -291,6 +305,7 @@ rb_ca_project (int argc, VALUE *argv, VALUE self)
291
305
  obj = ca_face_lift(obj, self);
292
306
  }
293
307
 
308
+ rb_ca_inherit_attr(obj, self);
294
309
  return obj;
295
310
  }
296
311
 
@@ -532,6 +547,41 @@ rb_ca_search_nearest_addr_kw (int argc, VALUE *argv, VALUE self)
532
547
 
533
548
  /* ---- sort_by_key family ---------------------------------------------- */
534
549
 
550
+ /* The key orders self's cells one for one, so it has self's shape. */
551
+ static void
552
+ ca_by_key_check_shape (VALUE self, VALUE rkey, const char *name)
553
+ {
554
+ CArray *ca, *ck;
555
+ int8_t i;
556
+ rb_check_carray_object(rkey);
557
+ TypedData_Get_Struct(self, CArray, &carray_data_type, ca);
558
+ TypedData_Get_Struct(rkey, CArray, &carray_data_type, ck);
559
+ if ( ca->ndim == ck->ndim ) {
560
+ for (i = 0; i < ca->ndim; i++) {
561
+ if ( ca->dim[i] != ck->dim[i] ) break;
562
+ }
563
+ if ( i == ca->ndim ) return;
564
+ }
565
+ rb_raise(rb_eArgError, "%s: the key's shape %"PRIsVALUE" differs from "
566
+ "the array's %"PRIsVALUE, name,
567
+ rb_funcall(rkey, rb_intern("shape"), 0),
568
+ rb_funcall(self, rb_intern("shape"), 0));
569
+ }
570
+
571
+ /* self at the addresses an extremum by key found. A fiber with no cell to
572
+ compare has a masked (or UNDEF) address and answers UNDEF. */
573
+ static VALUE
574
+ ca_by_key_fetch (VALUE self, VALUE addrs)
575
+ {
576
+ if ( addrs == CA_UNDEF ) {
577
+ return CA_UNDEF;
578
+ }
579
+ if ( rb_obj_is_carray(addrs) && RTEST(rb_ca_has_mask(addrs)) ) {
580
+ return rb_funcall(self, rb_intern("project"), 1, addrs);
581
+ }
582
+ return rb_ca_fetch(self, addrs);
583
+ }
584
+
535
585
  /* sort_by_key(key, axis: 0) -- gather self at key.sort_addr(axis:). */
536
586
  static VALUE
537
587
  rb_ca_sort_by_key (int argc, VALUE *argv, VALUE self)
@@ -542,6 +592,7 @@ rb_ca_sort_by_key (int argc, VALUE *argv, VALUE self)
542
592
  if ( NIL_P(raxis) ) {
543
593
  raxis = INT2FIX(0);
544
594
  }
595
+ ca_by_key_check_shape(self, rkey, "sort_by_key");
545
596
  VALUE addrs = rb_ca_sort_addr_c(rkey, raxis, 0, 1); /* masked_last=1 (:last), unchanged default */
546
597
  return rb_ca_fetch(self, addrs);
547
598
  }
@@ -553,9 +604,11 @@ rb_ca_max_by_key (int argc, VALUE *argv, VALUE self)
553
604
  VALUE rkey, rkw = Qnil, raxis = Qnil;
554
605
  rb_scan_args(argc, argv, "1:", &rkey, &rkw);
555
606
  rb_scan_options(rkw, "axis", &raxis);
556
- if ( RTEST(rb_ca_is_empty(self)) ) {
557
- return rb_const_get(rb_cObject, rb_intern("UNDEF"));
607
+ ca_by_key_check_shape(self, rkey, "max_by_key");
608
+ if ( NIL_P(raxis) && RTEST(rb_ca_is_empty(self)) ) {
609
+ return CA_UNDEF;
558
610
  }
611
+ rkey = ca_order_key(rkey, "max_by_key");
559
612
 
560
613
  VALUE addrs;
561
614
 
@@ -568,7 +621,7 @@ rb_ca_max_by_key (int argc, VALUE *argv, VALUE self)
568
621
  rb_hash_aset(kw, ID2SYM(id_axis), raxis);
569
622
  addrs = rb_funcallv_kw(rkey, rb_intern("max_addr"), 1, &kw, RB_PASS_KEYWORDS);
570
623
  }
571
- return rb_ca_fetch(self, addrs);
624
+ return ca_by_key_fetch(self, addrs);
572
625
  }
573
626
 
574
627
  /* min_by_key(key, axis: nil) -- self[key.min_addr(axis:)] (or UNDEF if empty). */
@@ -578,9 +631,11 @@ rb_ca_min_by_key (int argc, VALUE *argv, VALUE self)
578
631
  VALUE rkey, rkw = Qnil, raxis = Qnil;
579
632
  rb_scan_args(argc, argv, "1:", &rkey, &rkw);
580
633
  rb_scan_options(rkw, "axis", &raxis);
581
- if ( RTEST(rb_ca_is_empty(self)) ) {
582
- return rb_const_get(rb_cObject, rb_intern("UNDEF"));
634
+ ca_by_key_check_shape(self, rkey, "min_by_key");
635
+ if ( NIL_P(raxis) && RTEST(rb_ca_is_empty(self)) ) {
636
+ return CA_UNDEF;
583
637
  }
638
+ rkey = ca_order_key(rkey, "min_by_key");
584
639
 
585
640
  VALUE addrs;
586
641
 
@@ -593,7 +648,7 @@ rb_ca_min_by_key (int argc, VALUE *argv, VALUE self)
593
648
  rb_hash_aset(kw, ID2SYM(id_axis), raxis);
594
649
  addrs = rb_funcallv_kw(rkey, rb_intern("min_addr"), 1, &kw, RB_PASS_KEYWORDS);
595
650
  }
596
- return rb_ca_fetch(self, addrs);
651
+ return ca_by_key_fetch(self, addrs);
597
652
  }
598
653
 
599
654
  /* ---- take_along_axis / put_along_axis -------------------------------- */
@@ -637,10 +692,16 @@ rb_ca_put_along_axis (int argc, VALUE *argv, VALUE self)
637
692
  raxis = INT2FIX(0);
638
693
  }
639
694
  VALUE addrs = rb_ca_axis2addr_c(self, argv[0], raxis);
640
- /* A masked index writes nothing: scatter_replace! skips a masked
641
- address (and writes the rest last-write-wins, as the store does). */
695
+ /* A masked index writes nothing. The values are first laid out over the
696
+ indices by the store itself (an array of the indices' shape, so a
697
+ broadcast, UNDEF or a Face value means what it means unmasked), and
698
+ only the cells at unmasked indices are written. */
642
699
  if ( RTEST(rb_ca_has_mask(addrs)) ) {
643
- rb_funcall(self, rb_intern("scatter_replace!"), 2, addrs, argv[1]);
700
+ VALUE sel = rb_funcall(addrs, rb_intern("is_not_masked"), 0);
701
+ VALUE vals = rb_funcall(self, rb_intern("project"), 1, addrs);
702
+ rb_funcall(vals, rb_intern("[]="), 1, argv[1]);
703
+ rb_ca_store(self, rb_funcall(addrs, rb_intern("[]"), 1, sel),
704
+ rb_funcall(vals, rb_intern("[]"), 1, sel));
644
705
  return self;
645
706
  }
646
707
  rb_ca_store(self, addrs, argv[1]);
@@ -653,7 +714,12 @@ static VALUE
653
714
  rb_ca_range_method (VALUE self)
654
715
  {
655
716
  VALUE mm_argv[1] = { Qnil };
656
- VALUE pair = rb_ca_minmax_ki(0, mm_argv, self);
717
+ CArray *ca;
718
+ VALUE pair;
719
+ TypedData_Get_Struct(self, CArray, &carray_data_type, ca);
720
+ /* A Face answers its own minmax (in its order, with its elements). */
721
+ pair = ca_is_face(ca) ? rb_funcall(self, rb_intern("minmax"), 0)
722
+ : rb_ca_minmax_ki(0, mm_argv, self);
657
723
  VALUE lo = rb_ary_entry(pair, 0);
658
724
  VALUE hi = rb_ary_entry(pair, 1);
659
725
  return rb_range_new(lo, hi, 0);
@@ -661,6 +727,422 @@ rb_ca_range_method (VALUE self)
661
727
 
662
728
  /* ---- nlargest / nsmallest family ------------------------------------- */
663
729
 
730
+ /* The answer is the cells first in the order (value, then NaN, then
731
+ masked; the values descending for the largest) with ties broken by
732
+ position, as `order` breaks them. Fewer cells than the fiber are found
733
+ by the buffered walk below. The other routes (the whole fiber, and the
734
+ keys the walk does not read) sort or partition every cell, which picks
735
+ the right values but, among cells that tie with the last one picked,
736
+ any of them; ca_topk_stable replaces the tied candidates by the
737
+ earliest cells that tie and sorts the candidates by (order, position). */
738
+
739
+ typedef struct {
740
+ const char *v; /* the fiber's values, contiguous */
741
+ const boolean8_t *m; /* its mask, or NULL */
742
+ int8_t type;
743
+ ca_size_t bytes;
744
+ int desc;
745
+ } ca_topk_fiber_t;
746
+
747
+ /* 0 a value, 1 NaN, 2 masked: the classes come in that order. */
748
+ static int
749
+ ca_topk_class (const ca_topk_fiber_t *f, ca_size_t i)
750
+ {
751
+ if ( f->m && f->m[i] ) return 2;
752
+ if ( f->type == CA_FLOAT64 && isnan(((const double *) f->v)[i]) ) return 1;
753
+ if ( f->type == CA_FLOAT32 && isnan(((const float *) f->v)[i]) ) return 1;
754
+ return 0;
755
+ }
756
+
757
+ #define CA_TOPK_CMP(T) \
758
+ { T a_ = ((const T *) f->v)[i], b_ = ((const T *) f->v)[j]; \
759
+ r = (a_ < b_) ? -1 : (a_ > b_) ? 1 : 0; } \
760
+ break
761
+
762
+ /* The order of cells i and j, ties not broken. */
763
+ static int
764
+ ca_topk_cmp (const ca_topk_fiber_t *f, ca_size_t i, ca_size_t j)
765
+ {
766
+ int ci = ca_topk_class(f, i), cj = ca_topk_class(f, j), r = 0;
767
+ if ( ci != cj ) return ( ci < cj ) ? -1 : 1;
768
+ if ( ci != 0 ) return 0;
769
+ switch ( f->type ) {
770
+ case CA_BOOLEAN:
771
+ case CA_UINT8: CA_TOPK_CMP(uint8_t);
772
+ case CA_INT8: CA_TOPK_CMP(int8_t);
773
+ case CA_INT16: CA_TOPK_CMP(int16_t);
774
+ case CA_UINT16: CA_TOPK_CMP(uint16_t);
775
+ case CA_INT32: CA_TOPK_CMP(int32_t);
776
+ case CA_UINT32: CA_TOPK_CMP(uint32_t);
777
+ case CA_INT64: CA_TOPK_CMP(int64_t);
778
+ case CA_UINT64: CA_TOPK_CMP(uint64_t);
779
+ case CA_FLOAT32: CA_TOPK_CMP(float);
780
+ case CA_FLOAT64: CA_TOPK_CMP(double);
781
+ case CA_FIXLEN:
782
+ r = memcmp(f->v + i * f->bytes, f->v + j * f->bytes, (size_t) f->bytes);
783
+ r = ( r > 0 ) - ( r < 0 );
784
+ break;
785
+ default:
786
+ r = 0;
787
+ }
788
+ return f->desc ? -r : r;
789
+ }
790
+
791
+ #undef CA_TOPK_CMP
792
+
793
+ /* Merge sort of positions by (order, position). */
794
+ static void
795
+ ca_topk_sort (const ca_topk_fiber_t *f, ca_size_t *p, ca_size_t *tmp, ca_size_t n)
796
+ {
797
+ ca_size_t h, i, j, k;
798
+ if ( n < 2 ) return;
799
+ h = n / 2;
800
+ ca_topk_sort(f, p, tmp, h);
801
+ ca_topk_sort(f, p + h, tmp, n - h);
802
+ i = 0; j = h; k = 0;
803
+ while ( i < h && j < n ) {
804
+ int r = ca_topk_cmp(f, p[i], p[j]);
805
+ if ( r < 0 || ( r == 0 && p[i] < p[j] ) ) tmp[k++] = p[i++];
806
+ else tmp[k++] = p[j++];
807
+ }
808
+ while ( i < h ) tmp[k++] = p[i++];
809
+ while ( j < n ) tmp[k++] = p[j++];
810
+ memcpy(p, tmp, (size_t) n * sizeof(ca_size_t));
811
+ }
812
+
813
+ #define CA_TOPK_SCAN_VALUE(T) \
814
+ { const T *v_ = (const T *) f->v; T w_ = v_[worst]; \
815
+ for (j = 0; j <= end && ties > 0; j++) { \
816
+ if ( v_[j] == w_ && ! ( f->m && f->m[j] ) ) { c[n++] = j; ties--; } \
817
+ } } \
818
+ break
819
+
820
+ /* The earliest cells, up to `end`, that tie with cell `worst`, appended to
821
+ c[n..] until `ties` are found. A value compares by ==, which leaves out
822
+ NaN and the masked cells; those classes tie within themselves. */
823
+ static ca_size_t
824
+ ca_topk_scan_ties (const ca_topk_fiber_t *f, ca_size_t worst, ca_size_t end,
825
+ ca_size_t *c, ca_size_t n, ca_size_t ties)
826
+ {
827
+ ca_size_t j;
828
+ int cls = ca_topk_class(f, worst);
829
+ if ( cls != 0 || f->type == CA_FIXLEN ) {
830
+ for (j = 0; j <= end && ties > 0; j++) {
831
+ if ( ca_topk_cmp(f, j, worst) == 0 ) { c[n++] = j; ties--; }
832
+ }
833
+ return n;
834
+ }
835
+ switch ( f->type ) {
836
+ case CA_BOOLEAN:
837
+ case CA_UINT8: CA_TOPK_SCAN_VALUE(uint8_t);
838
+ case CA_INT8: CA_TOPK_SCAN_VALUE(int8_t);
839
+ case CA_INT16: CA_TOPK_SCAN_VALUE(int16_t);
840
+ case CA_UINT16: CA_TOPK_SCAN_VALUE(uint16_t);
841
+ case CA_INT32: CA_TOPK_SCAN_VALUE(int32_t);
842
+ case CA_UINT32: CA_TOPK_SCAN_VALUE(uint32_t);
843
+ case CA_INT64: CA_TOPK_SCAN_VALUE(int64_t);
844
+ case CA_UINT64: CA_TOPK_SCAN_VALUE(uint64_t);
845
+ case CA_FLOAT32: CA_TOPK_SCAN_VALUE(float);
846
+ case CA_FLOAT64: CA_TOPK_SCAN_VALUE(double);
847
+ }
848
+ return n;
849
+ }
850
+
851
+ #undef CA_TOPK_SCAN_VALUE
852
+
853
+ static void
854
+ ca_topk_fix_fiber (const ca_topk_fiber_t *f, ca_size_t len,
855
+ ca_size_t *c, ca_size_t cap, ca_size_t *tmp)
856
+ {
857
+ ca_size_t worst = c[0], ties = 0, n = 0, end = 0, k;
858
+ for (k = 1; k < cap; k++) {
859
+ if ( ca_topk_cmp(f, c[k], worst) > 0 ) worst = c[k];
860
+ }
861
+ /* Keep the candidates before the worst; the ones tied with it are
862
+ replaced by the earliest cells that tie, which lie no later than the
863
+ last tied candidate. */
864
+ for (k = 0; k < cap; k++) {
865
+ if ( ca_topk_cmp(f, c[k], worst) < 0 ) {
866
+ c[n++] = c[k];
867
+ }
868
+ else {
869
+ ties++;
870
+ if ( c[k] > end ) end = c[k];
871
+ }
872
+ }
873
+ (void) len;
874
+ n = ca_topk_scan_ties(f, worst, end, c, n, ties);
875
+ ca_topk_sort(f, c, tmp, cap);
876
+ }
877
+
878
+ /* `picked` holds cap candidate positions per fiber along axis; returns the
879
+ stable answer of the same shape. The fibers are read with the axis
880
+ moved last, from a contiguous copy. */
881
+ static VALUE
882
+ ca_topk_stable (VALUE self, VALUE picked, long axis, int desc)
883
+ {
884
+ volatile VALUE key = ca_order_key(self, desc ? "nlargest" : "nsmallest");
885
+ volatile VALUE vals, pk, holder = 0;
886
+ VALUE perm[CA_RANK_MAX], inv[CA_RANK_MAX];
887
+ CArray *cv, *cp;
888
+ ca_topk_fiber_t f;
889
+ ca_size_t len, cap, outer, o, *tmp;
890
+ int ndim, k, last;
891
+
892
+ GetCArray(key, cv);
893
+ ndim = cv->ndim;
894
+ last = ( axis == ndim - 1 );
895
+ if ( ! last ) {
896
+ int n = 0;
897
+ for (k = 0; k < ndim; k++) if ( k != axis ) perm[n++] = INT2FIX(k);
898
+ perm[n] = LONG2NUM(axis);
899
+ for (k = 0; k < ndim; k++) inv[NUM2INT(perm[k])] = INT2FIX(k);
900
+ }
901
+ /* An entity whose fibers already lie along its last axis is read in
902
+ place; anything else is copied into that layout. */
903
+ if ( last && ca_is_entity(cv) ) {
904
+ vals = key;
905
+ ca_update_mask(cv);
906
+ }
907
+ else {
908
+ vals = rb_ca_copy(last ? key : rb_funcallv(key, rb_intern("transpose"), ndim, perm));
909
+ }
910
+ pk = rb_ca_copy(last ? picked : rb_funcallv(picked, rb_intern("transpose"), ndim, perm));
911
+ GetCArray(vals, cv);
912
+ GetCArray(pk, cp);
913
+
914
+ len = cv->dim[ndim - 1];
915
+ cap = cp->dim[ndim - 1];
916
+ if ( len == 0 || cap == 0 ) return picked;
917
+ outer = cv->elements / len;
918
+ f.type = cv->data_type;
919
+ f.bytes = cv->bytes;
920
+ f.desc = desc;
921
+ tmp = ALLOCV_N(ca_size_t, holder, cap);
922
+ for (o = 0; o < outer; o++) {
923
+ f.v = cv->ptr + o * len * cv->bytes;
924
+ f.m = cv->mask ? (boolean8_t *) cv->mask->ptr + o * len : NULL;
925
+ ca_topk_fix_fiber(&f, len, (ca_size_t *) cp->ptr + o * cap, cap, tmp);
926
+ }
927
+ ALLOCV_END(holder);
928
+ return last ? pk : rb_ca_copy(rb_funcallv(pk, rb_intern("transpose"), ndim, inv));
929
+ }
930
+
931
+ /* Whether self holds a cell the order puts after the numbers: a masked
932
+ cell, or a NaN. */
933
+ static int
934
+ ca_topk_has_cells_after_numbers (VALUE self, CArray *ca)
935
+ {
936
+ if ( ca_has_mask(ca) && ca_count_masked(ca) > 0 ) {
937
+ return 1;
938
+ }
939
+ if ( ca_is_float_type(ca) ) {
940
+ VALUE nan = rb_funcall(self, rb_intern("is_nan"), 0);
941
+ return RTEST(rb_funcall(nan, rb_intern("any"), 0));
942
+ }
943
+ return 0;
944
+ }
945
+
946
+ /* ---- the buffered walk ------------------------------------------------
947
+ Each fiber is read once. The cells that come before the cut (the last
948
+ of the cap cells kept so far, in (order, position)) are collected in a
949
+ buffer of a few times cap; when it fills, a selection keeps the first
950
+ cap and the cut moves up. The work area is the buffer, whatever the
951
+ length of the fiber, and a fiber in order costs one selection per
952
+ bufferful instead of a heap update per cell.
953
+
954
+ The walk is written once as a macro over three others: CLASS(i) (0 a
955
+ value, 1 NaN, 2 masked), AFTER(i, j) (cell i comes after cell j in
956
+ (order, position)) and ENTERS(j) (cell j comes before the cut), and
957
+ instantiated per type so that the comparisons
958
+ are inline. The fixlen type takes the generic comparison. */
959
+
960
+ /* Rearranges b[0..nb) so that b[0..cap) hold the cap cells first in
961
+ (order, position): quickselect with median-of-three pivots; past a
962
+ depth limit the remaining range is sorted (tmp holds nb positions). */
963
+ #define CA_TOPK_SELECT(AFTER) \
964
+ do { \
965
+ lo = 0; hi = nb; \
966
+ depth = 2; \
967
+ for (s = nb; s > 1; s >>= 1) depth += 2; \
968
+ while ( hi - lo > 16 ) { \
969
+ mid = lo + ( hi - lo ) / 2; last = hi - 1; \
970
+ if ( depth-- == 0 ) break; \
971
+ if ( AFTER(b[lo], b[mid]) ) { t = b[lo]; b[lo] = b[mid]; b[mid] = t; } \
972
+ if ( AFTER(b[mid], b[last]) ) { t = b[mid]; b[mid] = b[last]; b[last] = t; } \
973
+ if ( AFTER(b[lo], b[mid]) ) { t = b[lo]; b[lo] = b[mid]; b[mid] = t; } \
974
+ t = b[mid]; b[mid] = b[last]; b[last] = t; \
975
+ for (s = lo, i = lo; i < last; i++) { \
976
+ if ( AFTER(b[last], b[i]) ) { t = b[s]; b[s] = b[i]; b[i] = t; s++; } \
977
+ } \
978
+ t = b[s]; b[s] = b[last]; b[last] = t; \
979
+ if ( s == cap || s + 1 == cap ) { lo = hi; break; } \
980
+ if ( cap < s ) hi = s; \
981
+ else lo = s + 1; \
982
+ } \
983
+ if ( hi > lo ) ca_topk_sort(f, b + lo, tmp, hi - lo); \
984
+ } while (0)
985
+
986
+ #define CA_TOPK_DEFINE_BUF(NAME, T, CLASS, AFTER, ENTERS) \
987
+ static void \
988
+ NAME (const ca_topk_fiber_t *f, ca_size_t len, ca_size_t cap, \
989
+ ca_size_t *b, ca_size_t *tmp, ca_size_t size) \
990
+ { \
991
+ const T *v_ = (const T *) f->v; \
992
+ ca_size_t j, nb = 0, cut = 0, lo, hi, i, s, t, mid, last; \
993
+ int rc_ = 3, depth; \
994
+ T rv_; \
995
+ (void) v_; \
996
+ memset(&rv_, 0, sizeof(rv_)); \
997
+ for (j = 0; j < len; j++) { \
998
+ if ( rc_ != 3 && ! ( ENTERS(j) ) ) continue; \
999
+ b[nb++] = j; \
1000
+ if ( nb == size ) { \
1001
+ CA_TOPK_SELECT(AFTER); \
1002
+ nb = cap; \
1003
+ } \
1004
+ else if ( ! ( nb == cap && rc_ == 3 ) ) { /* the first cap cells */ \
1005
+ continue; \
1006
+ } \
1007
+ cut = b[0]; \
1008
+ for (i = 1; i < cap; i++) if ( AFTER(b[i], cut) ) cut = b[i]; \
1009
+ rc_ = CLASS(cut); \
1010
+ if ( rc_ == 0 ) memcpy(&rv_, f->v + cut * f->bytes, sizeof(rv_)); \
1011
+ } \
1012
+ if ( nb > cap ) CA_TOPK_SELECT(AFTER); \
1013
+ ca_topk_sort(f, b, tmp, cap); \
1014
+ }
1015
+
1016
+ /* The typed comparisons. A cell enters when it comes before the cut;
1017
+ one that ties the cut does not, it lies later. */
1018
+ #define CA_TOPK_CLASS_INT(i) ( ( f->m && f->m[i] ) ? 2 : 0 )
1019
+ #define CA_TOPK_CLASS_FLT(i) ( ( f->m && f->m[i] ) ? 2 : ( isnan(v_[i]) ? 1 : 0 ) )
1020
+ #define CA_TOPK_AFTER_V(CLASS, i, j) \
1021
+ ( CLASS(i) != CLASS(j) ? CLASS(i) > CLASS(j) \
1022
+ : ( CLASS(i) == 0 && v_[i] != v_[j] ) \
1023
+ ? ( f->desc ? v_[i] < v_[j] : v_[i] > v_[j] ) \
1024
+ : (i) > (j) )
1025
+ #define CA_TOPK_AFTER_INT(i, j) CA_TOPK_AFTER_V(CA_TOPK_CLASS_INT, i, j)
1026
+ #define CA_TOPK_AFTER_FLT(i, j) CA_TOPK_AFTER_V(CA_TOPK_CLASS_FLT, i, j)
1027
+ #define CA_TOPK_ENTERS_V(CLASS, j) \
1028
+ ( CLASS(j) < rc_ || \
1029
+ ( CLASS(j) == 0 && rc_ == 0 && \
1030
+ ( f->desc ? v_[j] > rv_ : v_[j] < rv_ ) ) )
1031
+ #define CA_TOPK_ENTERS_INT(j) CA_TOPK_ENTERS_V(CA_TOPK_CLASS_INT, j)
1032
+ #define CA_TOPK_ENTERS_FLT(j) CA_TOPK_ENTERS_V(CA_TOPK_CLASS_FLT, j)
1033
+
1034
+ CA_TOPK_DEFINE_BUF(ca_topk_buf_u8, uint8_t, CA_TOPK_CLASS_INT, CA_TOPK_AFTER_INT, CA_TOPK_ENTERS_INT)
1035
+ CA_TOPK_DEFINE_BUF(ca_topk_buf_i8, int8_t, CA_TOPK_CLASS_INT, CA_TOPK_AFTER_INT, CA_TOPK_ENTERS_INT)
1036
+ CA_TOPK_DEFINE_BUF(ca_topk_buf_i16, int16_t, CA_TOPK_CLASS_INT, CA_TOPK_AFTER_INT, CA_TOPK_ENTERS_INT)
1037
+ CA_TOPK_DEFINE_BUF(ca_topk_buf_u16, uint16_t, CA_TOPK_CLASS_INT, CA_TOPK_AFTER_INT, CA_TOPK_ENTERS_INT)
1038
+ CA_TOPK_DEFINE_BUF(ca_topk_buf_i32, int32_t, CA_TOPK_CLASS_INT, CA_TOPK_AFTER_INT, CA_TOPK_ENTERS_INT)
1039
+ CA_TOPK_DEFINE_BUF(ca_topk_buf_u32, uint32_t, CA_TOPK_CLASS_INT, CA_TOPK_AFTER_INT, CA_TOPK_ENTERS_INT)
1040
+ CA_TOPK_DEFINE_BUF(ca_topk_buf_i64, int64_t, CA_TOPK_CLASS_INT, CA_TOPK_AFTER_INT, CA_TOPK_ENTERS_INT)
1041
+ CA_TOPK_DEFINE_BUF(ca_topk_buf_u64, uint64_t, CA_TOPK_CLASS_INT, CA_TOPK_AFTER_INT, CA_TOPK_ENTERS_INT)
1042
+ CA_TOPK_DEFINE_BUF(ca_topk_buf_f32, float, CA_TOPK_CLASS_FLT, CA_TOPK_AFTER_FLT, CA_TOPK_ENTERS_FLT)
1043
+ CA_TOPK_DEFINE_BUF(ca_topk_buf_f64, double, CA_TOPK_CLASS_FLT, CA_TOPK_AFTER_FLT, CA_TOPK_ENTERS_FLT)
1044
+
1045
+ /* fixlen: the generic comparison; v_ is unused. */
1046
+ #define CA_TOPK_CLASS_GEN(i) ca_topk_class(f, i)
1047
+ #define CA_TOPK_AFTER_GEN(i, j) ca_topk_after(f, i, j)
1048
+ #define CA_TOPK_ENTERS_GEN(j) ( ca_topk_cmp(f, j, cut) < 0 )
1049
+
1050
+ /* Whether cell i comes after cell j in (order, position). */
1051
+ static int
1052
+ ca_topk_after (const ca_topk_fiber_t *f, ca_size_t i, ca_size_t j)
1053
+ {
1054
+ int r = ca_topk_cmp(f, i, j);
1055
+ return r > 0 || ( r == 0 && i > j );
1056
+ }
1057
+
1058
+ CA_TOPK_DEFINE_BUF(ca_topk_buf_gen, char, CA_TOPK_CLASS_GEN, CA_TOPK_AFTER_GEN, CA_TOPK_ENTERS_GEN)
1059
+
1060
+ #undef CA_TOPK_DEFINE_BUF
1061
+ #undef CA_TOPK_SELECT
1062
+ #undef CA_TOPK_CLASS_INT
1063
+ #undef CA_TOPK_CLASS_FLT
1064
+ #undef CA_TOPK_AFTER_V
1065
+ #undef CA_TOPK_AFTER_INT
1066
+ #undef CA_TOPK_AFTER_FLT
1067
+ #undef CA_TOPK_ENTERS_V
1068
+ #undef CA_TOPK_ENTERS_INT
1069
+ #undef CA_TOPK_ENTERS_FLT
1070
+ #undef CA_TOPK_CLASS_GEN
1071
+ #undef CA_TOPK_AFTER_GEN
1072
+ #undef CA_TOPK_ENTERS_GEN
1073
+
1074
+ /* The first cap cells of one fiber in (order, position), sorted, into
1075
+ b[0..cap). b and tmp hold size positions, cap < size <= len. */
1076
+ static void
1077
+ ca_topk_buf_fiber (const ca_topk_fiber_t *f, ca_size_t len, ca_size_t cap,
1078
+ ca_size_t *b, ca_size_t *tmp, ca_size_t size)
1079
+ {
1080
+ switch ( f->type ) {
1081
+ case CA_BOOLEAN:
1082
+ case CA_UINT8: ca_topk_buf_u8(f, len, cap, b, tmp, size); break;
1083
+ case CA_INT8: ca_topk_buf_i8(f, len, cap, b, tmp, size); break;
1084
+ case CA_INT16: ca_topk_buf_i16(f, len, cap, b, tmp, size); break;
1085
+ case CA_UINT16: ca_topk_buf_u16(f, len, cap, b, tmp, size); break;
1086
+ case CA_INT32: ca_topk_buf_i32(f, len, cap, b, tmp, size); break;
1087
+ case CA_UINT32: ca_topk_buf_u32(f, len, cap, b, tmp, size); break;
1088
+ case CA_INT64: ca_topk_buf_i64(f, len, cap, b, tmp, size); break;
1089
+ case CA_UINT64: ca_topk_buf_u64(f, len, cap, b, tmp, size); break;
1090
+ case CA_FLOAT32: ca_topk_buf_f32(f, len, cap, b, tmp, size); break;
1091
+ case CA_FLOAT64: ca_topk_buf_f64(f, len, cap, b, tmp, size); break;
1092
+ default: ca_topk_buf_gen(f, len, cap, b, tmp, size); break;
1093
+ }
1094
+ }
1095
+
1096
+ /* Per-axis top-k positions of `key` (an order key: not object, not
1097
+ complex) by the buffered walk, for cap < the length of the axis. The
1098
+ fibers come in the row-major order of the other axes, so the answer is
1099
+ filled with the axis last and moved into place at the end (cap cells
1100
+ per fiber). */
1101
+ static VALUE
1102
+ ca_topk_buffered_positions (VALUE key, long cap, long axis, int desc)
1103
+ {
1104
+ volatile VALUE holder = 0, out;
1105
+ VALUE perm[CA_RANK_MAX];
1106
+ ca_size_t odim[CA_RANK_MAX];
1107
+ CArray *ck, *co;
1108
+ ca_iter_state st;
1109
+ ca_topk_fiber_t f;
1110
+ ca_size_t *buf, *tmp, n, o = 0, k, len, size;
1111
+ int64_t *op;
1112
+ boolean8_t *m;
1113
+ char *p;
1114
+ int ndim, i, j = 0;
1115
+
1116
+ GetCArray(key, ck);
1117
+ ndim = ck->ndim;
1118
+ for (i = 0; i < ndim; i++) if ( i != axis ) odim[j++] = ck->dim[i];
1119
+ odim[ndim - 1] = cap;
1120
+ len = ck->dim[axis];
1121
+ out = rb_carray_new(CA_INT64, ndim, odim, 0, NULL);
1122
+ GetCArray(out, co);
1123
+ op = (int64_t *) co->ptr;
1124
+
1125
+ f.type = ck->data_type;
1126
+ f.bytes = ck->bytes;
1127
+ f.desc = desc;
1128
+ size = ( len < 4 * cap + 256 ) ? len : 4 * cap + 256;
1129
+ buf = ALLOCV_N(ca_size_t, holder, 2 * size);
1130
+ tmp = buf + size;
1131
+ CA_FOR_EACH_FIBER_MASKED(st, ck, axis, 0, p, n, m) {
1132
+ f.v = p;
1133
+ f.m = m;
1134
+ ca_topk_buf_fiber(&f, n, cap, buf, tmp, size);
1135
+ for (k = 0; k < cap; k++) op[o * cap + k] = (int64_t) buf[k];
1136
+ o++;
1137
+ }
1138
+ ALLOCV_END(holder);
1139
+ if ( axis == ndim - 1 ) return out;
1140
+ for (i = 0; i < ndim; i++) {
1141
+ perm[i] = INT2FIX( i < axis ? i : ( i == axis ? ndim - 1 : i - 1 ) );
1142
+ }
1143
+ return rb_ca_copy(rb_funcallv(out, rb_intern("transpose"), ndim, perm));
1144
+ }
1145
+
664
1146
  /* Per-axis top-k positions (cap clamped, axis normalized).
665
1147
  * Called by rb_ca_topk_index below. */
666
1148
  static VALUE
@@ -671,12 +1153,44 @@ rb_ca_topk_positions (VALUE self, long cap, long axis_norm, int desc)
671
1153
  long dim_axis = (long) ca->dim[axis_norm];
672
1154
  VALUE raxis_norm = LONG2NUM(axis_norm);
673
1155
 
1156
+ /* The buffered walk reads any key but an object (its cells compare in
1157
+ Ruby) or a complex one (no order), and keeps cap cells per fiber. */
1158
+ if ( ca->data_type != CA_OBJECT && ! ca_is_complex_type(ca) ) {
1159
+ VALUE key = ca_order_key(self, desc ? "nlargest" : "nsmallest");
1160
+ CArray *ck;
1161
+ GetCArray(key, ck);
1162
+ if ( ck->data_type != CA_OBJECT && ! ca_is_complex_type(ck) &&
1163
+ cap < dim_axis ) {
1164
+ return ca_topk_buffered_positions(key, cap, axis_norm, desc);
1165
+ }
1166
+ }
1167
+
1168
+ /* The largest: reversing the ascending order would put the NaN and the
1169
+ masked cells first, so rank them descending (NaN, then the masked
1170
+ cells, last) when there are any. An object array is ranked by
1171
+ `order` both ways (its cells compare in Ruby). */
1172
+ if ( ca->data_type == CA_OBJECT ||
1173
+ ( desc && ca_topk_has_cells_after_numbers(self, ca) ) ) {
1174
+ VALUE kw = rb_hash_new();
1175
+ rb_hash_aset(kw, ID2SYM(rb_intern("axis")), raxis_norm);
1176
+ rb_hash_aset(kw, ID2SYM(rb_intern("descending")), desc ? Qtrue : Qfalse);
1177
+ VALUE rank = rb_funcallv_kw(self, rb_intern("order"), 1, &kw, RB_PASS_KEYWORDS);
1178
+ VALUE full = rb_ca_sort_index_ki_quick(rank, raxis_norm);
1179
+ if ( cap >= dim_axis ) {
1180
+ return rb_ca_copy(full);
1181
+ }
1182
+ int ndim = (int) ca->ndim;
1183
+ VALUE spec[CA_RANK_MAX];
1184
+ for ( int k = 0; k < ndim; k++ ) {
1185
+ spec[k] = Qnil;
1186
+ }
1187
+ spec[axis_norm] = rb_range_new(INT2FIX(0), LONG2NUM(cap), 1);
1188
+ return rb_ca_copy(rb_ca_fetch2(full, ndim, spec));
1189
+ }
1190
+
674
1191
  if ( cap >= dim_axis ) {
675
1192
  VALUE full = rb_ca_sort_index_ki_quick(self, raxis_norm);
676
- if ( desc ) {
677
- full = rb_ca_flip_axis(full, NUM2LONG(raxis_norm));
678
- }
679
- return rb_ca_copy(full);
1193
+ return ca_topk_stable(self, full, axis_norm, desc);
680
1194
  }
681
1195
 
682
1196
  long pivot = desc ? (dim_axis - cap) : (cap - 1);
@@ -703,7 +1217,7 @@ rb_ca_topk_positions (VALUE self, long cap, long axis_norm, int desc)
703
1217
  order = rb_ca_flip_axis(order, NUM2LONG(raxis_norm));
704
1218
  }
705
1219
  VALUE picked = rb_ca_take_along_axis_c(partidx_top, order, raxis_norm);
706
- return rb_ca_copy(picked);
1220
+ return ca_topk_stable(self, picked, axis_norm, desc);
707
1221
  }
708
1222
 
709
1223
  /* Dispatcher: normalize axis, clamp cap, empty-cap corner.
@@ -807,11 +1321,10 @@ rb_ca_nsmallest (int argc, VALUE *argv, VALUE self)
807
1321
  * ever compare equal), so a later sort_addr key never gets consulted;
808
1322
  * dense ranks preserve ties, so it does.
809
1323
  *
810
- * descending: still works under method: :dense via the same
811
- * (n-1)-ascending transform below: n is fixed per-fiber, so within a
812
- * tied group ascending values are equal and (n-1)-ascending stays
813
- * equal too -- ties survive the transform, and the group-to-group
814
- * order still reverses correctly. */
1324
+ * descending: ranks the largest value 0, with NaN after the numbers as
1325
+ * ascending. The kernel gives the descending dense rank; the ordinal rank
1326
+ * is the ascending ordinal rank of it, so tied cells keep their original
1327
+ * order in both directions. */
815
1328
 
816
1329
  static VALUE
817
1330
  rb_ca_order (int argc, VALUE *argv, VALUE self)
@@ -826,11 +1339,24 @@ rb_ca_order (int argc, VALUE *argv, VALUE self)
826
1339
  int do_stable = NIL_P(rkind) ? 0
827
1340
  : ca_symbol_choice(rkind, "kind", "quick", "stable", NULL);
828
1341
 
829
- VALUE asc, n;
1342
+ if ( RTEST(rdesc) && ! dense ) {
1343
+ VALUE kw = rb_hash_new();
1344
+ rb_hash_aset(kw, ID2SYM(rb_intern("axis")), raxis);
1345
+ rb_hash_aset(kw, ID2SYM(rb_intern("descending")), Qtrue);
1346
+ rb_hash_aset(kw, ID2SYM(rb_intern("method")), ID2SYM(rb_intern("dense")));
1347
+ rb_hash_aset(kw, ID2SYM(rb_intern("kind")), do_stable ? ID2SYM(rb_intern("stable")) : ID2SYM(rb_intern("quick")));
1348
+ VALUE ranked = rb_funcallv_kw(self, rb_intern("order"), 1, &kw, RB_PASS_KEYWORDS);
1349
+ rb_hash_delete(kw, ID2SYM(rb_intern("descending")));
1350
+ rb_hash_delete(kw, ID2SYM(rb_intern("method")));
1351
+ return rb_funcallv_kw(ranked, rb_intern("order"), 1, &kw, RB_PASS_KEYWORDS);
1352
+ }
1353
+
1354
+ /* The kernel's dense == 2 is the descending dense rank. */
1355
+ int kmode = ( dense && RTEST(rdesc) ) ? 2 : dense;
830
1356
  if ( NIL_P(raxis) ) {
831
1357
  VALUE flat = rb_ca_flatten(self);
832
1358
  /* Direct C-level dispatch via c_callable: true extern (mkkernel). */
833
- VALUE ranked = rb_ca_rank_index_ki_kind_dense(flat, INT2FIX(0), do_stable, dense);
1359
+ VALUE ranked = rb_ca_rank_index_ki_kind_dense(flat, INT2FIX(0), do_stable, kmode);
834
1360
  /* reshape(*shape) */
835
1361
  CArray *ca;
836
1362
  TypedData_Get_Struct(self, CArray, &carray_data_type, ca);
@@ -838,43 +1364,12 @@ rb_ca_order (int argc, VALUE *argv, VALUE self)
838
1364
  for ( long k = 0; k < (long) ca->ndim; k++ ) {
839
1365
  shape_argv[k] = LONG2NUM((long) ca->dim[k]);
840
1366
  }
841
- asc = rb_ca_reshape((int) ca->ndim, shape_argv, ranked);
842
- n = RTEST(rb_ca_has_mask(self))
843
- ? SIZE2NUM(ca_count_not_masked(ca)) /* = elements - count_masked */
844
- : rb_ca_elements(self);
845
- } else {
846
- long axis_norm = rb_ca_normalize_axis_value(self, raxis, "order");
847
- VALUE raxis_norm = LONG2NUM(axis_norm);
848
- asc = rb_ca_rank_index_ki_kind_dense(self, raxis_norm, do_stable, dense);
849
- if ( RTEST(rb_ca_has_mask(self)) ) {
850
- n = rb_ca_count_not_masked_c(self, raxis_norm);
851
- } else {
852
- CArray *ca;
853
- TypedData_Get_Struct(self, CArray, &carray_data_type, ca);
854
- n = LONG2NUM((long) ca->dim[axis_norm]);
855
- }
856
- if ( RTEST(rb_obj_is_kind_of(n, rb_cCArray)) ) {
857
- VALUE ia_argv[1] = { raxis_norm };
858
- n = rb_ca_insert_axis(1, ia_argv, n);
859
- }
860
- }
861
-
862
- VALUE diff;
863
- if ( RTEST(rdesc) ) {
864
- /* `-` via rb_funcall handles both Integer-Integer and
865
- CArray/Integer mixed receivers uniformly via Ruby's operator
866
- dispatch. No single C entry covers both. */
867
- VALUE n_minus_1 = rb_funcall(n, id_sub, 1, INT2FIX(1));
868
- diff = rb_funcall(n_minus_1, id_sub, 1, asc);
869
- } else {
870
- diff = asc;
1367
+ return rb_ca_reshape((int) ca->ndim, shape_argv, ranked);
871
1368
  }
872
- /* Output data_type: CA_SIZE (= ca_size_t, int64 on 64-bit builds),
873
- matching the rest of the *_index family (sort_index / partition_index
874
- / rank_index) that order is built on. No explicit cast needed here:
875
- asc (rank_index_ki's output) is already CA_SIZE, and Integer - CArray
876
- promotion in the descending branch preserves it. */
877
- return diff;
1369
+ long axis_norm = rb_ca_normalize_axis_value(self, raxis, "order");
1370
+ /* Output data_type: CA_SIZE (int64 on 64-bit builds), as the rest of the
1371
+ *_index family. */
1372
+ return rb_ca_rank_index_ki_kind_dense(self, LONG2NUM(axis_norm), do_stable, kmode);
878
1373
  }
879
1374
 
880
1375
  /* ---- linear_section / linear_fetch ----------------------------------- */
@@ -896,9 +1391,7 @@ ca_linear_prep (VALUE self, VALUE raxis, const char *name, long *axis_out)
896
1391
  Face) is caught by the rb_ca_to_float64 cast below, so no separate
897
1392
  float-numeric flag is needed. */
898
1393
  if ( ca_is_face(ca) ) {
899
- if ( ! ca_test_flag(ca, CA_FLAG_FACE_ORDERABLE_STORAGE) ) {
900
- ca_face_refuse_order(name, self, "order");
901
- }
1394
+ ca_face_require_orderable(self, name, "order");
902
1395
  self = rb_ca_strip_face_value(self);
903
1396
  TypedData_Get_Struct(self, CArray, &carray_data_type, ca);
904
1397
  }
@@ -925,13 +1418,26 @@ rb_ca_linear_section_m (int argc, VALUE *argv, VALUE self)
925
1418
  rb_check_arity(argc, 1, 1);
926
1419
  VALUE val = argv[0];
927
1420
 
928
- /* Query gate: the reference axis, still a Face here, reconciles the query
929
- into its own space (ca_face_reconcile_operand). */
1421
+ /* Query gate: the reference axis, still a Face here, and the query are
1422
+ brought into one space (ca_face_reconcile_pair). */
930
1423
  if ( rb_obj_is_carray(self) ) {
931
1424
  CArray *sca;
932
1425
  TypedData_Get_Struct(self, CArray, &carray_data_type, sca);
933
1426
  if ( ca_is_face(sca) ) {
934
- val = ca_face_reconcile_operand(self, val, "linear_section");
1427
+ volatile VALUE ref = self, query = val;
1428
+ ca_face_reconcile_pair(&ref, &query, "linear_section");
1429
+ self = ref;
1430
+ val = query;
1431
+ }
1432
+ }
1433
+
1434
+ {
1435
+ CArray *ca;
1436
+ TypedData_Get_Struct(self, CArray, &carray_data_type, ca);
1437
+ if ( ca_is_complex_type(ca) ) {
1438
+ rb_raise(rb_eCADataTypeError,
1439
+ "linear_section: a %s array has no order (complex numbers are "
1440
+ "not ordered)", ca_type_name[ca->data_type]);
935
1441
  }
936
1442
  }
937
1443
 
@@ -960,6 +1466,27 @@ rb_ca_linear_fetch_m (int argc, VALUE *argv, VALUE self)
960
1466
  rb_check_arity(argc, 1, 1);
961
1467
  VALUE addr = argv[0];
962
1468
 
1469
+ /* A complex array interpolates its real and imaginary parts: the
1470
+ weights are real, so each part is a real interpolation. */
1471
+ {
1472
+ CArray *ca;
1473
+ TypedData_Get_Struct(self, CArray, &carray_data_type, ca);
1474
+ if ( ca_is_complex_type(ca) ) {
1475
+ VALUE args[2], re, im;
1476
+ int n = 1;
1477
+ args[0] = addr;
1478
+ if ( ! NIL_P(ropt) ) args[n++] = ropt;
1479
+ re = rb_funcallv_kw(rb_funcall(self, rb_intern("real"), 0),
1480
+ rb_intern("linear_fetch"), n, args,
1481
+ NIL_P(ropt) ? RB_NO_KEYWORDS : RB_PASS_KEYWORDS);
1482
+ im = rb_funcallv_kw(rb_funcall(self, rb_intern("imag"), 0),
1483
+ rb_intern("linear_fetch"), n, args,
1484
+ NIL_P(ropt) ? RB_NO_KEYWORDS : RB_PASS_KEYWORDS);
1485
+ return rb_funcall(re, '+', 1,
1486
+ rb_funcall(im, '*', 1, rb_Complex(INT2FIX(0), INT2FIX(1))));
1487
+ }
1488
+ }
1489
+
963
1490
  long axis_norm = 0;
964
1491
  VALUE sc = ca_linear_prep(self, raxis, "linear_fetch", &axis_norm);
965
1492
  return rb_ca_linear_fetch_ki(sc, addr, LONG2NUM(axis_norm));