carray 3.0.1 → 3.0.2

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 (104) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +520 -0
  3. data/README.md +2 -2
  4. data/carray.gemspec +1 -1
  5. data/ext/ca_axis_dispatch.c +33 -4
  6. data/ext/ca_axis_group.c +202 -96
  7. data/ext/ca_categorical_iterator.c +108 -54
  8. data/ext/ca_kernel_iterator.c +317 -51
  9. data/ext/ca_kernel_iterator.h +142 -35
  10. data/ext/ca_obj_array.c +62 -20
  11. data/ext/ca_obj_block.c +4 -4
  12. data/ext/ca_obj_const_string.c +85 -26
  13. data/ext/ca_obj_face.c +24 -0
  14. data/ext/ca_obj_face.h +15 -0
  15. data/ext/ca_obj_fixlen_string.c +18 -5
  16. data/ext/ca_obj_meld.c +123 -25
  17. data/ext/ca_obj_object.c +8 -0
  18. data/ext/ca_obj_select.c +49 -34
  19. data/ext/ca_obj_stack.c +3 -8
  20. data/ext/ca_obj_stride.c +72 -1
  21. data/ext/ca_obj_string.c +8 -4
  22. data/ext/ca_obj_window.c +8 -2
  23. data/ext/ca_op_ipower.c +1 -2
  24. data/ext/ca_rng_normal.h +42 -0
  25. data/ext/ca_rng_xoshiro256pp.h +105 -0
  26. data/ext/ca_sweep_engine.c +307 -143
  27. data/ext/ca_sweep_engine.h +26 -5
  28. data/ext/carray.h +21 -2
  29. data/ext/carray_access.c +32 -20
  30. data/ext/carray_address_basis.c +590 -0
  31. data/ext/carray_broadcast.c +3 -3
  32. data/ext/carray_call_cfunc.c +667 -483
  33. data/ext/carray_cast.c +115 -41
  34. data/ext/carray_copy.c +55 -30
  35. data/ext/carray_core.c +83 -3
  36. data/ext/carray_count.c +9 -10
  37. data/ext/carray_factorize.c +46 -25
  38. data/ext/carray_internal.h +17 -0
  39. data/ext/carray_kernels_reduce_aggregate.c +168 -0
  40. data/ext/carray_kernels_reduce_cumulative.c +270 -1
  41. data/ext/carray_kernels_reduce_extreme.c +554 -8
  42. data/ext/carray_kernels_scan.c +4 -4
  43. data/ext/carray_kernels_search.c +94 -14
  44. data/ext/carray_loop.c +7 -1
  45. data/ext/carray_mask.c +23 -8
  46. data/ext/carray_median_percentile.c +55 -0
  47. data/ext/carray_operator.c +4 -4
  48. data/ext/carray_order.c +1 -1
  49. data/ext/carray_random.c +384 -40
  50. data/ext/carray_slab.c +13 -0
  51. data/ext/carray_sort.c +20 -22
  52. data/ext/mk_call_cfunc.rb +103 -116
  53. data/ext/mkkernel.rb +297 -29
  54. data/ext/ruby_carray.c +10 -1
  55. data/ext/version.h +4 -4
  56. data/lib/carray/autoload_carray.rb +5 -3
  57. data/lib/carray/autoload_method_extension.rb +12 -0
  58. data/lib/carray/axis_group.rb +77 -0
  59. data/lib/carray/basics.rb +4 -0
  60. data/lib/carray/block_iterator.rb +92 -16
  61. data/lib/carray/categorical.rb +150 -33
  62. data/lib/carray/categorical_iterator.rb +207 -80
  63. data/lib/carray/const_string.rb +131 -27
  64. data/lib/carray/construct.rb +40 -0
  65. data/lib/carray/data_type_extension.rb +3 -0
  66. data/lib/carray/data_type_limits.rb +91 -0
  67. data/lib/carray/fixlen_string.rb +1 -1
  68. data/lib/carray/frame/csv_parser.rb +11 -4
  69. data/lib/carray/frame/frame.rb +81 -10
  70. data/lib/carray/frame/group.rb +36 -3
  71. data/lib/carray/frame/io.rb +67 -15
  72. data/lib/carray/frame/records.rb +18 -4
  73. data/lib/carray/frame/verbs.rb +14 -11
  74. data/lib/carray/inspect.rb +42 -9
  75. data/lib/carray/iterator.rb +143 -0
  76. data/lib/carray/lazy.rb +0 -37
  77. data/lib/carray/mask_gap_fill.rb +3 -1
  78. data/lib/carray/methods/discovery_along.rb +74 -0
  79. data/lib/carray/methods/factorize.rb +50 -0
  80. data/lib/carray/methods/is_in.rb +13 -2
  81. data/lib/carray/methods/locate_addr.rb +75 -2
  82. data/lib/carray/methods/mask_duplicates.rb +35 -1
  83. data/lib/carray/methods/nunique.rb +22 -1
  84. data/lib/carray/methods/repeat.rb +110 -0
  85. data/lib/carray/methods/unique.rb +41 -1
  86. data/lib/carray/rng.rb +86 -0
  87. data/lib/carray/slab_iterator.rb +58 -13
  88. data/lib/carray/string_operation_extension.rb +5 -1
  89. data/lib/carray/time.rb +18 -2
  90. data/lib/carray/window_iterator.rb +142 -20
  91. data/lib/carray.rb +2 -0
  92. data/yard-stubs/ca_obj_block.rb +2 -7
  93. data/yard-stubs/ca_obj_window.rb +10 -2
  94. data/yard-stubs/carray_access.rb +1 -1
  95. data/yard-stubs/carray_broadcast.rb +1 -1
  96. data/yard-stubs/carray_core.rb +0 -80
  97. data/yard-stubs/carray_count.rb +7 -2
  98. data/yard-stubs/carray_lazy.rb +205 -0
  99. data/yard-stubs/carray_math.rb +1486 -3
  100. data/yard-stubs/carray_median_percentile.rb +16 -2
  101. data/yard-stubs/carray_order.rb +9 -69
  102. data/yard-stubs/carray_slab.rb +9 -7
  103. data/yard-stubs/carray_sort.rb +7 -5
  104. metadata +9 -1
@@ -1810,7 +1810,7 @@ cummax_ki_native_f32 (VALUE self, CArray *ca, int axis)
1810
1810
  ? op_mask + (ca_size_t)(po - co_root) / (ca_size_t) co->bytes
1811
1811
  : NULL;
1812
1812
  CA_SLAB_SCAN_T_GATED(float, float, st_in, pi, mi,
1813
- st_out, po, mo_fiber, -INFINITY, if (v > acc) acc = v; r = acc);
1813
+ st_out, po, mo_fiber, NAN, acc = fmax(acc, v); r = acc);
1814
1814
  ca_iter_state_sync_slab(&st_out);
1815
1815
  }
1816
1816
  ca_iter_state_finish(&st_in);
@@ -1862,7 +1862,7 @@ cummax_ki_native_f64 (VALUE self, CArray *ca, int axis)
1862
1862
  ? op_mask + (ca_size_t)(po - co_root) / (ca_size_t) co->bytes
1863
1863
  : NULL;
1864
1864
  CA_SLAB_SCAN_T_GATED(double, double, st_in, pi, mi,
1865
- st_out, po, mo_fiber, -INFINITY, if (v > acc) acc = v; r = acc);
1865
+ st_out, po, mo_fiber, NAN, acc = fmax(acc, v); r = acc);
1866
1866
  ca_iter_state_sync_slab(&st_out);
1867
1867
  }
1868
1868
  ca_iter_state_finish(&st_in);
@@ -2498,7 +2498,7 @@ cummin_ki_native_f32 (VALUE self, CArray *ca, int axis)
2498
2498
  ? op_mask + (ca_size_t)(po - co_root) / (ca_size_t) co->bytes
2499
2499
  : NULL;
2500
2500
  CA_SLAB_SCAN_T_GATED(float, float, st_in, pi, mi,
2501
- st_out, po, mo_fiber, INFINITY, if (v < acc) acc = v; r = acc);
2501
+ st_out, po, mo_fiber, NAN, acc = fmin(acc, v); r = acc);
2502
2502
  ca_iter_state_sync_slab(&st_out);
2503
2503
  }
2504
2504
  ca_iter_state_finish(&st_in);
@@ -2550,7 +2550,7 @@ cummin_ki_native_f64 (VALUE self, CArray *ca, int axis)
2550
2550
  ? op_mask + (ca_size_t)(po - co_root) / (ca_size_t) co->bytes
2551
2551
  : NULL;
2552
2552
  CA_SLAB_SCAN_T_GATED(double, double, st_in, pi, mi,
2553
- st_out, po, mo_fiber, INFINITY, if (v < acc) acc = v; r = acc);
2553
+ st_out, po, mo_fiber, NAN, acc = fmin(acc, v); r = acc);
2554
2554
  ca_iter_state_sync_slab(&st_out);
2555
2555
  }
2556
2556
  ca_iter_state_finish(&st_in);
@@ -4247,7 +4247,7 @@ rb_ca_find_value_index_ki (VALUE self, VALUE rval, VALUE raxis)
4247
4247
  }
4248
4248
  if ( self_face_comparable ) {
4249
4249
  if ( rval_is_face ) {
4250
- rval = rb_ca_strip_face_value(rval);
4250
+ rval = ca_face_operand_descend(rval, "find_value_index_ki");
4251
4251
  }
4252
4252
  } else if ( self_was_face ) {
4253
4253
  if ( rb_respond_to(self_ref, rb_intern("to_comparable")) ) {
@@ -8724,7 +8724,7 @@ rb_ca_bsearch_ki (VALUE self, VALUE rval, VALUE raxis)
8724
8724
  }
8725
8725
  if ( self_face_comparable ) {
8726
8726
  if ( rval_is_face ) {
8727
- rval = rb_ca_strip_face_value(rval);
8727
+ rval = ca_face_operand_descend(rval, "bsearch_ki");
8728
8728
  }
8729
8729
  } else if ( self_was_face ) {
8730
8730
  if ( rb_respond_to(self_ref, rb_intern("to_comparable")) ) {
@@ -13046,7 +13046,7 @@ rb_ca_search_ki (int argc, VALUE *argv, VALUE self)
13046
13046
  }
13047
13047
  if ( self_face_comparable ) {
13048
13048
  if ( rval_is_face ) {
13049
- rval = rb_ca_strip_face_value(rval);
13049
+ rval = ca_face_operand_descend(rval, "search_ki");
13050
13050
  }
13051
13051
  } else if ( self_was_face ) {
13052
13052
  if ( rb_respond_to(self_ref, rb_intern("to_comparable")) ) {
@@ -16724,12 +16724,32 @@ search_nearest_ki_native_object (VALUE self, CArray *ca, VALUE rval, int axis)
16724
16724
  {
16725
16725
  /* CA_OBJECT nearest: minimum of query_val.distance(cell), compared
16726
16726
  with `<` (matches the legacy flat proc_nearest_addr_VALUE). */
16727
+ /* Nearest needs a metric. #distance is the protocol the 2.0 flat
16728
+ kernel used, back when Numeric#distance was a monkey patch; it
16729
+ is an opt-in refinement now, and a refinement does not reach an
16730
+ rb_funcall from C -- so a number reaching here answers no more
16731
+ than a String does. Measure a number the way #distance itself
16732
+ does, keep #distance for anything that defines a real one, and
16733
+ refuse the rest by name rather than let a bare NoMethodError out
16734
+ of the loop below. */
16735
+ ID nearest_id = rb_intern("distance");
16736
+ int nearest_by_distance = rb_respond_to(query_val, nearest_id);
16737
+ if ( ! nearest_by_distance && ! rb_obj_is_kind_of(query_val, rb_cNumeric) ) {
16738
+ rb_raise(rb_eCADataTypeError,
16739
+ "search_nearest: nearest needs a distance, and %s is neither a "
16740
+ "number nor answers #distance (define one on the stored "
16741
+ "objects, or use search / bsearch for an exact match)",
16742
+ rb_obj_classname(query_val));
16743
+ }
16727
16744
  result = (ca_size_t) -1;
16728
16745
  VALUE best = Qnil;
16729
16746
  for ( ca_size_t i = 0; i < slab_n; i++ ) {
16730
16747
  if ( mask_in && mask_in[i * slab_mask_stride] ) continue;
16731
16748
  VALUE v = *(VALUE *)(slab_ptr + i * slab_stride);
16732
- VALUE dist = rb_funcall(query_val, rb_intern("distance"), 1, v);
16749
+ VALUE dist = nearest_by_distance
16750
+ ? rb_funcall(query_val, nearest_id, 1, v)
16751
+ : rb_funcall(rb_funcall(query_val, '-', 1, v),
16752
+ rb_intern("abs"), 0);
16733
16753
  if ( NIL_P(best) || RTEST(rb_funcall(dist, rb_intern("<"), 1, best)) ) {
16734
16754
  best = dist; result = i;
16735
16755
  }
@@ -16975,12 +16995,32 @@ search_nearest_ki_native_object (VALUE self, CArray *ca, VALUE rval, int axis)
16975
16995
  {
16976
16996
  /* CA_OBJECT nearest: minimum of query_val.distance(cell), compared
16977
16997
  with `<` (matches the legacy flat proc_nearest_addr_VALUE). */
16998
+ /* Nearest needs a metric. #distance is the protocol the 2.0 flat
16999
+ kernel used, back when Numeric#distance was a monkey patch; it
17000
+ is an opt-in refinement now, and a refinement does not reach an
17001
+ rb_funcall from C -- so a number reaching here answers no more
17002
+ than a String does. Measure a number the way #distance itself
17003
+ does, keep #distance for anything that defines a real one, and
17004
+ refuse the rest by name rather than let a bare NoMethodError out
17005
+ of the loop below. */
17006
+ ID nearest_id = rb_intern("distance");
17007
+ int nearest_by_distance = rb_respond_to(query_val, nearest_id);
17008
+ if ( ! nearest_by_distance && ! rb_obj_is_kind_of(query_val, rb_cNumeric) ) {
17009
+ rb_raise(rb_eCADataTypeError,
17010
+ "search_nearest: nearest needs a distance, and %s is neither a "
17011
+ "number nor answers #distance (define one on the stored "
17012
+ "objects, or use search / bsearch for an exact match)",
17013
+ rb_obj_classname(query_val));
17014
+ }
16978
17015
  result = (ca_size_t) -1;
16979
17016
  VALUE best = Qnil;
16980
17017
  for ( ca_size_t i = 0; i < slab_n; i++ ) {
16981
17018
  if ( mask_in && mask_in[i * slab_mask_stride] ) continue;
16982
17019
  VALUE v = *(VALUE *)(slab_ptr + i * slab_stride);
16983
- VALUE dist = rb_funcall(query_val, rb_intern("distance"), 1, v);
17020
+ VALUE dist = nearest_by_distance
17021
+ ? rb_funcall(query_val, nearest_id, 1, v)
17022
+ : rb_funcall(rb_funcall(query_val, '-', 1, v),
17023
+ rb_intern("abs"), 0);
16984
17024
  if ( NIL_P(best) || RTEST(rb_funcall(dist, rb_intern("<"), 1, best)) ) {
16985
17025
  best = dist; result = i;
16986
17026
  }
@@ -17055,7 +17095,7 @@ rb_ca_search_nearest_ki (VALUE self, VALUE rval, VALUE raxis)
17055
17095
  }
17056
17096
  if ( self_face_comparable ) {
17057
17097
  if ( rval_is_face ) {
17058
- rval = rb_ca_strip_face_value(rval);
17098
+ rval = ca_face_operand_descend(rval, "search_nearest_ki");
17059
17099
  }
17060
17100
  } else if ( self_was_face ) {
17061
17101
  if ( rb_respond_to(self_ref, rb_intern("to_comparable")) ) {
@@ -21801,7 +21841,7 @@ rb_ca_bsearch_addr_ki (VALUE self, VALUE rval, VALUE raxis)
21801
21841
  }
21802
21842
  if ( self_face_comparable ) {
21803
21843
  if ( rval_is_face ) {
21804
- rval = rb_ca_strip_face_value(rval);
21844
+ rval = ca_face_operand_descend(rval, "bsearch_addr_ki");
21805
21845
  }
21806
21846
  } else if ( self_was_face ) {
21807
21847
  if ( rb_respond_to(self_ref, rb_intern("to_comparable")) ) {
@@ -26403,7 +26443,7 @@ rb_ca_search_addr_ki (int argc, VALUE *argv, VALUE self)
26403
26443
  }
26404
26444
  if ( self_face_comparable ) {
26405
26445
  if ( rval_is_face ) {
26406
- rval = rb_ca_strip_face_value(rval);
26446
+ rval = ca_face_operand_descend(rval, "search_addr_ki");
26407
26447
  }
26408
26448
  } else if ( self_was_face ) {
26409
26449
  if ( rb_respond_to(self_ref, rb_intern("to_comparable")) ) {
@@ -30338,12 +30378,32 @@ for ( int8_t mm = 0; mm < st.outer_ndim; mm++ ) {
30338
30378
  {
30339
30379
  /* CA_OBJECT nearest (view_flat addr): minimum of
30340
30380
  query_val.distance(cell), compared with `<`. */
30381
+ /* Nearest needs a metric. #distance is the protocol the 2.0 flat
30382
+ kernel used, back when Numeric#distance was a monkey patch; it
30383
+ is an opt-in refinement now, and a refinement does not reach an
30384
+ rb_funcall from C -- so a number reaching here answers no more
30385
+ than a String does. Measure a number the way #distance itself
30386
+ does, keep #distance for anything that defines a real one, and
30387
+ refuse the rest by name rather than let a bare NoMethodError out
30388
+ of the loop below. */
30389
+ ID nearest_id = rb_intern("distance");
30390
+ int nearest_by_distance = rb_respond_to(query_val, nearest_id);
30391
+ if ( ! nearest_by_distance && ! rb_obj_is_kind_of(query_val, rb_cNumeric) ) {
30392
+ rb_raise(rb_eCADataTypeError,
30393
+ "search_nearest_addr: nearest needs a distance, and %s is neither a "
30394
+ "number nor answers #distance (define one on the stored "
30395
+ "objects, or use search / bsearch for an exact match)",
30396
+ rb_obj_classname(query_val));
30397
+ }
30341
30398
  result = (ca_size_t) -1;
30342
30399
  VALUE best = Qnil;
30343
30400
  for ( ca_size_t i = 0; i < slab_n; i++ ) {
30344
30401
  if ( mask_in && mask_in[i * slab_mask_stride] ) continue;
30345
30402
  VALUE v = *(VALUE *)(slab_ptr + i * slab_stride);
30346
- VALUE dist = rb_funcall(query_val, rb_intern("distance"), 1, v);
30403
+ VALUE dist = nearest_by_distance
30404
+ ? rb_funcall(query_val, nearest_id, 1, v)
30405
+ : rb_funcall(rb_funcall(query_val, '-', 1, v),
30406
+ rb_intern("abs"), 0);
30347
30407
  if ( NIL_P(best) || RTEST(rb_funcall(dist, rb_intern("<"), 1, best)) ) {
30348
30408
  best = dist; result = i;
30349
30409
  }
@@ -30596,12 +30656,32 @@ rb_raise(rb_eNotImpError, "search_nearest_addr_ki: view_flat semantics with CArr
30596
30656
  {
30597
30657
  /* CA_OBJECT nearest (view_flat addr): minimum of
30598
30658
  query_val.distance(cell), compared with `<`. */
30659
+ /* Nearest needs a metric. #distance is the protocol the 2.0 flat
30660
+ kernel used, back when Numeric#distance was a monkey patch; it
30661
+ is an opt-in refinement now, and a refinement does not reach an
30662
+ rb_funcall from C -- so a number reaching here answers no more
30663
+ than a String does. Measure a number the way #distance itself
30664
+ does, keep #distance for anything that defines a real one, and
30665
+ refuse the rest by name rather than let a bare NoMethodError out
30666
+ of the loop below. */
30667
+ ID nearest_id = rb_intern("distance");
30668
+ int nearest_by_distance = rb_respond_to(query_val, nearest_id);
30669
+ if ( ! nearest_by_distance && ! rb_obj_is_kind_of(query_val, rb_cNumeric) ) {
30670
+ rb_raise(rb_eCADataTypeError,
30671
+ "search_nearest_addr: nearest needs a distance, and %s is neither a "
30672
+ "number nor answers #distance (define one on the stored "
30673
+ "objects, or use search / bsearch for an exact match)",
30674
+ rb_obj_classname(query_val));
30675
+ }
30599
30676
  result = (ca_size_t) -1;
30600
30677
  VALUE best = Qnil;
30601
30678
  for ( ca_size_t i = 0; i < slab_n; i++ ) {
30602
30679
  if ( mask_in && mask_in[i * slab_mask_stride] ) continue;
30603
30680
  VALUE v = *(VALUE *)(slab_ptr + i * slab_stride);
30604
- VALUE dist = rb_funcall(query_val, rb_intern("distance"), 1, v);
30681
+ VALUE dist = nearest_by_distance
30682
+ ? rb_funcall(query_val, nearest_id, 1, v)
30683
+ : rb_funcall(rb_funcall(query_val, '-', 1, v),
30684
+ rb_intern("abs"), 0);
30605
30685
  if ( NIL_P(best) || RTEST(rb_funcall(dist, rb_intern("<"), 1, best)) ) {
30606
30686
  best = dist; result = i;
30607
30687
  }
@@ -30676,7 +30756,7 @@ rb_ca_search_nearest_addr_ki (VALUE self, VALUE rval, VALUE raxis)
30676
30756
  }
30677
30757
  if ( self_face_comparable ) {
30678
30758
  if ( rval_is_face ) {
30679
- rval = rb_ca_strip_face_value(rval);
30759
+ rval = ca_face_operand_descend(rval, "search_nearest_addr_ki");
30680
30760
  }
30681
30761
  } else if ( self_was_face ) {
30682
30762
  if ( rb_respond_to(self_ref, rb_intern("to_comparable")) ) {
@@ -31230,7 +31310,7 @@ rb_ca_linear_section_binary_ki (VALUE self, VALUE rval, VALUE raxis)
31230
31310
  }
31231
31311
  if ( self_face_comparable ) {
31232
31312
  if ( rval_is_face ) {
31233
- rval = rb_ca_strip_face_value(rval);
31313
+ rval = ca_face_operand_descend(rval, "linear_section_binary_ki");
31234
31314
  }
31235
31315
  } else if ( self_was_face ) {
31236
31316
  if ( rb_respond_to(self_ref, rb_intern("to_comparable")) ) {
@@ -31679,7 +31759,7 @@ rb_ca_linear_section_linear_ki (VALUE self, VALUE rval, VALUE raxis)
31679
31759
  }
31680
31760
  if ( self_face_comparable ) {
31681
31761
  if ( rval_is_face ) {
31682
- rval = rb_ca_strip_face_value(rval);
31762
+ rval = ca_face_operand_descend(rval, "linear_section_linear_ki");
31683
31763
  }
31684
31764
  } else if ( self_was_face ) {
31685
31765
  if ( rb_respond_to(self_ref, rb_intern("to_comparable")) ) {
@@ -32090,7 +32170,7 @@ rb_ca_linear_fetch_ki (VALUE self, VALUE rval, VALUE raxis)
32090
32170
  }
32091
32171
  if ( self_face_comparable ) {
32092
32172
  if ( rval_is_face ) {
32093
- rval = rb_ca_strip_face_value(rval);
32173
+ rval = ca_face_operand_descend(rval, "linear_fetch_ki");
32094
32174
  }
32095
32175
  } else if ( self_was_face ) {
32096
32176
  if ( rb_respond_to(self_ref, rb_intern("to_comparable")) ) {
data/ext/carray_loop.c CHANGED
@@ -120,6 +120,13 @@ rb_ca_index_walk (VALUE self, CArray *ca, int8_t level,
120
120
  volatile VALUE ret = Qnil;
121
121
  ca_size_t i;
122
122
  if ( level == ca->ndim - 1 ) {
123
+ /* One value-yield buffer for the whole innermost run. It was once an
124
+ ALLOCA_N inside the loop, which never releases per iteration: the C
125
+ stack grew by (ndim + 1) VALUEs per cell and a long enough axis
126
+ raised SystemStackError (a 1-D each_with_index / map_with_index!
127
+ died around a million cells on the main stack, and far earlier on a
128
+ Thread's smaller one). */
129
+ VALUE argv[CA_RANK_MAX + 1];
123
130
  for (i=0; i<ca->dim[level]; i++) {
124
131
  volatile VALUE obj;
125
132
  idx[level] = i;
@@ -132,7 +139,6 @@ rb_ca_index_walk (VALUE self, CArray *ca, int8_t level,
132
139
  that want the whole subscript should write |*idx|. */
133
140
  if ( mode & CA_LOOP_WITH_VALUE ) {
134
141
  int argc = (int)ca->ndim + 1;
135
- VALUE *argv = ALLOCA_N(VALUE, argc);
136
142
  argv[0] = rb_ca_fetch_index(self, idx);
137
143
  MEMCPY(argv + 1, RARRAY_CONST_PTR(ridx), VALUE, ca->ndim);
138
144
  obj = rb_yield_values2(argc, argv);
data/ext/carray_mask.c CHANGED
@@ -660,32 +660,47 @@ ca_unmask (void *ap, char *fill_value)
660
660
  }
661
661
  }
662
662
 
663
- CArray *
664
- ca_unmask_copy (void *ap, char *fill_value)
665
- {
666
- CArray *ca = (CArray *) ap;
663
+ typedef struct {
664
+ CArray *ca;
667
665
  CArray *co;
666
+ char *fill_value;
667
+ } ca_unmask_copy_args_t;
668
+
669
+ static VALUE
670
+ ca_unmask_copy_fill (VALUE arg)
671
+ {
672
+ ca_unmask_copy_args_t *a = (ca_unmask_copy_args_t *) arg;
673
+ CArray *ca = a->ca, *co = a->co;
668
674
  char *q;
669
675
  boolean8_t *m;
670
676
  ca_size_t i;
671
677
 
672
- co = ca_template(ca);
673
678
  ca_copy_data(ca, co->ptr);
674
679
 
675
- if ( fill_value && ca_has_mask(ca) ) {
680
+ if ( a->fill_value && ca_has_mask(ca) ) {
676
681
  ca_attach(ca);
677
682
  q = co->ptr;
678
683
  m = (boolean8_t *) ca->mask->ptr;
679
684
  for (i=0; i<ca->elements; i++) {
680
685
  if ( *m ) {
681
- memcpy(q, fill_value, ca->bytes);
686
+ memcpy(q, a->fill_value, ca->bytes);
682
687
  }
683
688
  m++; q+=co->bytes;
684
689
  }
685
690
  ca_detach(ca);
686
691
  }
692
+ return Qnil;
693
+ }
687
694
 
688
- return co;
695
+ CArray *
696
+ ca_unmask_copy (void *ap, char *fill_value)
697
+ {
698
+ ca_unmask_copy_args_t a;
699
+ a.ca = (CArray *) ap;
700
+ a.co = ca_template(a.ca);
701
+ a.fill_value = fill_value;
702
+ ca_fill_or_free(a.co, ca_unmask_copy_fill, (VALUE) &a);
703
+ return a.co;
689
704
  }
690
705
 
691
706
  void
@@ -336,6 +336,48 @@ median_flat (VALUE self, long min_count, VALUE fill_value, int keep_axis)
336
336
  Every function in this block (down to the median entry) is [object].
337
337
  ===================================================================== */
338
338
 
339
+ /* [object] Is this operand's arithmetic the arithmetic interpolation means?
340
+ `lo` / `hi` are a stored object on the flat path and an object CArray of
341
+ them on the per-axis one, so look through a CArray to an element. An
342
+ empty one has nothing to interpolate and passes. */
343
+ static int
344
+ mp_object_interpolates (VALUE v)
345
+ {
346
+ if ( rb_obj_is_kind_of(v, rb_cNumeric) ) {
347
+ return 1;
348
+ }
349
+ if ( rb_obj_is_kind_of(v, rb_cCArray) ) {
350
+ CArray *ca;
351
+ GetCArray(v, ca);
352
+ if ( ca->elements == 0 ) {
353
+ return 1;
354
+ }
355
+ return rb_obj_is_kind_of(rb_ca_fetch_addr(v, 0), rb_cNumeric) ? 1 : 0;
356
+ }
357
+ return 0;
358
+ }
359
+
360
+ /* [object] Refuse to interpolate between two elements that have no
361
+ arithmetic meaning it. A String has both a `*` and a `+`, and
362
+ String#* truncates the Float weight to zero -- so :linear quietly
363
+ returned "" instead of failing, which is the reason this guard is
364
+ here rather than a rescue. The three picking methods need no
365
+ arithmetic and are the way through. */
366
+ static void
367
+ mp_require_interpolable (const char *who, VALUE method, VALUE lo)
368
+ {
369
+ if ( mp_object_interpolates(lo) ) {
370
+ return;
371
+ }
372
+ rb_raise(rb_eCADataTypeError,
373
+ "%s: method: :%s interpolates between two elements, which %s "
374
+ "does not support; use method: :lower, :higher or :nearest to "
375
+ "pick an element instead",
376
+ who, rb_id2name(SYM2ID(method)),
377
+ rb_obj_classname(rb_obj_is_kind_of(lo, rb_cCArray)
378
+ ? rb_ca_fetch_addr(lo, 0) : lo));
379
+ }
380
+
339
381
  /* [object] recv.mid(axis: raxis) -- e.g. min/max/sort along an axis. */
340
382
  static VALUE
341
383
  obj_call_axis (VALUE recv, ID mid, VALUE raxis)
@@ -419,6 +461,17 @@ median_object_axis (VALUE self, long axis, long n, int keep_axis)
419
461
  long k = n / 2 - 1;
420
462
  VALUE lo, hi;
421
463
  obj_kth_pair(self, axis, k, n, &lo, &hi);
464
+ /* An even count has no middle element, only the average of two -- so
465
+ a column whose objects have no arithmetic has a median only at odd
466
+ length. percentile(50, method: :lower) names one either way. */
467
+ if ( ! mp_object_interpolates(lo) ) {
468
+ rb_raise(rb_eCADataTypeError,
469
+ "median: an even number of elements has no middle one, and "
470
+ "%s cannot be averaged; use percentile(50, method: :lower) "
471
+ "(or :higher / :nearest) to pick an element instead",
472
+ rb_obj_classname(rb_obj_is_kind_of(lo, rb_cCArray)
473
+ ? rb_ca_fetch_addr(lo, 0) : lo));
474
+ }
422
475
  result = rb_funcall(rb_funcall(lo, id_plus, 1, hi), id_div, 1, DBL2NUM(2.0));
423
476
  } else {
424
477
  VALUE kv = obj_kth_one(self, axis, (n - 1) / 2);
@@ -480,12 +533,14 @@ pct_compute_object (VALUE method, long k, double r, long n, VALUE lo, VALUE hi)
480
533
  }
481
534
  if ( method == sym_linear ) {
482
535
  if ( r == 0.0 || k + 1 >= n ) return rb_funcall(lo, id_mul, 1, DBL2NUM(1.0));
536
+ mp_require_interpolable("percentile", method, lo);
483
537
  VALUE a = rb_funcall(lo, id_mul, 1, DBL2NUM(1.0 - r));
484
538
  VALUE b = rb_funcall(hi, id_mul, 1, DBL2NUM(r));
485
539
  return rb_funcall(a, id_plus, 1, b);
486
540
  }
487
541
  if ( method == sym_midpoint ) {
488
542
  if ( k + 1 >= n ) return rb_funcall(lo, id_mul, 1, DBL2NUM(1.0));
543
+ mp_require_interpolable("percentile", method, lo);
489
544
  return rb_funcall(rb_funcall(lo, id_plus, 1, hi), id_div, 1, DBL2NUM(2.0));
490
545
  }
491
546
  rb_raise(rb_eArgError, "percentile: invalid method (BUG)");
@@ -633,7 +633,7 @@ rb_ca_call_binop (volatile VALUE self, volatile VALUE other,
633
633
  ca_size_t i1, i2, i3;
634
634
  int fast_path;
635
635
 
636
- /* do implicit casting and resolving unbound repeat array */
636
+ /* do implicit casting */
637
637
  rb_ca_cast_self_or_other(&self, &other);
638
638
 
639
639
  TypedData_Get_Struct(self, CArray, &carray_data_type, ca1);
@@ -1030,7 +1030,7 @@ rb_ca_call_binop_bang (VALUE self, VALUE other, ca_binop_func_t func[])
1030
1030
 
1031
1031
  rb_ca_modify(self);
1032
1032
 
1033
- /* do implicit casting and resolving unbound repeat array */
1033
+ /* do implicit casting */
1034
1034
  rb_ca_cast_other(&self, &other);
1035
1035
 
1036
1036
  TypedData_Get_Struct(self, CArray, &carray_data_type, ca1);
@@ -1572,7 +1572,7 @@ rb_ca_call_bincmp (volatile VALUE self, volatile VALUE other,
1572
1572
  for fixlen-storage Faces (memcmp is already correct there). */
1573
1573
  ca_face_reconcile_comparison(&self, &other);
1574
1574
 
1575
- /* do implicit casting and resolving unbound repeat array */
1575
+ /* do implicit casting */
1576
1576
  rb_ca_cast_self_or_other(&self, &other);
1577
1577
 
1578
1578
  TypedData_Get_Struct(self, CArray, &carray_data_type, ca1);
@@ -1804,7 +1804,7 @@ rb_ca_coerce (VALUE self, VALUE other)
1804
1804
  return rb_ca_coerce(self, rb_funcall(other,rb_intern("to_ca"),0));
1805
1805
  }
1806
1806
  else {
1807
- /* do implicit casting and resolving unbound repeat array */
1807
+ /* do implicit casting */
1808
1808
  rb_ca_cast_self_or_other(&self, &other);
1809
1809
  return rb_assoc_new(other, self);
1810
1810
  }
data/ext/carray_order.c CHANGED
@@ -854,7 +854,7 @@ rb_ca_linear_section_m (int argc, VALUE *argv, VALUE self)
854
854
  }
855
855
  if ( self_comparable ) {
856
856
  if ( val_is_face ) {
857
- val = rb_ca_strip_face_value(val);
857
+ val = ca_face_operand_descend(val, "linear_section");
858
858
  }
859
859
  }
860
860
  else if ( self_is_face ) {