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
@@ -197,8 +197,7 @@ pct_compute (VALUE method, long k, double r, long n, double lo, double hi)
197
197
  return use_k ? lo : hi;
198
198
  }
199
199
  if ( method == sym_linear ) {
200
- if ( r == 0.0 || k + 1 >= n ) return lo;
201
- return lo * (1.0 - r) + hi * r;
200
+ return ca_percentile_linear(lo, hi, r, k, n);
202
201
  }
203
202
  if ( method == sym_midpoint ) {
204
203
  if ( k + 1 >= n ) return lo;
@@ -397,11 +396,16 @@ mp_first_non_numeric (VALUE v)
397
396
  the elements' own arithmetic and an exact weight, so a Rational or a
398
397
  BigDecimal stays exact and a Float stays a Float. */
399
398
 
400
- /* The interpolation weight r (0 <= r < 1) as a Rational. */
399
+ /* [object] the weight of the upper element at p, exact: (n-1)*p/100 - k as
400
+ a Rational, with p read as the decimal it was written as. The double r
401
+ carries the rounding of (n-1)*p/100 (p = 30 over five elements is
402
+ 0.19999999999999996 there, not 1/5), so it is not used for the weight. */
401
403
  static VALUE
402
- obj_weight (double r)
404
+ obj_weight (long n, double p, long k)
403
405
  {
404
- return rb_funcall(DBL2NUM(r), id_rationalize, 0);
406
+ VALUE pr = rb_funcall(DBL2NUM(p), id_rationalize, 0);
407
+ VALUE f = rb_funcall(rb_funcall(pr, id_mul, 1, LONG2NUM(n - 1)), rb_intern("quo"), 1, INT2FIX(100));
408
+ return rb_funcall(f, rb_intern("-"), 1, LONG2NUM(k));
405
409
  }
406
410
 
407
411
  /* The midpoint of lo and hi, (lo + hi) * (1/2): exact for an Integer or a
@@ -488,7 +492,8 @@ obj_kth_pair (VALUE self, long axis, long k, long n, VALUE *lo, VALUE *hi)
488
492
  }
489
493
 
490
494
  /* [object] keep_axis flat: [1,...,1] entity holding val (UNDEF assigns
491
- masked). Uses []= so UNDEF / arbitrary objects store correctly. */
495
+ masked), in self's Face when self is one. Uses []= so UNDEF / arbitrary
496
+ objects store correctly. */
492
497
  static VALUE
493
498
  obj_keep_axis_full (VALUE self, VALUE val)
494
499
  {
@@ -503,7 +508,7 @@ obj_keep_axis_full (VALUE self, VALUE val)
503
508
  for ( int i = 0; i < ndim; i++ ) av[i] = INT2FIX(0);
504
509
  av[ndim] = val;
505
510
  rb_funcallv(out, id_aset, ndim + 1, av);
506
- return out;
511
+ return ca_is_face(ca) ? ca_face_relift_entity(self, out) : out;
507
512
  }
508
513
 
509
514
  /* [object] per-axis median (= reduced CArray; 1-D self -> bare scalar).
@@ -531,13 +536,41 @@ median_object_axis (VALUE self, long axis, long n, int keep_axis)
531
536
  VALUE kv = obj_kth_one(self, axis, (n - 1) / 2);
532
537
  result = kv;
533
538
  }
534
- if ( keep_axis ) {
539
+ /* a 1-D self reduces to a bare object; mp_axis_answer keeps it */
540
+ if ( keep_axis && rb_obj_is_kind_of(result, rb_cCArray) ) {
535
541
  VALUE ia[1] = { LONG2NUM(axis) };
536
542
  result = rb_ca_insert_axis(1, ia, result);
537
543
  }
538
544
  return result;
539
545
  }
540
546
 
547
+ /* [entry] An axis form's answer in the shape the other reductions give it.
548
+ A reduction that removes the only axis of a 1-D array is a bare value, as
549
+ sum(axis: 0) is, and keep_axis: makes it a one-cell array; the lanes answer
550
+ either way, so the answer is shaped here. An answer the object lane took
551
+ as a slice of its partitioned copy is made an array of its own, as the
552
+ other reductions' answers are. */
553
+ static VALUE
554
+ mp_axis_answer (VALUE self, VALUE res, int keep_axis)
555
+ {
556
+ CArray *ca;
557
+ GetCArray(self, ca);
558
+ int is_array = RTEST(rb_obj_is_kind_of(res, rb_cCArray));
559
+ if ( ca->ndim != 1 ) {
560
+ if ( is_array ) {
561
+ CArray *cr;
562
+ GetCArray(res, cr);
563
+ if ( ! ca_is_entity(cr) && ! ca_is_face(cr) ) res = rb_funcall(res, id_copy, 0);
564
+ }
565
+ return res;
566
+ }
567
+ if ( keep_axis ) {
568
+ if ( is_array ) return res;
569
+ return obj_keep_axis_full(self, res);
570
+ }
571
+ return is_array ? rb_funcall(res, id_aref, 1, INT2FIX(0)) : res;
572
+ }
573
+
541
574
  /* [object] flat median: mask-strip / min_count, flatten, axis-0 reduce. */
542
575
  static VALUE
543
576
  median_object_flat (VALUE self, long min_count, VALUE fill_value, int keep_axis)
@@ -573,7 +606,7 @@ median_object_flat (VALUE self, long min_count, VALUE fill_value, int keep_axis)
573
606
  The :linear / :midpoint interpolation funcalls are irreducible for
574
607
  arbitrary objects; the pick methods return the element itself. */
575
608
  static VALUE
576
- pct_compute_object (VALUE method, long k, double r, long n, VALUE lo, VALUE hi)
609
+ pct_compute_object (VALUE method, double p, long k, double r, long n, VALUE lo, VALUE hi)
577
610
  {
578
611
  if ( method == sym_lower ) return lo;
579
612
  if ( method == sym_higher ) return (r == 0.0) ? lo : hi;
@@ -584,7 +617,7 @@ pct_compute_object (VALUE method, long k, double r, long n, VALUE lo, VALUE hi)
584
617
  if ( method == sym_linear ) {
585
618
  if ( r == 0.0 || k + 1 >= n ) return lo;
586
619
  mp_require_interpolable("percentile", method, lo);
587
- VALUE w = obj_weight(r);
620
+ VALUE w = obj_weight(n, p, k);
588
621
  VALUE a = rb_funcall(lo, id_mul, 1, rb_funcall(INT2FIX(1), rb_intern("-"), 1, w));
589
622
  VALUE b = rb_funcall(hi, id_mul, 1, w);
590
623
  return rb_funcall(a, id_plus, 1, b);
@@ -610,7 +643,7 @@ pct_object_one_partition (VALUE self, long axis, long n, double p, VALUE method)
610
643
  VALUE lo = Qnil, hi = Qnil;
611
644
  if ( need == PCT_LOWER_ONLY ) lo = obj_kth_one(self, axis, k);
612
645
  else obj_kth_pair(self, axis, k, n, &lo, &hi);
613
- return pct_compute_object(method, k, r, n, lo, hi);
646
+ return pct_compute_object(method, p, k, r, n, lo, hi);
614
647
  }
615
648
 
616
649
  /* [object] single p from a fully sorted entity (multi-p shared sort). */
@@ -629,7 +662,7 @@ pct_object_one_sorted (VALUE sorted, long axis, long n, double p, VALUE method)
629
662
  VALUE lo = Qnil, hi = Qnil;
630
663
  if ( need != PCT_UPPER_ONLY ) lo = obj_slice(sorted, axis, k);
631
664
  if ( need != PCT_LOWER_ONLY ) hi = obj_slice(sorted, axis, kup);
632
- return pct_compute_object(method, k, r, n, lo, hi);
665
+ return pct_compute_object(method, p, k, r, n, lo, hi);
633
666
  }
634
667
 
635
668
  /* [object] per-axis percentile -> array of (reduced CArray | scalar),
@@ -681,7 +714,9 @@ pct_object_axis_masked_one (VALUE self, VALUE sorted, long axis, double p,
681
714
  VALUE fill_value, int keep_axis)
682
715
  {
683
716
  int8_t ax = (int8_t) axis;
684
- VALUE out = rb_ca_new_reduced(self, &ax, 1, CA_OBJECT, keep_axis);
717
+ /* `sorted` is an entity of self's shape (the storage, when self is a
718
+ Face); the answer is built on it and lifted back by the caller. */
719
+ VALUE out = rb_ca_new_reduced(sorted, &ax, 1, CA_OBJECT, keep_axis);
685
720
  CArray *co;
686
721
  GetCArray(out, co);
687
722
  CArray *cs;
@@ -718,7 +753,7 @@ pct_object_axis_masked_one (VALUE self, VALUE sorted, long axis, double p,
718
753
  VALUE lo = Qnil, hi = Qnil;
719
754
  if ( need != PCT_UPPER_ONLY ) lo = sb[f0 + (ca_size_t)(k * INNER)];
720
755
  if ( need != PCT_LOWER_ONLY ) hi = sb[f0 + (ca_size_t)(kup * INNER)];
721
- op[oc] = pct_compute_object(method, k, r, np, lo, hi);
756
+ op[oc] = pct_compute_object(method, p, k, r, np, lo, hi);
722
757
  }
723
758
  }
724
759
  return out;
@@ -743,7 +778,11 @@ percentile_object_axis_masked (VALUE self, VALUE pers, long axis, VALUE method,
743
778
  return res;
744
779
  }
745
780
 
781
+ /* A Face (a string array) is sorted as itself and read through its
782
+ storage, whose cells are the values. */
746
783
  VALUE sorted = rb_funcall(obj_call_axis(self, id_sort, LONG2NUM(axis)), id_copy, 0);
784
+ int is_face = ca_is_face(ca);
785
+ if ( is_face ) sorted = rb_ca_strip_face_value(sorted);
747
786
  CArray *cs;
748
787
  GetCArray(sorted, cs);
749
788
  const boolean8_t *sm = cs->mask ? (const boolean8_t *) cs->mask->ptr : NULL;
@@ -754,10 +793,10 @@ percentile_object_axis_masked (VALUE self, VALUE pers, long axis, VALUE method,
754
793
  VALUE result = rb_ary_new_capa(npers);
755
794
  for ( long i = 0; i < npers; i++ ) {
756
795
  double p = NUM2DBL(rb_ary_entry(pers, i));
757
- rb_ary_push(result,
758
- pct_object_axis_masked_one(self, sorted, axis, p, method,
759
- OUTER, MM, INNER, sm, min_count,
760
- fill_value, keep_axis));
796
+ VALUE one = pct_object_axis_masked_one(self, sorted, axis, p, method,
797
+ OUTER, MM, INNER, sm, min_count,
798
+ fill_value, keep_axis);
799
+ rb_ary_push(result, is_face ? ca_face_relift_entity(self, one) : one);
761
800
  }
762
801
  RB_GC_GUARD(sorted);
763
802
  return result;
@@ -867,16 +906,23 @@ rb_ca_median_m_body (int argc, VALUE *argv, VALUE self)
867
906
  long n = (long) ca->dim[axis];
868
907
  if ( n == 0 ) return mp_axis_all_masked(self, axis, keep_axis);
869
908
  /* mask / min_count / fill_value require the per-fiber select (each fiber
870
- has its own n_present); the plain partition path assumes a uniform n. */
871
- if ( RTEST(rb_ca_has_mask(self)) || min_count > 0 || !NIL_P(fill_value) )
872
- return is_obj
909
+ has its own n_present); the plain partition path assumes a uniform n.
910
+ Switch ca_fast_paths_enabled(): off, the per-fiber select. */
911
+ VALUE res;
912
+ if ( RTEST(rb_ca_has_mask(self)) || min_count > 0 || !NIL_P(fill_value)
913
+ || ! ca_fast_paths_enabled() )
914
+ res = is_obj
873
915
  ? median_object_axis_masked(self, axis, min_count, fill_value, keep_axis)
874
916
  : median_axis_masked(self, axis, min_count, fill_value, keep_axis);
875
- return is_obj ? median_object_axis(self, axis, n, keep_axis)
876
- : median_axis(self, axis, n, keep_axis);
917
+ else
918
+ res = is_obj ? median_object_axis(self, axis, n, keep_axis)
919
+ : median_axis(self, axis, n, keep_axis);
920
+ return mp_axis_answer(self, res, keep_axis);
877
921
  }
878
- return is_obj ? median_object_flat(self, min_count, fill_value, keep_axis)
879
- : median_flat(self, min_count, fill_value, keep_axis);
922
+ /* self may be the float64 view made above, read through its CArray */
923
+ CA_RETURN_KEEPING(self,
924
+ is_obj ? median_object_flat(self, min_count, fill_value, keep_axis)
925
+ : median_flat(self, min_count, fill_value, keep_axis));
880
926
  }
881
927
 
882
928
  /* =====================================================================
@@ -1317,9 +1363,10 @@ rb_ca_percentile_kw (int argc, VALUE *argv, VALUE ropt, VALUE self)
1317
1363
  long axis = rb_ca_normalize_axis_value(self, raxis, NULL);
1318
1364
  /* mask / min_count / fill_value require the per-fiber select (each fiber
1319
1365
  has its own n_present); the plain partition/sort path assumes a
1320
- uniform n. These paths bake keep_axis into the reduced output. */
1366
+ uniform n. These paths bake keep_axis into the reduced output.
1367
+ Switch ca_fast_paths_enabled(): off, the per-fiber select. */
1321
1368
  int per_fiber = RTEST(rb_ca_has_mask(self)) || min_count > 0
1322
- || !NIL_P(fill_value);
1369
+ || !NIL_P(fill_value) || ! ca_fast_paths_enabled();
1323
1370
  if ( !is_obj ) {
1324
1371
  result = per_fiber
1325
1372
  ? pct_axis_masked(self, pers, axis, method, min_count, fill_value, keep_axis)
@@ -1329,7 +1376,7 @@ rb_ca_percentile_kw (int argc, VALUE *argv, VALUE ropt, VALUE self)
1329
1376
  min_count, fill_value, keep_axis);
1330
1377
  } else {
1331
1378
  result = percentile_object_axis(self, pers, axis, method);
1332
- if ( keep_axis ) {
1379
+ if ( keep_axis && ca->ndim > 1 ) {
1333
1380
  long len = RARRAY_LEN(result);
1334
1381
  VALUE w = rb_ary_new_capa(len);
1335
1382
  VALUE ia[1] = { LONG2NUM(axis) };
@@ -1338,6 +1385,11 @@ rb_ca_percentile_kw (int argc, VALUE *argv, VALUE ropt, VALUE self)
1338
1385
  result = w;
1339
1386
  }
1340
1387
  }
1388
+ {
1389
+ long len = RARRAY_LEN(result);
1390
+ for ( long i = 0; i < len; i++ )
1391
+ rb_ary_store(result, i, mp_axis_answer(self, rb_ary_entry(result, i), keep_axis));
1392
+ }
1341
1393
  } else if ( !is_obj ) {
1342
1394
  result = pct_flat(self, pers, min_count, fill_value, method, keep_axis);
1343
1395
  } else {
@@ -1350,6 +1402,8 @@ rb_ca_percentile_kw (int argc, VALUE *argv, VALUE ropt, VALUE self)
1350
1402
  result = w;
1351
1403
  }
1352
1404
  }
1405
+ /* self may be the float64 view made above, read through its CArray */
1406
+ RB_GC_GUARD(self);
1353
1407
 
1354
1408
  if ( single_p ) return rb_ary_entry(result, 0);
1355
1409
  return result;