carray 3.0.3 → 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 (212) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +264 -0
  3. data/README.md +3 -2
  4. data/carray.gemspec +1 -1
  5. data/ext/ca_axis_descriptor.h +27 -24
  6. data/ext/ca_axis_dispatch.c +1 -6
  7. data/ext/ca_axis_group.c +27 -27
  8. data/ext/ca_binop_dispatch.c +1 -29
  9. data/ext/ca_binop_dispatch.h +0 -1
  10. data/ext/ca_categorical_iterator.c +205 -134
  11. data/ext/ca_compare.c +7 -18
  12. data/ext/ca_group_iter.c +16 -17
  13. data/ext/ca_kernel_iterator.c +1234 -1994
  14. data/ext/ca_kernel_iterator.h +391 -437
  15. data/ext/ca_monop_dispatch.c +82 -59
  16. data/ext/ca_monop_dispatch.h +1 -0
  17. data/ext/ca_obj_array.c +96 -39
  18. data/ext/ca_obj_bincmp.c +24 -21
  19. data/ext/ca_obj_binop.c +15 -26
  20. data/ext/ca_obj_bitarray.c +3 -2
  21. data/ext/ca_obj_bitfield.c +5 -6
  22. data/ext/ca_obj_block.c +18 -9
  23. data/ext/ca_obj_byte_swap.c +5 -6
  24. data/ext/ca_obj_const_string.c +173 -19
  25. data/ext/ca_obj_face.c +238 -57
  26. data/ext/ca_obj_face.h +19 -0
  27. data/ext/ca_obj_fake.c +15 -29
  28. data/ext/ca_obj_farray.c +6 -3
  29. data/ext/ca_obj_field.c +17 -5
  30. data/ext/ca_obj_fixlen_string.c +2 -3
  31. data/ext/ca_obj_grid.c +406 -132
  32. data/ext/ca_obj_meld.c +172 -34
  33. data/ext/ca_obj_moncmp.c +23 -8
  34. data/ext/ca_obj_monop.c +67 -57
  35. data/ext/ca_obj_object.c +157 -51
  36. data/ext/ca_obj_record.c +12 -7
  37. data/ext/ca_obj_reduce.c +13 -8
  38. data/ext/ca_obj_refer.c +25 -27
  39. data/ext/ca_obj_remap.c +30 -9
  40. data/ext/ca_obj_repeat.c +9 -10
  41. data/ext/ca_obj_roll.c +6 -5
  42. data/ext/ca_obj_select.c +14 -9
  43. data/ext/ca_obj_select_axis.c +15 -47
  44. data/ext/ca_obj_shift.c +11 -1
  45. data/ext/ca_obj_stack.c +179 -34
  46. data/ext/ca_obj_stride.c +162 -115
  47. data/ext/ca_obj_string.c +71 -4
  48. data/ext/ca_obj_tile.c +6 -5
  49. data/ext/ca_obj_time.c +22 -6
  50. data/ext/ca_obj_timedelta.c +22 -6
  51. data/ext/ca_obj_transpose.c +5 -3
  52. data/ext/ca_obj_triop.c +23 -32
  53. data/ext/ca_obj_window.c +190 -26
  54. data/ext/ca_sweep_engine.c +2 -2
  55. data/ext/caframe_csv_reader.c +80 -1
  56. data/ext/carray.h +181 -75
  57. data/ext/carray_access.c +184 -96
  58. data/ext/carray_address_basis.c +343 -87
  59. data/ext/carray_attribute.c +89 -6
  60. data/ext/carray_bincount.c +10 -8
  61. data/ext/carray_broadcast.c +13 -8
  62. data/ext/carray_build_flags.h +3 -0
  63. data/ext/carray_call_cfunc.c +108 -0
  64. data/ext/carray_cast.c +142 -24
  65. data/ext/carray_cast_func.rb +46 -6
  66. data/ext/carray_class.c +4 -3
  67. data/ext/carray_config.h +28 -0
  68. data/ext/carray_conversion.c +16 -7
  69. data/ext/carray_copy.c +51 -14
  70. data/ext/carray_core.c +147 -55
  71. data/ext/carray_count.c +50 -20
  72. data/ext/carray_element.c +76 -33
  73. data/ext/carray_factorize.c +118 -64
  74. data/ext/carray_generate.c +191 -50
  75. data/ext/carray_histogram.c +62 -17
  76. data/ext/carray_hold.c +12 -15
  77. data/ext/carray_index_classifier.c +388 -176
  78. data/ext/carray_internal.h +336 -29
  79. data/ext/carray_kernels_bincmp.c +4453 -0
  80. data/ext/carray_kernels_binop.c +11368 -0
  81. data/ext/carray_kernels_init.c +1734 -0
  82. data/ext/carray_kernels_moncmp.c +2104 -0
  83. data/ext/carray_kernels_monop.c +18369 -0
  84. data/ext/carray_kernels_reduce_aggregate.c +23818 -0
  85. data/ext/carray_kernels_reduce_boolean.c +406 -0
  86. data/ext/carray_kernels_reduce_cumulative.c +10086 -0
  87. data/ext/carray_kernels_reduce_extreme.c +17634 -0
  88. data/ext/carray_kernels_reduce_variance.c +3849 -0
  89. data/ext/carray_kernels_scan.c +4302 -0
  90. data/ext/carray_kernels_search.c +33829 -0
  91. data/ext/carray_kernels_sort.c +10906 -0
  92. data/ext/carray_kernels_triop.c +1864 -0
  93. data/ext/carray_key_windows.c +216 -0
  94. data/ext/carray_lazy.c +170 -111
  95. data/ext/carray_mask.c +292 -72
  96. data/ext/carray_median_percentile.c +82 -28
  97. data/ext/carray_memory_view.c +669 -561
  98. data/ext/carray_operator.c +124 -47
  99. data/ext/carray_order.c +384 -76
  100. data/ext/carray_partition.c +28 -27
  101. data/ext/carray_random.c +67 -13
  102. data/ext/carray_round.c +273 -0
  103. data/ext/carray_scatter.c +63 -38
  104. data/ext/carray_segment.c +3 -3
  105. data/ext/carray_slab.c +91 -28
  106. data/ext/carray_sort.c +35 -35
  107. data/ext/carray_struct.c +19 -18
  108. data/ext/carray_test.c +26 -5
  109. data/ext/carray_undef.c +4 -0
  110. data/ext/carray_utils.c +246 -45
  111. data/ext/mk_call_cfunc.rb +19 -2
  112. data/ext/mkkernel.rb +198 -240
  113. data/ext/ruby_carray.c +10 -4
  114. data/ext/version.h +4 -4
  115. data/lib/carray/attribute.rb +126 -22
  116. data/lib/carray/autoload_carray.rb +8 -31
  117. data/lib/carray/autoload_method_extension.rb +14 -5
  118. data/lib/carray/axis_group.rb +255 -106
  119. data/lib/carray/basics.rb +78 -10
  120. data/lib/carray/bincount_nd.rb +12 -2
  121. data/lib/carray/block_iterator.rb +126 -23
  122. data/lib/carray/boolean_reduce.rb +17 -17
  123. data/lib/carray/categorical.rb +165 -43
  124. data/lib/carray/categorical_iterator.rb +130 -27
  125. data/lib/carray/complex.rb +5 -5
  126. data/lib/carray/conditional.rb +67 -18
  127. data/lib/carray/const_string.rb +14 -5
  128. data/lib/carray/construct.rb +15 -11
  129. data/lib/carray/data_type_extension.rb +1 -0
  130. data/lib/carray/distance_limit.rb +77 -0
  131. data/lib/carray/fixlen_string.rb +7 -1
  132. data/lib/carray/frame/column_verbs.rb +70 -0
  133. data/lib/carray/frame/convert.rb +69 -0
  134. data/lib/carray/frame/csv_parser.rb +111 -17
  135. data/lib/carray/frame/describe.rb +3 -2
  136. data/lib/carray/frame/frame.rb +246 -21
  137. data/lib/carray/frame/group.rb +510 -95
  138. data/lib/carray/frame/io.rb +115 -28
  139. data/lib/carray/frame/join.rb +312 -51
  140. data/lib/carray/frame/records.rb +83 -7
  141. data/lib/carray/frame/reshape.rb +42 -35
  142. data/lib/carray/frame/sort.rb +7 -19
  143. data/lib/carray/frame/verbs.rb +188 -48
  144. data/lib/carray/frame/windows.rb +117 -0
  145. data/lib/carray/frame.rb +2 -0
  146. data/lib/carray/fuse_source.rb +11 -4
  147. data/lib/carray/fusion.rb +40 -16
  148. data/lib/carray/histogram.rb +6 -1
  149. data/lib/carray/inspect.rb +31 -8
  150. data/lib/carray/iterator.rb +85 -7
  151. data/lib/carray/key_windows.rb +620 -0
  152. data/lib/carray/lazy.rb +182 -183
  153. data/lib/carray/mask_gap_fill.rb +63 -57
  154. data/lib/carray/meld_reduce.rb +12 -14
  155. data/lib/carray/methods/bin.rb +26 -18
  156. data/lib/carray/methods/bincount.rb +16 -0
  157. data/lib/carray/methods/bit_string.rb +1 -1
  158. data/lib/carray/methods/choose.rb +16 -3
  159. data/lib/carray/methods/composition.rb +3 -1
  160. data/lib/carray/methods/gather_nd.rb +14 -5
  161. data/lib/carray/methods/insert_block.rb +2 -2
  162. data/lib/carray/methods/is_between.rb +59 -0
  163. data/lib/carray/methods/is_in.rb +53 -36
  164. data/lib/carray/methods/join.rb +1 -6
  165. data/lib/carray/methods/lag.rb +49 -0
  166. data/lib/carray/methods/locate_addr.rb +47 -51
  167. data/lib/carray/methods/mode.rb +1 -1
  168. data/lib/carray/methods/nunique.rb +11 -6
  169. data/lib/carray/methods/resize.rb +3 -3
  170. data/lib/carray/methods/value_counts.rb +4 -11
  171. data/lib/carray/record_order.rb +3 -3
  172. data/lib/carray/rng.rb +3 -0
  173. data/lib/carray/runtime.rb +53 -8
  174. data/lib/carray/segment_iterator.rb +313 -82
  175. data/lib/carray/serialize.rb +24 -10
  176. data/lib/carray/slab_iterator.rb +50 -0
  177. data/lib/carray/string.rb +105 -5
  178. data/lib/carray/string_operation_extension.rb +8 -1
  179. data/lib/carray/struct.rb +9 -21
  180. data/lib/carray/struct_builder.rb +19 -8
  181. data/lib/carray/time.rb +726 -104
  182. data/lib/carray/window_iterator.rb +122 -16
  183. data/lib/carray.rb +7 -0
  184. data/yard-stubs/ca_obj_bitfield.rb +1 -1
  185. data/yard-stubs/ca_obj_field.rb +4 -0
  186. data/yard-stubs/ca_obj_grid.rb +21 -10
  187. data/yard-stubs/ca_obj_refer.rb +2 -1
  188. data/yard-stubs/ca_obj_roll.rb +2 -2
  189. data/yard-stubs/ca_obj_shift.rb +5 -1
  190. data/yard-stubs/ca_obj_tile.rb +1 -1
  191. data/yard-stubs/ca_obj_window.rb +8 -3
  192. data/yard-stubs/carray_access.rb +6 -1
  193. data/yard-stubs/carray_attribute.rb +0 -5
  194. data/yard-stubs/carray_cast.rb +10 -0
  195. data/yard-stubs/carray_conversion.rb +2 -1
  196. data/yard-stubs/carray_copy.rb +4 -2
  197. data/yard-stubs/carray_count.rb +4 -4
  198. data/yard-stubs/carray_element.rb +10 -4
  199. data/yard-stubs/carray_generate.rb +21 -1
  200. data/yard-stubs/carray_mask.rb +26 -2
  201. data/yard-stubs/carray_math.rb +27 -1
  202. data/yard-stubs/carray_median_percentile.rb +5 -2
  203. data/yard-stubs/carray_memory_view.rb +40 -23
  204. data/yard-stubs/carray_order.rb +29 -18
  205. data/yard-stubs/carray_random.rb +13 -4
  206. data/yard-stubs/carray_scatter.rb +14 -9
  207. data/yard-stubs/carray_slab.rb +9 -2
  208. data/yard-stubs/carray_sort.rb +6 -5
  209. data/yard-stubs/carray_test.rb +5 -3
  210. metadata +25 -3
  211. data/lib/carray/arrow_tensor.rb +0 -401
  212. 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
@@ -254,12 +258,12 @@ rb_ca_project (int argc, VALUE *argv, VALUE self)
254
258
  lfval = ALLOCV_N(char, hlfval, ca->bytes); /* converting a fill value */
255
259
  ufval = ALLOCV_N(char, hufval, ca->bytes); /* can raise; the unwind frees */
256
260
 
257
- if ( ! NIL_P(vlfval) ) {
261
+ if ( ! NIL_P(vlfval) && vlfval != CA_UNDEF ) {
258
262
  rb_ca_obj2ptr(self, vlfval, lfval);
259
263
  rb_ca_obj2ptr(self, vlfval, ufval);
260
264
  }
261
265
 
262
- if ( ! NIL_P(vufval) ) {
266
+ if ( ! NIL_P(vufval) && vufval != CA_UNDEF ) {
263
267
  rb_ca_obj2ptr(self, vufval, ufval);
264
268
  }
265
269
 
@@ -274,15 +278,19 @@ rb_ca_project (int argc, VALUE *argv, VALUE self)
274
278
  TypedData_Get_Struct(vstorage, CArray, &carray_data_type, ca);
275
279
  }
276
280
 
277
- co = ca_project(ca, ci,
278
- ( ! NIL_P(vlfval) ) ? lfval : NULL,
279
- ( ( ! NIL_P(vufval) ) || ( ! NIL_P(vlfval) ) ) ? ufval : NULL);
281
+ /* A fill of UNDEF is no fill: the cell is masked, as it is with none. */
282
+ {
283
+ VALUE vlower = vlfval;
284
+ VALUE vupper = NIL_P(vufval) ? vlfval : vufval;
285
+ obj = ca_project(ca, ci,
286
+ ( NIL_P(vlower) || vlower == CA_UNDEF ) ? NULL : lfval,
287
+ ( NIL_P(vupper) || vupper == CA_UNDEF ) ? NULL : ufval);
288
+ }
289
+ GetCArray(obj, co);
280
290
 
281
291
  ALLOCV_END(hlfval);
282
292
  ALLOCV_END(hufval);
283
293
 
284
- obj = ca_wrap_struct(co);
285
-
286
294
  if ( ! ca_is_any_masked(co) ) {
287
295
  obj = rb_ca_unmask_copy(obj);
288
296
  }
@@ -291,6 +299,7 @@ rb_ca_project (int argc, VALUE *argv, VALUE self)
291
299
  obj = ca_face_lift(obj, self);
292
300
  }
293
301
 
302
+ rb_ca_inherit_attr(obj, self);
294
303
  return obj;
295
304
  }
296
305
 
@@ -532,6 +541,41 @@ rb_ca_search_nearest_addr_kw (int argc, VALUE *argv, VALUE self)
532
541
 
533
542
  /* ---- sort_by_key family ---------------------------------------------- */
534
543
 
544
+ /* The key orders self's cells one for one, so it has self's shape. */
545
+ static void
546
+ ca_by_key_check_shape (VALUE self, VALUE rkey, const char *name)
547
+ {
548
+ CArray *ca, *ck;
549
+ int8_t i;
550
+ rb_check_carray_object(rkey);
551
+ TypedData_Get_Struct(self, CArray, &carray_data_type, ca);
552
+ TypedData_Get_Struct(rkey, CArray, &carray_data_type, ck);
553
+ if ( ca->ndim == ck->ndim ) {
554
+ for (i = 0; i < ca->ndim; i++) {
555
+ if ( ca->dim[i] != ck->dim[i] ) break;
556
+ }
557
+ if ( i == ca->ndim ) return;
558
+ }
559
+ rb_raise(rb_eArgError, "%s: the key's shape %"PRIsVALUE" differs from "
560
+ "the array's %"PRIsVALUE, name,
561
+ rb_funcall(rkey, rb_intern("shape"), 0),
562
+ rb_funcall(self, rb_intern("shape"), 0));
563
+ }
564
+
565
+ /* self at the addresses an extremum by key found. A fiber with no cell to
566
+ compare has a masked (or UNDEF) address and answers UNDEF. */
567
+ static VALUE
568
+ ca_by_key_fetch (VALUE self, VALUE addrs)
569
+ {
570
+ if ( addrs == CA_UNDEF ) {
571
+ return CA_UNDEF;
572
+ }
573
+ if ( rb_obj_is_carray(addrs) && RTEST(rb_ca_has_mask(addrs)) ) {
574
+ return rb_funcall(self, rb_intern("project"), 1, addrs);
575
+ }
576
+ return rb_ca_fetch(self, addrs);
577
+ }
578
+
535
579
  /* sort_by_key(key, axis: 0) -- gather self at key.sort_addr(axis:). */
536
580
  static VALUE
537
581
  rb_ca_sort_by_key (int argc, VALUE *argv, VALUE self)
@@ -542,6 +586,7 @@ rb_ca_sort_by_key (int argc, VALUE *argv, VALUE self)
542
586
  if ( NIL_P(raxis) ) {
543
587
  raxis = INT2FIX(0);
544
588
  }
589
+ ca_by_key_check_shape(self, rkey, "sort_by_key");
545
590
  VALUE addrs = rb_ca_sort_addr_c(rkey, raxis, 0, 1); /* masked_last=1 (:last), unchanged default */
546
591
  return rb_ca_fetch(self, addrs);
547
592
  }
@@ -553,9 +598,11 @@ rb_ca_max_by_key (int argc, VALUE *argv, VALUE self)
553
598
  VALUE rkey, rkw = Qnil, raxis = Qnil;
554
599
  rb_scan_args(argc, argv, "1:", &rkey, &rkw);
555
600
  rb_scan_options(rkw, "axis", &raxis);
556
- if ( RTEST(rb_ca_is_empty(self)) ) {
557
- return rb_const_get(rb_cObject, rb_intern("UNDEF"));
601
+ ca_by_key_check_shape(self, rkey, "max_by_key");
602
+ if ( NIL_P(raxis) && RTEST(rb_ca_is_empty(self)) ) {
603
+ return CA_UNDEF;
558
604
  }
605
+ rkey = ca_order_key(rkey, "max_by_key");
559
606
 
560
607
  VALUE addrs;
561
608
 
@@ -568,7 +615,7 @@ rb_ca_max_by_key (int argc, VALUE *argv, VALUE self)
568
615
  rb_hash_aset(kw, ID2SYM(id_axis), raxis);
569
616
  addrs = rb_funcallv_kw(rkey, rb_intern("max_addr"), 1, &kw, RB_PASS_KEYWORDS);
570
617
  }
571
- return rb_ca_fetch(self, addrs);
618
+ return ca_by_key_fetch(self, addrs);
572
619
  }
573
620
 
574
621
  /* min_by_key(key, axis: nil) -- self[key.min_addr(axis:)] (or UNDEF if empty). */
@@ -578,9 +625,11 @@ rb_ca_min_by_key (int argc, VALUE *argv, VALUE self)
578
625
  VALUE rkey, rkw = Qnil, raxis = Qnil;
579
626
  rb_scan_args(argc, argv, "1:", &rkey, &rkw);
580
627
  rb_scan_options(rkw, "axis", &raxis);
581
- if ( RTEST(rb_ca_is_empty(self)) ) {
582
- return rb_const_get(rb_cObject, rb_intern("UNDEF"));
628
+ ca_by_key_check_shape(self, rkey, "min_by_key");
629
+ if ( NIL_P(raxis) && RTEST(rb_ca_is_empty(self)) ) {
630
+ return CA_UNDEF;
583
631
  }
632
+ rkey = ca_order_key(rkey, "min_by_key");
584
633
 
585
634
  VALUE addrs;
586
635
 
@@ -593,7 +642,7 @@ rb_ca_min_by_key (int argc, VALUE *argv, VALUE self)
593
642
  rb_hash_aset(kw, ID2SYM(id_axis), raxis);
594
643
  addrs = rb_funcallv_kw(rkey, rb_intern("min_addr"), 1, &kw, RB_PASS_KEYWORDS);
595
644
  }
596
- return rb_ca_fetch(self, addrs);
645
+ return ca_by_key_fetch(self, addrs);
597
646
  }
598
647
 
599
648
  /* ---- take_along_axis / put_along_axis -------------------------------- */
@@ -637,10 +686,16 @@ rb_ca_put_along_axis (int argc, VALUE *argv, VALUE self)
637
686
  raxis = INT2FIX(0);
638
687
  }
639
688
  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). */
689
+ /* A masked index writes nothing. The values are first laid out over the
690
+ indices by the store itself (an array of the indices' shape, so a
691
+ broadcast, UNDEF or a Face value means what it means unmasked), and
692
+ only the cells at unmasked indices are written. */
642
693
  if ( RTEST(rb_ca_has_mask(addrs)) ) {
643
- rb_funcall(self, rb_intern("scatter_replace!"), 2, addrs, argv[1]);
694
+ VALUE sel = rb_funcall(addrs, rb_intern("is_not_masked"), 0);
695
+ VALUE vals = rb_funcall(self, rb_intern("project"), 1, addrs);
696
+ rb_funcall(vals, rb_intern("[]="), 1, argv[1]);
697
+ rb_ca_store(self, rb_funcall(addrs, rb_intern("[]"), 1, sel),
698
+ rb_funcall(vals, rb_intern("[]"), 1, sel));
644
699
  return self;
645
700
  }
646
701
  rb_ca_store(self, addrs, argv[1]);
@@ -653,7 +708,12 @@ static VALUE
653
708
  rb_ca_range_method (VALUE self)
654
709
  {
655
710
  VALUE mm_argv[1] = { Qnil };
656
- VALUE pair = rb_ca_minmax_ki(0, mm_argv, self);
711
+ CArray *ca;
712
+ VALUE pair;
713
+ TypedData_Get_Struct(self, CArray, &carray_data_type, ca);
714
+ /* A Face answers its own minmax (in its order, with its elements). */
715
+ pair = ca_is_face(ca) ? rb_funcall(self, rb_intern("minmax"), 0)
716
+ : rb_ca_minmax_ki(0, mm_argv, self);
657
717
  VALUE lo = rb_ary_entry(pair, 0);
658
718
  VALUE hi = rb_ary_entry(pair, 1);
659
719
  return rb_range_new(lo, hi, 0);
@@ -661,6 +721,221 @@ rb_ca_range_method (VALUE self)
661
721
 
662
722
  /* ---- nlargest / nsmallest family ------------------------------------- */
663
723
 
724
+ /* The partition picks the right values but, among cells that tie with the
725
+ last one picked, any of them. The answer is the cells first in the
726
+ order (value, then NaN, then masked; the values descending for the
727
+ largest) with ties broken by position, as `order` breaks them. So the
728
+ tied candidates are replaced by the earliest cells that tie, and the
729
+ candidates are sorted by (order, position): O(n) per fiber besides the
730
+ sort of the candidates. */
731
+
732
+ typedef struct {
733
+ const char *v; /* the fiber's values, contiguous */
734
+ const boolean8_t *m; /* its mask, or NULL */
735
+ int8_t type;
736
+ ca_size_t bytes;
737
+ int desc;
738
+ } ca_topk_fiber_t;
739
+
740
+ /* 0 a value, 1 NaN, 2 masked: the classes come in that order. */
741
+ static int
742
+ ca_topk_class (const ca_topk_fiber_t *f, ca_size_t i)
743
+ {
744
+ if ( f->m && f->m[i] ) return 2;
745
+ if ( f->type == CA_FLOAT64 && isnan(((const double *) f->v)[i]) ) return 1;
746
+ if ( f->type == CA_FLOAT32 && isnan(((const float *) f->v)[i]) ) return 1;
747
+ return 0;
748
+ }
749
+
750
+ #define CA_TOPK_CMP(T) \
751
+ { T a_ = ((const T *) f->v)[i], b_ = ((const T *) f->v)[j]; \
752
+ r = (a_ < b_) ? -1 : (a_ > b_) ? 1 : 0; } \
753
+ break
754
+
755
+ /* The order of cells i and j, ties not broken. */
756
+ static int
757
+ ca_topk_cmp (const ca_topk_fiber_t *f, ca_size_t i, ca_size_t j)
758
+ {
759
+ int ci = ca_topk_class(f, i), cj = ca_topk_class(f, j), r = 0;
760
+ if ( ci != cj ) return ( ci < cj ) ? -1 : 1;
761
+ if ( ci != 0 ) return 0;
762
+ switch ( f->type ) {
763
+ case CA_BOOLEAN:
764
+ case CA_UINT8: CA_TOPK_CMP(uint8_t);
765
+ case CA_INT8: CA_TOPK_CMP(int8_t);
766
+ case CA_INT16: CA_TOPK_CMP(int16_t);
767
+ case CA_UINT16: CA_TOPK_CMP(uint16_t);
768
+ case CA_INT32: CA_TOPK_CMP(int32_t);
769
+ case CA_UINT32: CA_TOPK_CMP(uint32_t);
770
+ case CA_INT64: CA_TOPK_CMP(int64_t);
771
+ case CA_UINT64: CA_TOPK_CMP(uint64_t);
772
+ case CA_FLOAT32: CA_TOPK_CMP(float);
773
+ case CA_FLOAT64: CA_TOPK_CMP(double);
774
+ case CA_FIXLEN:
775
+ r = memcmp(f->v + i * f->bytes, f->v + j * f->bytes, (size_t) f->bytes);
776
+ r = ( r > 0 ) - ( r < 0 );
777
+ break;
778
+ default:
779
+ r = 0;
780
+ }
781
+ return f->desc ? -r : r;
782
+ }
783
+
784
+ #undef CA_TOPK_CMP
785
+
786
+ /* Merge sort of positions by (order, position). */
787
+ static void
788
+ ca_topk_sort (const ca_topk_fiber_t *f, ca_size_t *p, ca_size_t *tmp, ca_size_t n)
789
+ {
790
+ ca_size_t h, i, j, k;
791
+ if ( n < 2 ) return;
792
+ h = n / 2;
793
+ ca_topk_sort(f, p, tmp, h);
794
+ ca_topk_sort(f, p + h, tmp, n - h);
795
+ i = 0; j = h; k = 0;
796
+ while ( i < h && j < n ) {
797
+ int r = ca_topk_cmp(f, p[i], p[j]);
798
+ if ( r < 0 || ( r == 0 && p[i] < p[j] ) ) tmp[k++] = p[i++];
799
+ else tmp[k++] = p[j++];
800
+ }
801
+ while ( i < h ) tmp[k++] = p[i++];
802
+ while ( j < n ) tmp[k++] = p[j++];
803
+ memcpy(p, tmp, (size_t) n * sizeof(ca_size_t));
804
+ }
805
+
806
+ #define CA_TOPK_SCAN_VALUE(T) \
807
+ { const T *v_ = (const T *) f->v; T w_ = v_[worst]; \
808
+ for (j = 0; j <= end && ties > 0; j++) { \
809
+ if ( v_[j] == w_ && ! ( f->m && f->m[j] ) ) { c[n++] = j; ties--; } \
810
+ } } \
811
+ break
812
+
813
+ /* The earliest cells, up to `end`, that tie with cell `worst`, appended to
814
+ c[n..] until `ties` are found. A value compares by ==, which leaves out
815
+ NaN and the masked cells; those classes tie within themselves. */
816
+ static ca_size_t
817
+ ca_topk_scan_ties (const ca_topk_fiber_t *f, ca_size_t worst, ca_size_t end,
818
+ ca_size_t *c, ca_size_t n, ca_size_t ties)
819
+ {
820
+ ca_size_t j;
821
+ int cls = ca_topk_class(f, worst);
822
+ if ( cls != 0 || f->type == CA_FIXLEN ) {
823
+ for (j = 0; j <= end && ties > 0; j++) {
824
+ if ( ca_topk_cmp(f, j, worst) == 0 ) { c[n++] = j; ties--; }
825
+ }
826
+ return n;
827
+ }
828
+ switch ( f->type ) {
829
+ case CA_BOOLEAN:
830
+ case CA_UINT8: CA_TOPK_SCAN_VALUE(uint8_t);
831
+ case CA_INT8: CA_TOPK_SCAN_VALUE(int8_t);
832
+ case CA_INT16: CA_TOPK_SCAN_VALUE(int16_t);
833
+ case CA_UINT16: CA_TOPK_SCAN_VALUE(uint16_t);
834
+ case CA_INT32: CA_TOPK_SCAN_VALUE(int32_t);
835
+ case CA_UINT32: CA_TOPK_SCAN_VALUE(uint32_t);
836
+ case CA_INT64: CA_TOPK_SCAN_VALUE(int64_t);
837
+ case CA_UINT64: CA_TOPK_SCAN_VALUE(uint64_t);
838
+ case CA_FLOAT32: CA_TOPK_SCAN_VALUE(float);
839
+ case CA_FLOAT64: CA_TOPK_SCAN_VALUE(double);
840
+ }
841
+ return n;
842
+ }
843
+
844
+ #undef CA_TOPK_SCAN_VALUE
845
+
846
+ static void
847
+ ca_topk_fix_fiber (const ca_topk_fiber_t *f, ca_size_t len,
848
+ ca_size_t *c, ca_size_t cap, ca_size_t *tmp)
849
+ {
850
+ ca_size_t worst = c[0], ties = 0, n = 0, end = 0, k;
851
+ for (k = 1; k < cap; k++) {
852
+ if ( ca_topk_cmp(f, c[k], worst) > 0 ) worst = c[k];
853
+ }
854
+ /* Keep the candidates before the worst; the ones tied with it are
855
+ replaced by the earliest cells that tie, which lie no later than the
856
+ last tied candidate. */
857
+ for (k = 0; k < cap; k++) {
858
+ if ( ca_topk_cmp(f, c[k], worst) < 0 ) {
859
+ c[n++] = c[k];
860
+ }
861
+ else {
862
+ ties++;
863
+ if ( c[k] > end ) end = c[k];
864
+ }
865
+ }
866
+ (void) len;
867
+ n = ca_topk_scan_ties(f, worst, end, c, n, ties);
868
+ ca_topk_sort(f, c, tmp, cap);
869
+ }
870
+
871
+ /* `picked` holds cap candidate positions per fiber along axis; returns the
872
+ stable answer of the same shape. The fibers are read with the axis
873
+ moved last, from a contiguous copy. */
874
+ static VALUE
875
+ ca_topk_stable (VALUE self, VALUE picked, long axis, int desc)
876
+ {
877
+ volatile VALUE key = ca_order_key(self, desc ? "nlargest" : "nsmallest");
878
+ volatile VALUE vals, pk, holder = 0;
879
+ VALUE perm[CA_RANK_MAX], inv[CA_RANK_MAX];
880
+ CArray *cv, *cp;
881
+ ca_topk_fiber_t f;
882
+ ca_size_t len, cap, outer, o, *tmp;
883
+ int ndim, k, last;
884
+
885
+ GetCArray(key, cv);
886
+ ndim = cv->ndim;
887
+ last = ( axis == ndim - 1 );
888
+ if ( ! last ) {
889
+ int n = 0;
890
+ for (k = 0; k < ndim; k++) if ( k != axis ) perm[n++] = INT2FIX(k);
891
+ perm[n] = LONG2NUM(axis);
892
+ for (k = 0; k < ndim; k++) inv[NUM2INT(perm[k])] = INT2FIX(k);
893
+ }
894
+ /* An entity whose fibers already lie along its last axis is read in
895
+ place; anything else is copied into that layout. */
896
+ if ( last && ca_is_entity(cv) ) {
897
+ vals = key;
898
+ ca_update_mask(cv);
899
+ }
900
+ else {
901
+ vals = rb_ca_copy(last ? key : rb_funcallv(key, rb_intern("transpose"), ndim, perm));
902
+ }
903
+ pk = rb_ca_copy(last ? picked : rb_funcallv(picked, rb_intern("transpose"), ndim, perm));
904
+ GetCArray(vals, cv);
905
+ GetCArray(pk, cp);
906
+
907
+ len = cv->dim[ndim - 1];
908
+ cap = cp->dim[ndim - 1];
909
+ if ( len == 0 || cap == 0 ) return picked;
910
+ outer = cv->elements / len;
911
+ f.type = cv->data_type;
912
+ f.bytes = cv->bytes;
913
+ f.desc = desc;
914
+ tmp = ALLOCV_N(ca_size_t, holder, cap);
915
+ for (o = 0; o < outer; o++) {
916
+ f.v = cv->ptr + o * len * cv->bytes;
917
+ f.m = cv->mask ? (boolean8_t *) cv->mask->ptr + o * len : NULL;
918
+ ca_topk_fix_fiber(&f, len, (ca_size_t *) cp->ptr + o * cap, cap, tmp);
919
+ }
920
+ ALLOCV_END(holder);
921
+ return last ? pk : rb_ca_copy(rb_funcallv(pk, rb_intern("transpose"), ndim, inv));
922
+ }
923
+
924
+ /* Whether self holds a cell the order puts after the numbers: a masked
925
+ cell, or a NaN. */
926
+ static int
927
+ ca_topk_has_cells_after_numbers (VALUE self, CArray *ca)
928
+ {
929
+ if ( ca_has_mask(ca) && ca_count_masked(ca) > 0 ) {
930
+ return 1;
931
+ }
932
+ if ( ca_is_float_type(ca) ) {
933
+ VALUE nan = rb_funcall(self, rb_intern("is_nan"), 0);
934
+ return RTEST(rb_funcall(nan, rb_intern("any"), 0));
935
+ }
936
+ return 0;
937
+ }
938
+
664
939
  /* Per-axis top-k positions (cap clamped, axis normalized).
665
940
  * Called by rb_ca_topk_index below. */
666
941
  static VALUE
@@ -671,12 +946,32 @@ rb_ca_topk_positions (VALUE self, long cap, long axis_norm, int desc)
671
946
  long dim_axis = (long) ca->dim[axis_norm];
672
947
  VALUE raxis_norm = LONG2NUM(axis_norm);
673
948
 
949
+ /* The largest: reversing the ascending order would put the NaN and the
950
+ masked cells first, so rank them descending (NaN, then the masked
951
+ cells, last) when there are any. An object array is ranked by
952
+ `order` both ways (its cells compare in Ruby). */
953
+ if ( ca->data_type == CA_OBJECT ||
954
+ ( desc && ca_topk_has_cells_after_numbers(self, ca) ) ) {
955
+ VALUE kw = rb_hash_new();
956
+ rb_hash_aset(kw, ID2SYM(rb_intern("axis")), raxis_norm);
957
+ rb_hash_aset(kw, ID2SYM(rb_intern("descending")), desc ? Qtrue : Qfalse);
958
+ VALUE rank = rb_funcallv_kw(self, rb_intern("order"), 1, &kw, RB_PASS_KEYWORDS);
959
+ VALUE full = rb_ca_sort_index_ki_quick(rank, raxis_norm);
960
+ if ( cap >= dim_axis ) {
961
+ return rb_ca_copy(full);
962
+ }
963
+ int ndim = (int) ca->ndim;
964
+ VALUE spec[CA_RANK_MAX];
965
+ for ( int k = 0; k < ndim; k++ ) {
966
+ spec[k] = Qnil;
967
+ }
968
+ spec[axis_norm] = rb_range_new(INT2FIX(0), LONG2NUM(cap), 1);
969
+ return rb_ca_copy(rb_ca_fetch2(full, ndim, spec));
970
+ }
971
+
674
972
  if ( cap >= dim_axis ) {
675
973
  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);
974
+ return ca_topk_stable(self, full, axis_norm, desc);
680
975
  }
681
976
 
682
977
  long pivot = desc ? (dim_axis - cap) : (cap - 1);
@@ -703,7 +998,7 @@ rb_ca_topk_positions (VALUE self, long cap, long axis_norm, int desc)
703
998
  order = rb_ca_flip_axis(order, NUM2LONG(raxis_norm));
704
999
  }
705
1000
  VALUE picked = rb_ca_take_along_axis_c(partidx_top, order, raxis_norm);
706
- return rb_ca_copy(picked);
1001
+ return ca_topk_stable(self, picked, axis_norm, desc);
707
1002
  }
708
1003
 
709
1004
  /* Dispatcher: normalize axis, clamp cap, empty-cap corner.
@@ -807,11 +1102,10 @@ rb_ca_nsmallest (int argc, VALUE *argv, VALUE self)
807
1102
  * ever compare equal), so a later sort_addr key never gets consulted;
808
1103
  * dense ranks preserve ties, so it does.
809
1104
  *
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. */
1105
+ * descending: ranks the largest value 0, with NaN after the numbers as
1106
+ * ascending. The kernel gives the descending dense rank; the ordinal rank
1107
+ * is the ascending ordinal rank of it, so tied cells keep their original
1108
+ * order in both directions. */
815
1109
 
816
1110
  static VALUE
817
1111
  rb_ca_order (int argc, VALUE *argv, VALUE self)
@@ -826,11 +1120,24 @@ rb_ca_order (int argc, VALUE *argv, VALUE self)
826
1120
  int do_stable = NIL_P(rkind) ? 0
827
1121
  : ca_symbol_choice(rkind, "kind", "quick", "stable", NULL);
828
1122
 
829
- VALUE asc, n;
1123
+ if ( RTEST(rdesc) && ! dense ) {
1124
+ VALUE kw = rb_hash_new();
1125
+ rb_hash_aset(kw, ID2SYM(rb_intern("axis")), raxis);
1126
+ rb_hash_aset(kw, ID2SYM(rb_intern("descending")), Qtrue);
1127
+ rb_hash_aset(kw, ID2SYM(rb_intern("method")), ID2SYM(rb_intern("dense")));
1128
+ rb_hash_aset(kw, ID2SYM(rb_intern("kind")), do_stable ? ID2SYM(rb_intern("stable")) : ID2SYM(rb_intern("quick")));
1129
+ VALUE ranked = rb_funcallv_kw(self, rb_intern("order"), 1, &kw, RB_PASS_KEYWORDS);
1130
+ rb_hash_delete(kw, ID2SYM(rb_intern("descending")));
1131
+ rb_hash_delete(kw, ID2SYM(rb_intern("method")));
1132
+ return rb_funcallv_kw(ranked, rb_intern("order"), 1, &kw, RB_PASS_KEYWORDS);
1133
+ }
1134
+
1135
+ /* The kernel's dense == 2 is the descending dense rank. */
1136
+ int kmode = ( dense && RTEST(rdesc) ) ? 2 : dense;
830
1137
  if ( NIL_P(raxis) ) {
831
1138
  VALUE flat = rb_ca_flatten(self);
832
1139
  /* 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);
1140
+ VALUE ranked = rb_ca_rank_index_ki_kind_dense(flat, INT2FIX(0), do_stable, kmode);
834
1141
  /* reshape(*shape) */
835
1142
  CArray *ca;
836
1143
  TypedData_Get_Struct(self, CArray, &carray_data_type, ca);
@@ -838,43 +1145,12 @@ rb_ca_order (int argc, VALUE *argv, VALUE self)
838
1145
  for ( long k = 0; k < (long) ca->ndim; k++ ) {
839
1146
  shape_argv[k] = LONG2NUM((long) ca->dim[k]);
840
1147
  }
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
- }
1148
+ return rb_ca_reshape((int) ca->ndim, shape_argv, ranked);
860
1149
  }
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;
871
- }
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;
1150
+ long axis_norm = rb_ca_normalize_axis_value(self, raxis, "order");
1151
+ /* Output data_type: CA_SIZE (int64 on 64-bit builds), as the rest of the
1152
+ *_index family. */
1153
+ return rb_ca_rank_index_ki_kind_dense(self, LONG2NUM(axis_norm), do_stable, kmode);
878
1154
  }
879
1155
 
880
1156
  /* ---- linear_section / linear_fetch ----------------------------------- */
@@ -896,9 +1172,7 @@ ca_linear_prep (VALUE self, VALUE raxis, const char *name, long *axis_out)
896
1172
  Face) is caught by the rb_ca_to_float64 cast below, so no separate
897
1173
  float-numeric flag is needed. */
898
1174
  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
- }
1175
+ ca_face_require_orderable(self, name, "order");
902
1176
  self = rb_ca_strip_face_value(self);
903
1177
  TypedData_Get_Struct(self, CArray, &carray_data_type, ca);
904
1178
  }
@@ -925,13 +1199,26 @@ rb_ca_linear_section_m (int argc, VALUE *argv, VALUE self)
925
1199
  rb_check_arity(argc, 1, 1);
926
1200
  VALUE val = argv[0];
927
1201
 
928
- /* Query gate: the reference axis, still a Face here, reconciles the query
929
- into its own space (ca_face_reconcile_operand). */
1202
+ /* Query gate: the reference axis, still a Face here, and the query are
1203
+ brought into one space (ca_face_reconcile_pair). */
930
1204
  if ( rb_obj_is_carray(self) ) {
931
1205
  CArray *sca;
932
1206
  TypedData_Get_Struct(self, CArray, &carray_data_type, sca);
933
1207
  if ( ca_is_face(sca) ) {
934
- val = ca_face_reconcile_operand(self, val, "linear_section");
1208
+ volatile VALUE ref = self, query = val;
1209
+ ca_face_reconcile_pair(&ref, &query, "linear_section");
1210
+ self = ref;
1211
+ val = query;
1212
+ }
1213
+ }
1214
+
1215
+ {
1216
+ CArray *ca;
1217
+ TypedData_Get_Struct(self, CArray, &carray_data_type, ca);
1218
+ if ( ca_is_complex_type(ca) ) {
1219
+ rb_raise(rb_eCADataTypeError,
1220
+ "linear_section: a %s array has no order (complex numbers are "
1221
+ "not ordered)", ca_type_name[ca->data_type]);
935
1222
  }
936
1223
  }
937
1224
 
@@ -960,6 +1247,27 @@ rb_ca_linear_fetch_m (int argc, VALUE *argv, VALUE self)
960
1247
  rb_check_arity(argc, 1, 1);
961
1248
  VALUE addr = argv[0];
962
1249
 
1250
+ /* A complex array interpolates its real and imaginary parts: the
1251
+ weights are real, so each part is a real interpolation. */
1252
+ {
1253
+ CArray *ca;
1254
+ TypedData_Get_Struct(self, CArray, &carray_data_type, ca);
1255
+ if ( ca_is_complex_type(ca) ) {
1256
+ VALUE args[2], re, im;
1257
+ int n = 1;
1258
+ args[0] = addr;
1259
+ if ( ! NIL_P(ropt) ) args[n++] = ropt;
1260
+ re = rb_funcallv_kw(rb_funcall(self, rb_intern("real"), 0),
1261
+ rb_intern("linear_fetch"), n, args,
1262
+ NIL_P(ropt) ? RB_NO_KEYWORDS : RB_PASS_KEYWORDS);
1263
+ im = rb_funcallv_kw(rb_funcall(self, rb_intern("imag"), 0),
1264
+ rb_intern("linear_fetch"), n, args,
1265
+ NIL_P(ropt) ? RB_NO_KEYWORDS : RB_PASS_KEYWORDS);
1266
+ return rb_funcall(re, '+', 1,
1267
+ rb_funcall(im, '*', 1, rb_Complex(INT2FIX(0), INT2FIX(1))));
1268
+ }
1269
+ }
1270
+
963
1271
  long axis_norm = 0;
964
1272
  VALUE sc = ca_linear_prep(self, raxis, "linear_fetch", &axis_norm);
965
1273
  return rb_ca_linear_fetch_ki(sc, addr, LONG2NUM(axis_norm));