carray 3.0.1 → 3.0.3

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 (229) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +824 -0
  3. data/README.md +5 -6
  4. data/carray.gemspec +1 -1
  5. data/ext/ca_axis_dispatch.c +258 -4
  6. data/ext/ca_axis_group.c +368 -184
  7. data/ext/ca_binop_dispatch.c +54 -32
  8. data/ext/ca_binop_dispatch.h +1 -0
  9. data/ext/ca_categorical_iterator.c +90 -41
  10. data/ext/ca_compare.c +8 -2
  11. data/ext/ca_for_buffer.h +21 -68
  12. data/ext/ca_group_iter.c +27 -11
  13. data/ext/ca_kernel_iterator.c +960 -245
  14. data/ext/ca_kernel_iterator.h +352 -181
  15. data/ext/ca_monop_dispatch.c +4 -4
  16. data/ext/ca_monop_dispatch.h +4 -0
  17. data/ext/ca_obj_array.c +194 -42
  18. data/ext/ca_obj_bincmp.c +152 -121
  19. data/ext/ca_obj_binop.c +107 -210
  20. data/ext/ca_obj_bitarray.c +37 -24
  21. data/ext/ca_obj_bitfield.c +95 -62
  22. data/ext/ca_obj_block.c +5 -4
  23. data/ext/ca_obj_byte_swap.c +40 -40
  24. data/ext/ca_obj_const_string.c +180 -50
  25. data/ext/ca_obj_face.c +428 -62
  26. data/ext/ca_obj_face.h +107 -47
  27. data/ext/ca_obj_fake.c +21 -50
  28. data/ext/ca_obj_farray.c +1 -0
  29. data/ext/ca_obj_field.c +16 -3
  30. data/ext/ca_obj_fixlen_string.c +39 -26
  31. data/ext/ca_obj_grid.c +181 -124
  32. data/ext/ca_obj_meld.c +235 -55
  33. data/ext/ca_obj_moncmp.c +61 -61
  34. data/ext/ca_obj_monop.c +125 -107
  35. data/ext/ca_obj_object.c +551 -233
  36. data/ext/ca_obj_record.c +51 -16
  37. data/ext/ca_obj_reduce.c +5 -8
  38. data/ext/ca_obj_refer.c +117 -33
  39. data/ext/ca_obj_remap.c +57 -17
  40. data/ext/ca_obj_repeat.c +21 -5
  41. data/ext/ca_obj_roll.c +115 -11
  42. data/ext/ca_obj_select.c +88 -75
  43. data/ext/ca_obj_select_axis.c +58 -60
  44. data/ext/ca_obj_shift.c +22 -102
  45. data/ext/ca_obj_stack.c +223 -123
  46. data/ext/ca_obj_stride.c +267 -35
  47. data/ext/ca_obj_string.c +25 -22
  48. data/ext/ca_obj_tile.c +32 -16
  49. data/ext/ca_obj_time.c +23 -25
  50. data/ext/ca_obj_timedelta.c +21 -23
  51. data/ext/ca_obj_transpose.c +12 -3
  52. data/ext/ca_obj_triop.c +110 -117
  53. data/ext/ca_obj_window.c +157 -29
  54. data/ext/ca_op_ipower.c +25 -13
  55. data/ext/ca_op_powi.h +57 -30
  56. data/ext/ca_pow5_table.h +656 -0
  57. data/ext/ca_rng_normal.h +42 -0
  58. data/ext/ca_rng_xoshiro256pp.h +105 -0
  59. data/ext/ca_sort_kernels.h +70 -0
  60. data/ext/ca_sweep_engine.c +385 -198
  61. data/ext/ca_sweep_engine.h +31 -23
  62. data/ext/ca_triop_dispatch.c +4 -1
  63. data/ext/ca_triop_dispatch.h +5 -2
  64. data/ext/caframe_csv_reader.c +463 -0
  65. data/ext/carray.h +90 -28
  66. data/ext/carray_access.c +487 -231
  67. data/ext/carray_address_basis.c +731 -0
  68. data/ext/carray_attribute.c +45 -3
  69. data/ext/carray_bincount.c +32 -2
  70. data/ext/carray_broadcast.c +49 -17
  71. data/ext/carray_call_cfunc.c +667 -511
  72. data/ext/carray_cast.c +1556 -247
  73. data/ext/carray_cast_func.rb +34 -21
  74. data/ext/carray_conversion.c +197 -73
  75. data/ext/carray_copy.c +84 -38
  76. data/ext/carray_core.c +591 -119
  77. data/ext/carray_count.c +148 -96
  78. data/ext/carray_element.c +18 -27
  79. data/ext/carray_factorize.c +604 -291
  80. data/ext/carray_generate.c +76 -40
  81. data/ext/carray_histogram.c +152 -94
  82. data/ext/carray_hold.c +2 -1
  83. data/ext/carray_index_classifier.c +63 -96
  84. data/ext/carray_internal.h +400 -1
  85. data/ext/carray_lazy.c +834 -79
  86. data/ext/carray_loop.c +88 -66
  87. data/ext/carray_mask.c +705 -324
  88. data/ext/carray_math_kernel.h +1 -0
  89. data/ext/carray_median_percentile.c +263 -63
  90. data/ext/carray_memory_view.c +254 -248
  91. data/ext/carray_operator.c +1093 -363
  92. data/ext/carray_order.c +133 -69
  93. data/ext/carray_partition.c +102 -56
  94. data/ext/carray_random.c +582 -204
  95. data/ext/carray_scatter.c +308 -65
  96. data/ext/carray_segment.c +257 -0
  97. data/ext/carray_slab.c +183 -70
  98. data/ext/carray_sort.c +235 -240
  99. data/ext/carray_sort_kernel.c +8 -1
  100. data/ext/carray_struct.c +99 -21
  101. data/ext/carray_test.c +167 -64
  102. data/ext/carray_time_text.c +1182 -0
  103. data/ext/carray_utils.c +363 -98
  104. data/ext/extconf.rb +27 -2
  105. data/ext/mk_call_cfunc.rb +103 -118
  106. data/ext/mk_pow5_table.rb +35 -0
  107. data/ext/mkkernel.rb +1349 -851
  108. data/ext/ruby_carray.c +17 -2
  109. data/ext/version.h +4 -4
  110. data/ext/version.rb +4 -2
  111. data/lib/carray/attribute.rb +39 -0
  112. data/lib/carray/autoload_carray.rb +24 -3
  113. data/lib/carray/autoload_method_extension.rb +18 -0
  114. data/lib/carray/axis_group.rb +245 -20
  115. data/lib/carray/basics.rb +95 -25
  116. data/lib/carray/bincount_nd.rb +6 -0
  117. data/lib/carray/block_iterator.rb +200 -54
  118. data/lib/carray/boolean_reduce.rb +21 -29
  119. data/lib/carray/categorical.rb +244 -59
  120. data/lib/carray/categorical_iterator.rb +391 -757
  121. data/lib/carray/complex.rb +36 -29
  122. data/lib/carray/conditional.rb +94 -7
  123. data/lib/carray/const_string.rb +177 -27
  124. data/lib/carray/construct.rb +40 -0
  125. data/lib/carray/data_type_extension.rb +3 -0
  126. data/lib/carray/data_type_limits.rb +91 -0
  127. data/lib/carray/fixlen_string.rb +1 -1
  128. data/lib/carray/frame/concat.rb +157 -6
  129. data/lib/carray/frame/convert.rb +13 -7
  130. data/lib/carray/frame/csv_parser.rb +590 -76
  131. data/lib/carray/frame/describe.rb +79 -0
  132. data/lib/carray/frame/frame.rb +205 -35
  133. data/lib/carray/frame/group.rb +199 -40
  134. data/lib/carray/frame/io.rb +452 -60
  135. data/lib/carray/frame/join.rb +134 -46
  136. data/lib/carray/frame/records.rb +54 -19
  137. data/lib/carray/frame/reshape.rb +448 -0
  138. data/lib/carray/frame/sort.rb +26 -2
  139. data/lib/carray/frame/verbs.rb +726 -56
  140. data/lib/carray/frame.rb +2 -0
  141. data/lib/carray/fuse_source.rb +111 -2
  142. data/lib/carray/fusion.rb +229 -38
  143. data/lib/carray/inspect.rb +71 -20
  144. data/lib/carray/iterator.rb +157 -2
  145. data/lib/carray/lazy.rb +54 -67
  146. data/lib/carray/mask_gap_fill.rb +66 -50
  147. data/lib/carray/math.rb +32 -19
  148. data/lib/carray/meld_reduce.rb +40 -7
  149. data/lib/carray/methods/align_addr.rb +8 -4
  150. data/lib/carray/methods/bincount.rb +2 -2
  151. data/lib/carray/methods/bit_string.rb +14 -3
  152. data/lib/carray/methods/composition.rb +16 -10
  153. data/lib/carray/methods/discovery_along.rb +74 -0
  154. data/lib/carray/methods/factorize.rb +50 -0
  155. data/lib/carray/methods/gather_nd.rb +13 -2
  156. data/lib/carray/methods/insert_block.rb +9 -14
  157. data/lib/carray/methods/is_in.rb +130 -6
  158. data/lib/carray/methods/join.rb +1 -1
  159. data/lib/carray/methods/locate_addr.rb +235 -21
  160. data/lib/carray/methods/mask_duplicates.rb +35 -1
  161. data/lib/carray/methods/mode.rb +18 -1
  162. data/lib/carray/methods/nunique.rb +22 -1
  163. data/lib/carray/methods/repeat.rb +91 -0
  164. data/lib/carray/methods/resize.rb +21 -23
  165. data/lib/carray/methods/snap.rb +34 -12
  166. data/lib/carray/methods/unique.rb +52 -3
  167. data/lib/carray/record_order.rb +208 -0
  168. data/lib/carray/rng.rb +86 -0
  169. data/lib/carray/runtime.rb +14 -11
  170. data/lib/carray/segment_iterator.rb +990 -0
  171. data/lib/carray/serialize.rb +197 -27
  172. data/lib/carray/slab_iterator.rb +109 -40
  173. data/lib/carray/stack.rb +10 -4
  174. data/lib/carray/string_operation_extension.rb +9 -4
  175. data/lib/carray/struct.rb +142 -51
  176. data/lib/carray/struct_builder.rb +110 -14
  177. data/lib/carray/table.rb +2 -2
  178. data/lib/carray/time.rb +535 -105
  179. data/lib/carray/window_iterator.rb +259 -82
  180. data/lib/carray.rb +4 -2
  181. data/yard-stubs/ca_obj_array.rb +4 -4
  182. data/yard-stubs/ca_obj_bitarray.rb +7 -0
  183. data/yard-stubs/ca_obj_bitfield.rb +23 -12
  184. data/yard-stubs/ca_obj_block.rb +2 -7
  185. data/yard-stubs/ca_obj_fake.rb +1 -1
  186. data/yard-stubs/ca_obj_field.rb +1 -1
  187. data/yard-stubs/ca_obj_refer.rb +3 -3
  188. data/yard-stubs/ca_obj_roll.rb +0 -1
  189. data/yard-stubs/ca_obj_stride.rb +10 -7
  190. data/yard-stubs/ca_obj_transpose.rb +1 -1
  191. data/yard-stubs/ca_obj_window.rb +11 -4
  192. data/yard-stubs/carray_access.rb +6 -5
  193. data/yard-stubs/carray_broadcast.rb +2 -2
  194. data/yard-stubs/carray_cast.rb +78 -43
  195. data/yard-stubs/carray_conversion.rb +13 -1
  196. data/yard-stubs/carray_copy.rb +8 -3
  197. data/yard-stubs/carray_core.rb +3 -83
  198. data/yard-stubs/carray_count.rb +16 -2
  199. data/yard-stubs/carray_lazy.rb +210 -1
  200. data/yard-stubs/carray_loop.rb +4 -1
  201. data/yard-stubs/carray_mask.rb +34 -10
  202. data/yard-stubs/carray_math.rb +1499 -4
  203. data/yard-stubs/carray_median_percentile.rb +26 -8
  204. data/yard-stubs/carray_order.rb +43 -82
  205. data/yard-stubs/carray_scatter.rb +10 -3
  206. data/yard-stubs/carray_segment.rb +72 -0
  207. data/yard-stubs/carray_slab.rb +11 -8
  208. data/yard-stubs/carray_sort.rb +50 -8
  209. data/yard-stubs/carray_test.rb +10 -0
  210. data/yard-stubs/carray_utils.rb +0 -26
  211. metadata +19 -19
  212. data/ext/ca_for_each_element.h +0 -239
  213. data/ext/carray_build_flags.h +0 -3
  214. data/ext/carray_config.h +0 -28
  215. data/ext/carray_kernels_bincmp.c +0 -4446
  216. data/ext/carray_kernels_binop.c +0 -11001
  217. data/ext/carray_kernels_init.c +0 -1131
  218. data/ext/carray_kernels_map.c +0 -3467
  219. data/ext/carray_kernels_moncmp.c +0 -2097
  220. data/ext/carray_kernels_monop.c +0 -18313
  221. data/ext/carray_kernels_reduce_aggregate.c +0 -25837
  222. data/ext/carray_kernels_reduce_boolean.c +0 -330
  223. data/ext/carray_kernels_reduce_cumulative.c +0 -14593
  224. data/ext/carray_kernels_reduce_extreme.c +0 -16948
  225. data/ext/carray_kernels_reduce_variance.c +0 -3910
  226. data/ext/carray_kernels_scan.c +0 -3693
  227. data/ext/carray_kernels_search.c +0 -32138
  228. data/ext/carray_kernels_sort.c +0 -10626
  229. data/ext/carray_kernels_triop.c +0 -1392
data/ext/carray_order.c CHANGED
@@ -1,5 +1,6 @@
1
1
  #include "ruby.h"
2
2
  #include "carray.h"
3
+ #include "carray_internal.h" /* ca_integer_arg */
3
4
  #include "ca_kernel_iterator.h"
4
5
  #include "ca_obj_face.h"
5
6
  #include <math.h>
@@ -120,7 +121,7 @@ ca_project (CArray *ca, CArray *ci, char *lfill, char *ufill)
120
121
  {
121
122
  CArray *co;
122
123
 
123
- ca_attach_n(2, ca, ci); /* ATTACH */
124
+ ca_attach_n(2, ca, ci); /* window: nothing raises inside */
124
125
 
125
126
  co = carray_new(ca->data_type, ci->ndim, ci->dim, ca->bytes, NULL);
126
127
  ca_project_loop(co, ca, ci, lfill, ufill);
@@ -130,6 +131,96 @@ ca_project (CArray *ca, CArray *ci, char *lfill, char *ufill)
130
131
  return co;
131
132
  }
132
133
 
134
+ /* rk.__count_below__(q, inclusive) -- for each cell of q, how many values
135
+ * of the sorted array rk are below it (at or below it when inclusive).
136
+ * rk is 1-D, ascending, with no masked or NaN value; q has rk's data type
137
+ * (any shape, read in address order). The count is the insertion position
138
+ * of the cell among rk's values, found by binary search; a masked or NaN
139
+ * cell gets an arbitrary count, for the caller to mask. Returns an int64
140
+ * array of q's shape. Numeric and boolean types only. */
141
+
142
+ /* The search keeps a base and a length and moves the base by a comparison
143
+ rather than branching on it, which keeps the loop free of mispredicted
144
+ branches; the last comparison adds the cell at the base. */
145
+ #define CA_COUNT_BELOW_LOOP(T, CMP) \
146
+ { \
147
+ const T *r = (const T *) cr->ptr; \
148
+ const T *x = (const T *) cq->ptr; \
149
+ for ( i = 0; i < m; i++ ) { \
150
+ T v = x[i]; \
151
+ const T *base = r; \
152
+ ca_size_t len = n; \
153
+ if ( len == 0 ) { out[i] = 0; continue; } \
154
+ while ( len > 1 ) { \
155
+ ca_size_t half = len / 2; \
156
+ base = (base[half] CMP v) ? base + half : base; \
157
+ len -= half; \
158
+ } \
159
+ out[i] = (int64_t) (base - r) + ((*base CMP v) ? 1 : 0); \
160
+ } \
161
+ }
162
+
163
+ #define CA_COUNT_BELOW(T) \
164
+ if ( inclusive ) CA_COUNT_BELOW_LOOP(T, <=) \
165
+ else CA_COUNT_BELOW_LOOP(T, <)
166
+
167
+ static VALUE
168
+ rb_ca_count_below (VALUE self, VALUE vq, VALUE vinclusive)
169
+ {
170
+ volatile VALUE vout;
171
+ CArray *cr, *cq, *co;
172
+ ca_size_t n, m, i;
173
+ int64_t *out;
174
+ int inclusive = RTEST(vinclusive);
175
+
176
+ GetCArray(self, cr);
177
+ rb_check_carray_object(vq);
178
+ GetCArray(vq, cq);
179
+ if ( cr->ndim != 1 ) {
180
+ rb_raise(rb_eArgError, "__count_below__: the reference must be 1-D");
181
+ }
182
+ if ( cr->data_type != cq->data_type ) {
183
+ rb_raise(rb_eCADataTypeError,
184
+ "__count_below__: the reference and the query must have one data type");
185
+ }
186
+ switch ( cr->data_type ) {
187
+ case CA_BOOLEAN: case CA_INT8: case CA_UINT8: case CA_INT16: case CA_UINT16:
188
+ case CA_INT32: case CA_UINT32: case CA_INT64: case CA_UINT64:
189
+ case CA_FLOAT32: case CA_FLOAT64:
190
+ break;
191
+ default:
192
+ rb_raise(rb_eCADataTypeError, "__count_below__: no order for %s",
193
+ ca_type_name[cr->data_type]);
194
+ }
195
+
196
+ n = cr->elements;
197
+ m = cq->elements;
198
+ vout = rb_carray_new(CA_INT64, cq->ndim, cq->dim, 0, NULL);
199
+ GetCArray(vout, co);
200
+ out = (int64_t *) co->ptr;
201
+
202
+ ca_attach_n(2, cr, cq); /* window: nothing raises inside */
203
+ switch ( cr->data_type ) {
204
+ case CA_BOOLEAN:
205
+ case CA_UINT8: CA_COUNT_BELOW(uint8_t); break;
206
+ case CA_INT8: CA_COUNT_BELOW(int8_t); break;
207
+ case CA_INT16: CA_COUNT_BELOW(int16_t); break;
208
+ case CA_UINT16: CA_COUNT_BELOW(uint16_t); break;
209
+ case CA_INT32: CA_COUNT_BELOW(int32_t); break;
210
+ case CA_UINT32: CA_COUNT_BELOW(uint32_t); break;
211
+ case CA_INT64: CA_COUNT_BELOW(int64_t); break;
212
+ case CA_UINT64: CA_COUNT_BELOW(uint64_t); break;
213
+ case CA_FLOAT32: CA_COUNT_BELOW(float); break;
214
+ case CA_FLOAT64: CA_COUNT_BELOW(double); break;
215
+ }
216
+ ca_detach_n(2, cr, cq);
217
+
218
+ return vout;
219
+ }
220
+
221
+ #undef CA_COUNT_BELOW
222
+ #undef CA_COUNT_BELOW_LOOP
223
+
133
224
  /* project(idx, lval=nil, uval=nil) — gather: for each element of `idx`,
134
225
  * pick `self[idx[i]]`. Returns an entity CArray shaped like `idx`.
135
226
  *
@@ -142,6 +233,7 @@ VALUE
142
233
  rb_ca_project (int argc, VALUE *argv, VALUE self)
143
234
  {
144
235
  volatile VALUE obj, ridx, vlfval, vufval, vstorage;
236
+ volatile VALUE hlfval = 0, hufval = 0;
145
237
  CArray *ca, *ci, *co;
146
238
  char *lfval, *ufval;
147
239
  int self_is_face;
@@ -152,14 +244,15 @@ rb_ca_project (int argc, VALUE *argv, VALUE self)
152
244
  self_is_face = ca_is_face(ca);
153
245
 
154
246
  rb_check_carray_object(ridx);
247
+ ca_check_index_array(ridx);
155
248
  ci = ca_wrap_readonly(ridx, CA_SIZE);
156
249
 
157
250
  /* The fill args arrive as surface values. rb_ca_obj2ptr owns the
158
- surface->storage conversion: for a Face self it fires the scalar_to_storage
251
+ surface->storage conversion: for a Face self it fires the element_to_storage
159
252
  write hook (a datetime scalar / Time is reconciled to the Face's unit),
160
253
  for a plain array it writes the storage bytes directly. */
161
- lfval = xmalloc(ca->bytes);
162
- ufval = xmalloc(ca->bytes);
254
+ lfval = ALLOCV_N(char, hlfval, ca->bytes); /* converting a fill value */
255
+ ufval = ALLOCV_N(char, hufval, ca->bytes); /* can raise; the unwind frees */
163
256
 
164
257
  if ( ! NIL_P(vlfval) ) {
165
258
  rb_ca_obj2ptr(self, vlfval, lfval);
@@ -185,8 +278,8 @@ rb_ca_project (int argc, VALUE *argv, VALUE self)
185
278
  ( ! NIL_P(vlfval) ) ? lfval : NULL,
186
279
  ( ( ! NIL_P(vufval) ) || ( ! NIL_P(vlfval) ) ) ? ufval : NULL);
187
280
 
188
- xfree(lfval);
189
- xfree(ufval);
281
+ ALLOCV_END(hlfval);
282
+ ALLOCV_END(hufval);
190
283
 
191
284
  obj = ca_wrap_struct(co);
192
285
 
@@ -255,7 +348,7 @@ extern VALUE rb_ca_reshape (int argc, VALUE *argv, VALUE self)
255
348
  * through rb_funcall + kwarg hash construction. */
256
349
  extern VALUE rb_ca_sort_index_ki_quick (VALUE self, VALUE vaxis);
257
350
  extern VALUE rb_ca_partition_index_ki (VALUE self, VALUE vaxis, VALUE vkth);
258
- extern VALUE rb_ca_rank_index_ki_quick_dense (VALUE self, VALUE vaxis, int dense);
351
+ extern VALUE rb_ca_rank_index_ki_kind_dense (VALUE self, VALUE vaxis, int stable, int dense);
259
352
 
260
353
  /* ---- no-axis (flat) search helper -----------------------------------
261
354
  *
@@ -473,7 +566,7 @@ rb_ca_max_by_key (int argc, VALUE *argv, VALUE self)
473
566
  else {
474
567
  VALUE kw = rb_hash_new();
475
568
  rb_hash_aset(kw, ID2SYM(id_axis), raxis);
476
- addrs = rb_ca_argmax_addr_ki(1, &kw, rkey);
569
+ addrs = rb_funcallv_kw(rkey, rb_intern("max_addr"), 1, &kw, RB_PASS_KEYWORDS);
477
570
  }
478
571
  return rb_ca_fetch(self, addrs);
479
572
  }
@@ -498,7 +591,7 @@ rb_ca_min_by_key (int argc, VALUE *argv, VALUE self)
498
591
  else {
499
592
  VALUE kw = rb_hash_new();
500
593
  rb_hash_aset(kw, ID2SYM(id_axis), raxis);
501
- addrs = rb_ca_argmin_addr_ki(1, &kw, rkey);
594
+ addrs = rb_funcallv_kw(rkey, rb_intern("min_addr"), 1, &kw, RB_PASS_KEYWORDS);
502
595
  }
503
596
  return rb_ca_fetch(self, addrs);
504
597
  }
@@ -515,6 +608,11 @@ rb_ca_take_along_axis_c (VALUE self, VALUE indices, VALUE raxis)
515
608
  raxis = INT2FIX(0);
516
609
  }
517
610
  VALUE addrs = rb_ca_axis2addr_c(self, indices, raxis);
611
+ /* A masked index reads as UNDEF: project carries the mask of its
612
+ addresses into the result. */
613
+ if ( RTEST(rb_ca_has_mask(addrs)) ) {
614
+ return rb_funcall(self, rb_intern("project"), 1, addrs);
615
+ }
518
616
  return rb_ca_fetch(self, addrs);
519
617
  }
520
618
 
@@ -539,6 +637,12 @@ rb_ca_put_along_axis (int argc, VALUE *argv, VALUE self)
539
637
  raxis = INT2FIX(0);
540
638
  }
541
639
  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). */
642
+ if ( RTEST(rb_ca_has_mask(addrs)) ) {
643
+ rb_funcall(self, rb_intern("scatter_replace!"), 2, addrs, argv[1]);
644
+ return self;
645
+ }
542
646
  rb_ca_store(self, addrs, argv[1]);
543
647
  return self;
544
648
  }
@@ -611,12 +715,14 @@ rb_ca_topk_index (VALUE self, VALUE rn, VALUE raxis, int desc, const char *name)
611
715
  CArray *ca;
612
716
  TypedData_Get_Struct(self, CArray, &carray_data_type, ca);
613
717
  long a = rb_ca_normalize_axis_value(self, raxis, name);
614
- long n_val = NUM2LONG(rn);
718
+ long n_val = ca_integer_arg(rn, "n", name);
615
719
  long dim_a = (long) ca->dim[a];
616
- long cap = (n_val < dim_a) ? n_val : dim_a;
617
- if ( cap < 0 ) {
618
- cap = 0;
720
+ /* As Array#max(n): a negative count is an error, one past the length
721
+ gives the whole fiber. */
722
+ if ( n_val < 0 ) {
723
+ rb_raise(rb_eArgError, "%s: n must be non-negative (got %ld)", name, n_val);
619
724
  }
725
+ long cap = (n_val < dim_a) ? n_val : dim_a;
620
726
  if ( cap == 0 ) {
621
727
  /* Return zero-along-axis int64 array via the C carray constructor. */
622
728
  ca_size_t out_dim[CA_RANK_MAX];
@@ -711,29 +817,20 @@ static VALUE
711
817
  rb_ca_order (int argc, VALUE *argv, VALUE self)
712
818
  {
713
819
  VALUE ropt = rb_pop_options(&argc, &argv);
714
- VALUE raxis = Qnil, rdesc = Qfalse, rmethod = Qnil;
715
- rb_scan_options(ropt, "axis,descending,method", &raxis, &rdesc, &rmethod);
820
+ VALUE raxis = Qnil, rdesc = Qfalse, rmethod = Qnil, rkind = Qnil;
821
+ rb_scan_options(ropt, "axis,descending,method,kind", &raxis, &rdesc, &rmethod, &rkind);
716
822
  rb_check_arity(argc, 0, 0);
717
823
 
718
- int dense = 0; /* default method: :ordinal */
719
- if ( ! NIL_P(rmethod) ) {
720
- static ID sym_ordinal = 0, sym_dense = 0;
721
- if ( ! sym_ordinal ) sym_ordinal = rb_intern("ordinal");
722
- if ( ! sym_dense ) sym_dense = rb_intern("dense");
723
- ID method_id = SYM2ID(rmethod);
724
- if ( method_id == sym_ordinal ) dense = 0;
725
- else if ( method_id == sym_dense ) dense = 1;
726
- else {
727
- rb_raise(rb_eArgError, "order: unknown method %s (expected :ordinal or :dense)",
728
- rb_id2name(method_id));
729
- }
730
- }
824
+ int dense = NIL_P(rmethod) ? 0
825
+ : ca_symbol_choice(rmethod, "method", "ordinal", "dense", NULL);
826
+ int do_stable = NIL_P(rkind) ? 0
827
+ : ca_symbol_choice(rkind, "kind", "quick", "stable", NULL);
731
828
 
732
829
  VALUE asc, n;
733
830
  if ( NIL_P(raxis) ) {
734
831
  VALUE flat = rb_ca_flatten(self);
735
832
  /* Direct C-level dispatch via c_callable: true extern (mkkernel). */
736
- VALUE ranked = rb_ca_rank_index_ki_quick_dense(flat, INT2FIX(0), dense);
833
+ VALUE ranked = rb_ca_rank_index_ki_kind_dense(flat, INT2FIX(0), do_stable, dense);
737
834
  /* reshape(*shape) */
738
835
  CArray *ca;
739
836
  TypedData_Get_Struct(self, CArray, &carray_data_type, ca);
@@ -748,7 +845,7 @@ rb_ca_order (int argc, VALUE *argv, VALUE self)
748
845
  } else {
749
846
  long axis_norm = rb_ca_normalize_axis_value(self, raxis, "order");
750
847
  VALUE raxis_norm = LONG2NUM(axis_norm);
751
- asc = rb_ca_rank_index_ki_quick_dense(self, raxis_norm, dense);
848
+ asc = rb_ca_rank_index_ki_kind_dense(self, raxis_norm, do_stable, dense);
752
849
  if ( RTEST(rb_ca_has_mask(self)) ) {
753
850
  n = rb_ca_count_not_masked_c(self, raxis_norm);
754
851
  } else {
@@ -800,10 +897,7 @@ ca_linear_prep (VALUE self, VALUE raxis, const char *name, long *axis_out)
800
897
  float-numeric flag is needed. */
801
898
  if ( ca_is_face(ca) ) {
802
899
  if ( ! ca_test_flag(ca, CA_FLAG_FACE_ORDERABLE_STORAGE) ) {
803
- rb_raise(rb_eArgError,
804
- "%s: Face-typed input (%s) is not orderable by storage; "
805
- "use ca.parent to descend to storage",
806
- name, rb_obj_classname(self));
900
+ ca_face_refuse_order(name, self, "order");
807
901
  }
808
902
  self = rb_ca_strip_face_value(self);
809
903
  TypedData_Get_Struct(self, CArray, &carray_data_type, ca);
@@ -831,44 +925,13 @@ rb_ca_linear_section_m (int argc, VALUE *argv, VALUE self)
831
925
  rb_check_arity(argc, 1, 1);
832
926
  VALUE val = argv[0];
833
927
 
834
- /* Query gate (generic, mirror search + reference flip
835
- PROPOSAL_TO_COMPARABLE_RECEIVER_FLIP): the reference axis Face reconciles
836
- the query into its own space. A COMPARABLE axis is directly comparable
837
- -> strip a Face query, take a plain query as-is. A non-COMPARABLE Face
838
- axis calls its own to_comparable(query) for ANY query type (Face CArray,
839
- our Scalar, a Ruby Time / DateTime, ...), then strips; it raises if it
840
- cannot reconcile the query. `self` is still the pre-strip axis Face
841
- here, so it carries the unit for to_comparable and its COMPARABLE flag. */
842
- {
843
- int val_is_face = 0, self_is_face = 0, self_comparable = 0;
928
+ /* Query gate: the reference axis, still a Face here, reconciles the query
929
+ into its own space (ca_face_reconcile_operand). */
930
+ if ( rb_obj_is_carray(self) ) {
844
931
  CArray *sca;
845
- if ( rb_obj_is_kind_of(val, rb_cCArray) ) {
846
- CArray *vca;
847
- TypedData_Get_Struct(val, CArray, &carray_data_type, vca);
848
- val_is_face = ca_is_face(vca);
849
- }
850
932
  TypedData_Get_Struct(self, CArray, &carray_data_type, sca);
851
933
  if ( ca_is_face(sca) ) {
852
- self_is_face = 1;
853
- self_comparable = ca_test_flag(sca, CA_FLAG_FACE_COMPARABLE_STORAGE);
854
- }
855
- if ( self_comparable ) {
856
- if ( val_is_face ) {
857
- val = rb_ca_strip_face_value(val);
858
- }
859
- }
860
- else if ( self_is_face ) {
861
- if ( rb_respond_to(self, rb_intern("to_comparable")) ) {
862
- val = rb_funcall(self, rb_intern("to_comparable"), 1, val);
863
- val = rb_ca_strip_face_value(val);
864
- }
865
- else {
866
- rb_raise(rb_eArgError,
867
- "linear_section: non-comparable Face axis (%s) has no "
868
- "to_comparable to reconcile the query; use ca.parent to "
869
- "search the hidden storage explicitly",
870
- rb_obj_classname(self));
871
- }
934
+ val = ca_face_reconcile_operand(self, val, "linear_section");
872
935
  }
873
936
  }
874
937
 
@@ -925,6 +988,7 @@ Init_carray_order (void)
925
988
  id_sym_linear = rb_intern("linear");
926
989
 
927
990
  rb_define_method(rb_cCArray, "project", rb_ca_project, -1);
991
+ rb_define_private_method(rb_cCArray, "__count_below__", rb_ca_count_below, 2);
928
992
 
929
993
  /* Search family (dual API: index / addr) -- see the trampoline
930
994
  comment block above for the axis: kwarg dispatch contract. */
@@ -14,6 +14,7 @@
14
14
 
15
15
  #include "ruby.h"
16
16
  #include "carray.h"
17
+ #include "carray_internal.h" /* ca_axis_integer */
17
18
  #include "ca_kernel_iterator.h" /* CA_FOR_EACH_FIBER_INOUT */
18
19
  #include "ca_obj_face.h" /* CA_FACE_LIFT_IF_FACE */
19
20
  #include "ca_sort_kernels.h" /* ca_partition_quick_* / ca_partition_nan_* */
@@ -23,7 +24,6 @@
23
24
  /* External entries reached at link time (extern declarations rather than
24
25
  carray.h additions to keep the public header lean): */
25
26
  extern VALUE rb_ca_remap_new (VALUE cary, VALUE rmapper); /* ca_obj_remap.c */
26
- extern VALUE rb_ca_partition_addr_ki (VALUE self, VALUE vaxis, VALUE vkth); /* carray_kernels.c (generated, bind_ruby: false) */
27
27
  /* _mp ("masked position") twin: explicit masked_last, used by `partition`
28
28
  / `partition_copy` to pass masked_position: through (see MASKED_POSITION
29
29
  rev1 in mkkernel.rb's MkKernel.sort doc). */
@@ -111,7 +111,21 @@ ca_quickselect_bytes (char *buf, ca_size_t lo, ca_size_t hi,
111
111
  #undef QSCMP
112
112
  }
113
113
 
114
- /* partition_copy(kth, axis: 0) — eager counterpart to `partition(kth,
114
+ /* The kth of a partition along a fiber of length n: an Integer in
115
+ -n <= kth < n, negative counting from the end. */
116
+ static ca_size_t
117
+ partition_kth (ca_size_t n, VALUE vkth)
118
+ {
119
+ ca_size_t raw = (ca_size_t) ca_integer_arg(vkth, "kth", NULL);
120
+ ca_size_t kth = ( raw < 0 ) ? raw + n : raw;
121
+ if ( kth < 0 || kth >= n ) {
122
+ rb_raise(rb_eArgError, "%s: kth %lld out of range for length %lld",
123
+ ca_calling_method_name(), (long long) raw, (long long) n);
124
+ }
125
+ return kth;
126
+ }
127
+
128
+ /* partition_copy(kth, axis:) — eager counterpart to `partition(kth,
115
129
  * axis:)`. Returns a fresh entity CArray with the kth fiber-local
116
130
  * position holding the kth-smallest value. Average O(n) per fiber via
117
131
  * quickselect. Mask handling, axis kwarg, kth validation: identical to
@@ -141,22 +155,10 @@ rb_ca_partition_copy_c (VALUE self, VALUE vkth, VALUE vaxis)
141
155
  }
142
156
 
143
157
  /* Normalize axis. */
144
- int axis = NUM2INT(vaxis);
145
- if ( axis < 0 ) axis += cat->ndim;
146
- if ( axis < 0 || axis >= cat->ndim ) {
147
- rb_raise(rb_eArgError, "partition_copy: axis %d out of range for ndim %d",
148
- NUM2INT(vaxis), cat->ndim);
149
- }
158
+ int axis = rb_ca_normalize_axis_for_ndim(ca_axis_integer(vaxis, NULL), cat->ndim, NULL);
150
159
 
151
- /* Normalize kth (-dim[axis] <= kth < dim[axis], negative counts from end). */
152
160
  ca_size_t fiber_n = cat->dim[axis];
153
- ca_size_t kth = (ca_size_t) NUM2SSIZET(vkth);
154
- if ( kth < 0 ) kth += fiber_n;
155
- if ( kth < 0 || kth >= fiber_n ) {
156
- rb_raise(rb_eArgError,
157
- "partition_copy: kth %ld out of range for axis %d (dim=%ld)",
158
- (long) NUM2SSIZET(vkth), axis, (long) fiber_n);
159
- }
161
+ ca_size_t kth = partition_kth(fiber_n, vkth);
160
162
 
161
163
  /* CA_OBJECT branch: partition_index_ki carries the rb_funcall(<=>)
162
164
  * comparator. Build the entity via partition_index_ki ->
@@ -193,8 +195,10 @@ rb_ca_partition_copy_c (VALUE self, VALUE vkth, VALUE vaxis)
193
195
  INT2NUM((int) fbytes));
194
196
  CArray *cao;
195
197
  TypedData_Get_Struct(vout, CArray, &carray_data_type, cao);
196
- char *swap_tmp = ALLOCA_N(char, fbytes);
197
- char *pivot = ALLOCA_N(char, fbytes);
198
+ /* Two cells of scratch. ALLOCV, not the stack: a cell can be wide. */
199
+ volatile VALUE scratch_holder = 0;
200
+ char *swap_tmp = ALLOCV_N(char, scratch_holder, 2 * (size_t) fbytes);
201
+ char *pivot = swap_tmp + fbytes;
198
202
  ca_iter_state st_in, st_out;
199
203
  char *pi, *po;
200
204
  ca_size_t n;
@@ -205,15 +209,16 @@ rb_ca_partition_copy_c (VALUE self, VALUE vkth, VALUE vaxis)
205
209
  ca_quickselect_bytes(po, 0, n - 1, kth, fbytes, NULL, swap_tmp, pivot);
206
210
  }
207
211
  }
212
+ ALLOCV_END(scratch_holder);
208
213
  return vout;
209
214
  }
210
215
 
211
216
  if ( cat->data_type != CA_BOOLEAN &&
212
217
  (cat->data_type < CA_INT8 || cat->data_type > CA_FLOAT64) ) {
213
218
  rb_raise(rb_eCADataTypeError,
214
- "partition_copy: data_type %d not supported "
215
- "(expected one of: bool, i8, u8, i16, u16, i32, u32, i64, u64, f32, f64, object)",
216
- cat->data_type);
219
+ "partition_copy: source data_type :%s not supported "
220
+ "(expected boolean, an integer or float type, or object)",
221
+ ca_type_name[cat->data_type]);
217
222
  }
218
223
 
219
224
  volatile VALUE vout = rb_ca_template_with_type(target,
@@ -289,6 +294,13 @@ rb_ca_partition_copy_c_mp (VALUE self, VALUE vkth, VALUE vaxis, int masked_last)
289
294
  if ( ! ca_has_mask(ca) ) {
290
295
  return rb_ca_partition_copy_c(self, vkth, vaxis);
291
296
  }
297
+ /* Check axis and kth here, so a bad one is reported as partition_copy's
298
+ rather than as the partition this delegates to. */
299
+ {
300
+ int axis = rb_ca_normalize_axis_for_ndim(ca_axis_integer(vaxis, NULL),
301
+ ca->ndim, NULL);
302
+ partition_kth(ca->dim[axis], vkth);
303
+ }
292
304
  /* rb_ca_partitioned_view's rb_scan_args "1:" pattern needs call-frame
293
305
  keyword-splat state, only set by full Ruby method dispatch -- a raw
294
306
  C call segfaults/misparses (same constraint documented on
@@ -307,34 +319,56 @@ rb_ca_partition_copy_c_mp (VALUE self, VALUE vkth, VALUE vaxis, int masked_last)
307
319
  return obj;
308
320
  }
309
321
 
310
- /* Ruby entry: partition_copy(kth, axis: 0, masked_position: :last) */
322
+ /* Ruby entry: partition_copy(kth, axis: nil, masked_position: :last).
323
+ No axis means the whole array, as for sort_copy: flatten first. */
311
324
  static VALUE
312
- rb_ca_partition_copy (int argc, VALUE *argv, VALUE self)
325
+ rb_ca_partition_copy_body (int argc, VALUE *argv, VALUE self)
313
326
  {
314
327
  VALUE vkth, rkw = Qnil;
315
- VALUE vaxis = INT2NUM(0);
328
+ VALUE vaxis = Qnil;
316
329
  VALUE vmasked_position = Qnil;
317
330
  rb_scan_args(argc, argv, "1:", &vkth, &rkw);
318
331
  rb_scan_options(rkw, "axis,masked_position", &vaxis, &vmasked_position);
332
+ if ( NIL_P(vaxis) ) {
333
+ self = rb_ca_flatten(self);
334
+ vaxis = INT2NUM(0);
335
+ }
319
336
 
320
- int masked_last = 1;
321
- if ( !NIL_P(vmasked_position) ) {
322
- static ID sym_first = 0, sym_last = 0;
323
- if ( !sym_first ) sym_first = rb_intern("first");
324
- if ( !sym_last ) sym_last = rb_intern("last");
325
- ID mp_id = SYM2ID(vmasked_position);
326
- if ( mp_id == sym_last ) masked_last = 1;
327
- else if ( mp_id == sym_first ) masked_last = 0;
328
- else {
329
- rb_raise(rb_eArgError,
330
- "partition_copy: unknown masked_position %s (expected :first or :last)",
331
- rb_id2name(mp_id));
337
+ int masked_last = NIL_P(vmasked_position) ? 1
338
+ : ca_symbol_choice(vmasked_position, "masked_position", "first", "last", NULL);
339
+
340
+ /* Face gate, the same policy as the sort family's: a fixlen storage
341
+ partitions by memcmp, a numeric storage only when ORDERABLE (its order
342
+ is the surface order). The selection runs on the storage and the Face
343
+ is put back on the result. Without this the surface fixlen bytes were
344
+ selected by memcmp, which is not the order of a little-endian integer
345
+ (CATime) and not the label order of a code (CACategorical). */
346
+ volatile VALUE face = Qnil;
347
+ {
348
+ CArray *ca;
349
+ TypedData_Get_Struct(self, CArray, &carray_data_type, ca);
350
+ if ( ca_is_face(ca) ) {
351
+ VALUE st = rb_ca_strip_face_value(self);
352
+ CArray *sc;
353
+ TypedData_Get_Struct(st, CArray, &carray_data_type, sc);
354
+ if ( sc->data_type != CA_FIXLEN
355
+ && ! ca_test_flag(ca, CA_FLAG_FACE_ORDERABLE_STORAGE) ) {
356
+ ca_face_refuse_order("partition_copy", self, "order");
357
+ }
358
+ face = self;
359
+ self = st;
332
360
  }
333
361
  }
334
- return rb_ca_partition_copy_c_mp(self, vkth, vaxis, masked_last);
362
+
363
+ VALUE out = rb_ca_partition_copy_c_mp(self, vkth, vaxis, masked_last);
364
+
365
+ if ( face != Qnil ) {
366
+ out = ca_face_relift_entity(face, out);
367
+ }
368
+ return out;
335
369
  }
336
370
 
337
- /* partition(kth, axis: 0, masked_position: :last)
371
+ /* partition(kth, axis: nil, masked_position: :last)
338
372
  *
339
373
  * Returns a CARemap view of +self+ partitioned along +axis+ such that
340
374
  * the cell at the kth fiber-local position contains the kth-smallest
@@ -344,7 +378,7 @@ rb_ca_partition_copy (int argc, VALUE *argv, VALUE self)
344
378
  * :partition).
345
379
  *
346
380
  * Signature mirrors rb_ca_sorted_view: positional kth + optional axis: /
347
- * masked_position: kwargs (default 0 / :last). Mask handling mirrors
381
+ * masked_position: kwargs (default nil = whole array / :last). Mask handling mirrors
348
382
  * {sort}: masked cells are an incomparable sentinel clustered at
349
383
  * masked_position:, excluded from the kth-selection; a kth landing in
350
384
  * the masked cluster needs no selection (unspecified order, same
@@ -356,33 +390,27 @@ static VALUE
356
390
  rb_ca_partitioned_view (int argc, VALUE *argv, VALUE self)
357
391
  {
358
392
  VALUE vkth, rkw = Qnil;
359
- VALUE vaxis = INT2NUM(0);
393
+ VALUE vaxis = Qnil;
360
394
  VALUE vmasked_position = Qnil;
361
395
 
362
396
  rb_scan_args(argc, argv, "1:", &vkth, &rkw);
363
397
  rb_scan_options(rkw, "axis,masked_position", &vaxis, &vmasked_position);
364
398
 
365
- int masked_last = 1;
366
- if ( !NIL_P(vmasked_position) ) {
367
- static ID sym_first = 0, sym_last = 0;
368
- if ( !sym_first ) sym_first = rb_intern("first");
369
- if ( !sym_last ) sym_last = rb_intern("last");
370
- ID mp_id = SYM2ID(vmasked_position);
371
- if ( mp_id == sym_last ) masked_last = 1;
372
- else if ( mp_id == sym_first ) masked_last = 0;
373
- else {
374
- rb_raise(rb_eArgError,
375
- "partition: unknown masked_position %s (expected :first or :last)",
376
- rb_id2name(mp_id));
377
- }
378
- }
399
+ int masked_last = NIL_P(vmasked_position) ? 1
400
+ : ca_symbol_choice(vmasked_position, "masked_position", "first", "last", NULL);
379
401
 
380
402
  CArray *ca;
381
403
  TypedData_Get_Struct(self, CArray, &carray_data_type, ca);
382
404
 
383
405
  /* CA_FIXLEN flows through the same partition_addr_ki fixlen dialect
384
- as numeric; masked_position: applies uniformly across data types. */
385
- VALUE target = self;
406
+ as numeric; masked_position: applies uniformly across data types.
407
+ No axis means the whole array, as for sort: partition the flattened
408
+ view. */
409
+ volatile VALUE target = self;
410
+ if ( NIL_P(vaxis) ) {
411
+ target = rb_ca_flatten(self);
412
+ vaxis = INT2NUM(0);
413
+ }
386
414
 
387
415
  /* partition_addr_ki_mp validates axis + kth, splits masked cells to
388
416
  masked_position:, and quickselects per fiber over the unmasked
@@ -400,6 +428,24 @@ rb_ca_partitioned_view (int argc, VALUE *argv, VALUE self)
400
428
 
401
429
  /* ----------------------------------------------------------------------- */
402
430
 
431
+
432
+ typedef struct { int argc; VALUE *argv; VALUE self; } pc_call_t;
433
+
434
+ static VALUE
435
+ pc_answer_body (VALUE p)
436
+ {
437
+ pc_call_t *c = (pc_call_t *) p;
438
+ return rb_ca_partition_copy_body(c->argc, c->argv, c->self);
439
+ }
440
+
441
+ /* An error raised by the methods this one calls names this one. */
442
+ static VALUE
443
+ rb_ca_partition_copy (int argc, VALUE *argv, VALUE self)
444
+ {
445
+ pc_call_t c = { argc, argv, self };
446
+ return ca_answer_as_caller(pc_answer_body, (VALUE) &c);
447
+ }
448
+
403
449
  void
404
450
  Init_carray_partition (void)
405
451
  {