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_scatter.c CHANGED
@@ -14,7 +14,9 @@
14
14
  (boolean / object / fixlen → CADataTypeError; the bang
15
15
  cannot widen self, same rationale as fma! / fms!)
16
16
  replace: numeric or boolean (assignment, no widening)
17
- scalar Integer / Float, and Complex when self is complex
17
+ scalar any Numeric, true / false, or UNDEF; converted to self's
18
+ type as the indexer's store converts it (a value that does
19
+ not fit raises, as `self[i] = v` does)
18
20
 
19
21
  --------------------------------------------------------------------------- */
20
22
 
@@ -44,12 +46,19 @@
44
46
  #define OP_MIN_INT(qp, v) do { if ((v) < *(qp)) *(qp) = (v); } while (0)
45
47
  #define OP_MAX_INT(qp, v) do { if ((v) > *(qp)) *(qp) = (v); } while (0)
46
48
 
47
- /* The scalar operand as T. An Integer goes to T directly, not through
48
- double, so an int64 above 2**53 is exact. A complex self also takes a
49
- Ruby Complex scalar, whose parts sit in vd / vi; an Integer or Float
50
- scalar leaves vi at 0. */
51
- #define SCALAR_REAL(T) (v_is_float ? (T) vd : (T) vl)
52
- #define SCALAR_CMPLX(T) ((T) ((v_is_float ? (double)vd : (double)vl) + vi * I))
49
+ /* The scalar operand, already converted to self's type by rb_ca_obj2ptr
50
+ (the indexer's store conversion) and held as one cell in vcell. */
51
+ typedef union {
52
+ char bytes[16];
53
+ double d;
54
+ int64_t l;
55
+ #ifdef HAVE_COMPLEX_H
56
+ cmplx128_t z;
57
+ #endif
58
+ } ca_scatter_cell_t;
59
+
60
+ #define SCALAR_REAL(T) (*(const T *) vcell.bytes)
61
+ #define SCALAR_CMPLX(T) (*(const T *) vcell.bytes)
53
62
 
54
63
  #define LOOP_SCALAR(T, APPLY) LOOP_SCALAR_V(T, APPLY, SCALAR_REAL(T))
55
64
 
@@ -105,8 +114,8 @@
105
114
  both value and mask (indexer contract, `self[addrs] = vals`).
106
115
  - masked vals[i] flips target to masked (writes UNDEF); valid
107
116
  vals[i] writes value and clears the target's mask bit.
108
- Scalar vals is always valid (Fixnum / Float), so scalar path just
109
- writes and clears target mask. */
117
+ A scalar UNDEF masks every target (self's mask exists by then); any
118
+ other scalar writes and clears the target's mask. */
110
119
 
111
120
  #define LOOP_SCALAR_REPLACE(T) LOOP_SCALAR_REPLACE_V(T, SCALAR_REAL(T))
112
121
 
@@ -117,6 +126,10 @@
117
126
  if ( maddrs && maddrs[i] ) continue; \
118
127
  addr = p[i]; \
119
128
  CA_CHECK_INDEX(addr, elements); \
129
+ if ( vals_undef ) { \
130
+ mself[addr] = 1; \
131
+ continue; \
132
+ } \
120
133
  q[addr] = vs; \
121
134
  if ( mself ) mself[addr] = 0; \
122
135
  } \
@@ -204,11 +217,21 @@
204
217
  } \
205
218
  } while (0)
206
219
 
220
+ /* A value that is one cell for every address: a Numeric, true / false,
221
+ or UNDEF. Anything else (a CArray, an Array, ...) is coerced to an
222
+ array of values. */
223
+ static int
224
+ ca_scatter_is_scalar (VALUE v)
225
+ {
226
+ return v == CA_UNDEF || v == Qtrue || v == Qfalse
227
+ || rb_obj_is_kind_of(v, rb_cNumeric);
228
+ }
229
+
207
230
  /* Setup boilerplate: parse args and wrap addrs/vals.
208
231
 
209
232
  Outputs into caller's locals:
210
233
  ca, ci, cv (CArray*), n (ca_size_t),
211
- vals_scalar (int), v_is_float (int), vd, vi (double), vl (long).
234
+ vals_scalar, vals_undef (int), vcell (the scalar as one cell).
212
235
 
213
236
  The kernel then runs through AT_RUN, and an address out of range
214
237
  raising part way still leaves the cells already written.
@@ -218,9 +241,12 @@
218
241
  */
219
242
  /* Body shared by arithmetic and replace setups. Caller precondition:
220
243
  the data_type gate has already run (arithmetic = numeric only, replace
221
- = numeric or boolean). For the replace variant, `true` / `false`
222
- scalars are accepted as vals (bridged as vl = 1 / 0). */
223
- #define AT_SETUP_BODY(name, allow_bool_scalar) \
244
+ = numeric or boolean). A scalar is converted the way `self[i] = v`
245
+ converts it, so true / false reach only a boolean self and a value that
246
+ does not fit raises. A scalar UNDEF is a masked value: the arithmetic
247
+ family skips every pair, replace masks every target. */
248
+ #define AT_SETUP_BODY(name) \
249
+ ca_check_index_array(raddrs); \
224
250
  raddrs = rb_ca_wrap_readonly(raddrs, INT2NUM(CA_SIZE)); \
225
251
  TypedData_Get_Struct(raddrs, CArray, &carray_data_type, ci); \
226
252
  if ( ca_shares_root(ci, ca) ) { \
@@ -229,20 +255,17 @@
229
255
  } \
230
256
  n = ci->elements; \
231
257
  if ( n == 0 ) return self; \
232
- vals_scalar = (RB_FLOAT_TYPE_P(rvals) || FIXNUM_P(rvals) \
233
- || ((allow_bool_scalar) && (rvals == Qtrue || rvals == Qfalse)) \
234
- || (ca_is_complex_type(ca) && RB_TYPE_P(rvals, T_COMPLEX))); \
258
+ vals_scalar = ca_scatter_is_scalar(rvals); \
235
259
  if ( vals_scalar ) { \
236
- if ( RB_TYPE_P(rvals, T_COMPLEX) ) { \
237
- vd = NUM2DBL(rb_complex_real(rvals)); \
238
- vi = NUM2DBL(rb_complex_imag(rvals)); \
239
- v_is_float = 1; \
260
+ if ( rvals == CA_UNDEF ) { \
261
+ vals_undef = 1; \
262
+ } \
263
+ else { \
264
+ rb_ca_obj2ptr(self, rvals, vcell.bytes); \
240
265
  } \
241
- else if ( RB_FLOAT_TYPE_P(rvals) ) { vd = RFLOAT_VALUE(rvals); v_is_float = 1; } \
242
- else if ( FIXNUM_P(rvals) ) { vl = FIX2LONG(rvals); v_is_float = 0; } \
243
- else { vl = (rvals == Qtrue) ? 1 : 0; v_is_float = 0; } \
244
266
  } \
245
267
  else { \
268
+ /* coerce: a store into the receiver */ \
246
269
  rvals = rb_ca_wrap_readonly(rvals, INT2NUM(ca->data_type)); \
247
270
  TypedData_Get_Struct(rvals, CArray, &carray_data_type, cv); \
248
271
  if ( ca_shares_root(cv, ca) ) { \
@@ -259,8 +282,8 @@
259
282
  #define AT_SETUP_LOCALS \
260
283
  CArray *ca, *ci, *cv = NULL; \
261
284
  ca_size_t n; \
262
- int vals_scalar, v_is_float = 0; \
263
- double vd = 0.0, vi = 0.0; long vl = 0;
285
+ int vals_scalar, vals_undef = 0; \
286
+ ca_scatter_cell_t vcell = {{0}};
264
287
 
265
288
  #define AT_SETUP_OR_RETURN(name, ordered) \
266
289
  AT_SETUP_LOCALS \
@@ -273,7 +296,8 @@
273
296
  rb_raise(rb_eCADataTypeError, \
274
297
  name " requires a real array (complex values have no order)"); \
275
298
  } \
276
- AT_SETUP_BODY(name, 0)
299
+ AT_SETUP_BODY(name) \
300
+ if ( vals_undef ) return self;
277
301
 
278
302
  /* replace variant: accepts boolean self (assignment, no widening) and
279
303
  Ruby true / false as scalar vals. */
@@ -285,7 +309,8 @@
285
309
  rb_raise(rb_eCADataTypeError, \
286
310
  name " requires a numeric or boolean array"); \
287
311
  } \
288
- AT_SETUP_BODY(name, 1)
312
+ AT_SETUP_BODY(name) \
313
+ if ( vals_undef ) ca_create_mask(ca);
289
314
 
290
315
  /* The kernel works either on self's own memory or on a compact region:
291
316
  the distinct cells the addresses touch, read out of self and written
@@ -305,9 +330,8 @@ typedef struct {
305
330
  typedef struct {
306
331
  CArray *ca, *ci, *cv;
307
332
  ca_size_t n;
308
- int vals_scalar, v_is_float;
309
- double vd, vi;
310
- long vl;
333
+ int vals_scalar, vals_undef;
334
+ ca_scatter_cell_t vcell;
311
335
  ca_scatter_region_t *region;
312
336
  } ca_scatter_ctx_t;
313
337
 
@@ -330,10 +354,9 @@ fname (VALUE arg) \
330
354
  boolean8_t *mvals = (cv && cv->mask) ? (boolean8_t *) cv->mask->ptr : NULL; \
331
355
  boolean8_t *mself = rg_ ? rg_->mask \
332
356
  : (ca->mask ? (boolean8_t *) ca->mask->ptr : NULL); \
333
- int vals_scalar = c_->vals_scalar, v_is_float = c_->v_is_float; \
334
- double vd = c_->vd, vi = c_->vi; \
335
- long vl = c_->vl; \
336
- (void) cv; (void) mvals; (void) v_is_float; (void) vd; (void) vi; (void) vl; \
357
+ int vals_scalar = c_->vals_scalar, vals_undef = c_->vals_undef; \
358
+ ca_scatter_cell_t vcell = c_->vcell; \
359
+ (void) cv; (void) mvals; (void) vals_undef; (void) vcell; \
337
360
  DISPATCH; \
338
361
  return Qnil; \
339
362
  }
@@ -460,8 +483,8 @@ ca_scatter_region_body (VALUE varg)
460
483
  ca_scatter_ctx_t c_; \
461
484
  CArray *list_[3]; \
462
485
  c_.ca = ca; c_.ci = ci; c_.cv = cv; c_.n = n; \
463
- c_.vals_scalar = vals_scalar; c_.v_is_float = v_is_float; \
464
- c_.vd = vd; c_.vi = vi; c_.vl = vl; \
486
+ c_.vals_scalar = vals_scalar; c_.vals_undef = vals_undef; \
487
+ c_.vcell = vcell; \
465
488
  c_.region = NULL; \
466
489
  if ( ca_attach_is_alias(ca) ) { \
467
490
  list_[0] = ca; list_[1] = ci; list_[2] = cv; \
@@ -474,6 +497,9 @@ ca_scatter_region_body (VALUE varg)
474
497
  ca_attach_window(vals_scalar ? 1 : 2, list_, "rr", \
475
498
  ca_scatter_region_body, (VALUE) &ra_); \
476
499
  } \
500
+ /* the views AT_SETUP_BODY made are reached only through ci and cv */ \
501
+ RB_GC_GUARD(raddrs); \
502
+ RB_GC_GUARD(rvals); \
477
503
  } while (0)
478
504
 
479
505
  /* --------------------------------------------------------------- */
@@ -569,8 +595,7 @@ rb_ca_scatter_replace_bang (VALUE self, VALUE raddrs, VALUE rvals)
569
595
  /* Pre-scan: if vals is a masked CArray and self isn't yet masked,
570
596
  promote self so the kernel can flip target cells to masked (indexer
571
597
  `self[addrs] = vals` establishes self.mask lazily the same way). */
572
- if ( ! (RB_FLOAT_TYPE_P(rvals) || FIXNUM_P(rvals))
573
- && rb_obj_is_carray(rvals) ) {
598
+ if ( rb_obj_is_carray(rvals) ) {
574
599
  CArray *cv_pre;
575
600
  TypedData_Get_Struct(rvals, CArray, &carray_data_type, cv_pre);
576
601
  if ( ca_has_mask(cv_pre) ) {
data/ext/carray_segment.c CHANGED
@@ -228,7 +228,7 @@ rb_ca_s_segment_index (int argc, VALUE *argv, VALUE klass)
228
228
  return rout;
229
229
  }
230
230
 
231
- /* CArray.__segment_bounds__(name, lengths: l | offsets: o) -> int64[k+1]
231
+ /* CArray.__segment_boundaries_from_lengths_or_offsets__(name, lengths: l | offsets: o) -> int64[k+1]
232
232
 
233
233
  Internal: the validated boundaries, for a Ruby caller that takes the same
234
234
  two keywords (CArray#segments). `name` heads the error messages. */
@@ -239,7 +239,7 @@ rb_ca_s_segment_bounds (int argc, VALUE *argv, VALUE klass)
239
239
  ca_size_t k;
240
240
  VALUE rname;
241
241
  if ( argc < 1 ) {
242
- rb_raise(rb_eArgError, "__segment_bounds__: the caller's name is required");
242
+ rb_raise(rb_eArgError, "__segment_boundaries_from_lengths_or_offsets__: the caller's name is required");
243
243
  }
244
244
  rname = argv[0];
245
245
  StringValue(rname);
@@ -253,5 +253,5 @@ Init_carray_segment (void)
253
253
  id_offsets = rb_intern("offsets");
254
254
  rb_define_singleton_method(rb_cCArray, "segment_offsets", rb_ca_s_segment_offsets, -1);
255
255
  rb_define_singleton_method(rb_cCArray, "segment_index", rb_ca_s_segment_index, -1);
256
- rb_define_singleton_method(rb_cCArray, "__segment_bounds__", rb_ca_s_segment_bounds, -1);
256
+ rb_define_singleton_method(rb_cCArray, "__segment_boundaries_from_lengths_or_offsets__", rb_ca_s_segment_bounds, -1);
257
257
  }
data/ext/carray_slab.c CHANGED
@@ -28,8 +28,20 @@
28
28
  --------------------------------------------------------------------------- */
29
29
 
30
30
  #include "carray.h"
31
- #include "carray_internal.h" /* ca_attach_window, ca_calling_method_name, ca_axis_integer */
31
+ #include "carray_internal.h" /* ca_attach_window, ca_calling_method_name, ca_normalize_axis_arg */
32
32
  #include "carray_slab.h"
33
+ #include "ca_obj_face.h"
34
+
35
+ /* Promise 8 of the kernel iterator: a slab may be lent to Ruby as a view
36
+ onto the source's own memory only when the source is an entity and the
37
+ walk reads that memory in place (alias_ptr is src->ptr, not a scratch or
38
+ a root it gathered from). Any other slab is a copy, and a view built on
39
+ it would read memory the walk frees or reuses. */
40
+ static int
41
+ ca_slab_walk_reads_src_in_place (const ca_iter_state *it, CArray *src)
42
+ {
43
+ return ca_is_entity(src) && it->alias_ptr == src->ptr;
44
+ }
33
45
 
34
46
  /* Forward declarations: per-form loop bodies are defined later in this
35
47
  file and take a state pointer directly. */
@@ -66,21 +78,14 @@ ca_slab_parse_axes (ca_slab_iter_state_t *st, VALUE axis_arg, int8_t src_ndim)
66
78
  }
67
79
 
68
80
  n = (int) RARRAY_LEN(arr);
69
- if ( n < 1 || n > CA_RANK_MAX ) {
81
+ if ( n < 1 || n > CA_RANK_MAX ) { /* rank-check: a list of axes, no longer than the rank */
70
82
  rb_raise(rb_eArgError,
71
83
  "%s: axis count %d out of range [1..%d]",
72
84
  ca_calling_method_name(), n, CA_RANK_MAX);
73
85
  }
74
86
 
75
87
  for ( i = 0; i < n; i++ ) {
76
- int k = (int) ca_axis_integer(rb_ary_entry(arr, i), NULL);
77
- if ( k < 0 ) k += src_ndim;
78
- if ( k < 0 || k >= src_ndim ) {
79
- rb_raise(rb_eArgError,
80
- "%s: axis %d out of range for ndim %d",
81
- ca_calling_method_name(), NUM2INT(rb_ary_entry(arr, i)),
82
- src_ndim);
83
- }
88
+ int k = ca_normalize_axis_arg(rb_ary_entry(arr, i), src_ndim, NULL);
84
89
  for ( j = 0; j < i; j++ ) {
85
90
  if ( st->slab_axes[j] == (int8_t) k ) {
86
91
  rb_raise(rb_eArgError,
@@ -370,12 +375,13 @@ ca_slab_setup_input_slab_view (ca_slab_iter_state_t *st, CArray *src,
370
375
  p_in via slab_strides into the scratch, present slab_view as
371
376
  a CAStride over the carrier with contig strides.
372
377
 
373
- Path (a) needs p_in to lie in src's own buffer, which holds only
374
- when src is an entity. Any other source -- a view, whose cells the
375
- iterator hands out from the root, a parent or its own scratch --
378
+ Path (a) needs p_in to lie in src's own buffer: src is an entity
379
+ and the iterator walks that buffer (alias_ptr is src->ptr, not a
380
+ scratch it gathered into). Any other source -- a view, whose cells
381
+ the iterator hands out from the root, a parent or its own scratch --
376
382
  takes path (b): the iterator describes every slab by p_in and
377
383
  slab_strides, whatever its mode. */
378
- int contig = ca_is_entity(src)
384
+ int contig = ca_slab_walk_reads_src_in_place(st->t1, src)
379
385
  && ca_slab_strides_are_row_major_contig(slab_ndim,
380
386
  st->t1->slab_dims,
381
387
  st->t1->slab_strides,
@@ -436,9 +442,10 @@ ca_slab_setup_input_slab_view (ca_slab_iter_state_t *st, CArray *src,
436
442
  dim[0] = fiber_len;
437
443
  strides[0] = src->bytes;
438
444
  /* A window onto src only when the fiber lies in src's own buffer:
439
- an entity whose fiber is contiguous. A view's fiber comes from
440
- the root or the iterator's scratch, so it goes through a carrier. */
441
- in_alias = ca_is_entity(src) && (in_stride == src->bytes);
445
+ an entity the iterator walks in place, whose fiber is contiguous.
446
+ A view's fiber comes from the root or the iterator's scratch, so it
447
+ goes through a carrier. */
448
+ in_alias = ca_slab_walk_reads_src_in_place(st->t1, src) && (in_stride == src->bytes);
442
449
 
443
450
  if ( in_alias ) {
444
451
  /* ALIAS: parent = src. ca_stride_setup auto-creates slab_view->mask
@@ -494,8 +501,18 @@ ca_slab_advance_slab_view (ca_slab_iter_state_t *st,
494
501
  slab_cs->base_offset = (ca_size_t) (p_in - src->ptr);
495
502
  if ( slab_cs->mask != NULL ) {
496
503
  CAStride *mask_cs = (CAStride *) slab_cs->mask;
497
- mask_cs->ptr = (char *) m_in;
498
- mask_cs->base_offset = (ca_size_t) ((char *) m_in - src->mask->ptr);
504
+ if ( m_in != NULL ) {
505
+ mask_cs->ptr = (char *) m_in;
506
+ mask_cs->base_offset = (ca_size_t) ((char *) m_in - src->mask->ptr);
507
+ }
508
+ else {
509
+ /* The walk was opened on a source without a mask and the block
510
+ has given it one since: there is no mask cursor, so the slab's
511
+ mask reads the source's mask at the cell the data starts on. */
512
+ mask_cs->ptr = NULL;
513
+ mask_cs->base_offset = (ca_size_t) ((p_in - src->ptr) / src->bytes)
514
+ * src->mask->bytes;
515
+ }
499
516
  }
500
517
  } else if ( st->own_data_scratch != NULL ) {
501
518
  /* Non-contig multi-axis: K-D gather from src memory (via
@@ -515,7 +532,7 @@ ca_slab_advance_slab_view (ca_slab_iter_state_t *st,
515
532
  both slab_view and carrier at the iter's delivered addr. */
516
533
  slab_cs->ptr = p_in;
517
534
  carrier_cw->ptr = p_in;
518
- if ( slab_cs->mask != NULL ) {
535
+ if ( slab_cs->mask != NULL && m_in != NULL ) {
519
536
  CAStride *mask_cs = (CAStride *) slab_cs->mask;
520
537
  mask_cs->ptr = (char *) m_in;
521
538
  carrier_cw->mask->ptr = (char *) m_in;
@@ -626,10 +643,26 @@ ca_map_slab_cast_body (VALUE arg)
626
643
  return Qnil;
627
644
  }
628
645
 
646
+ /* What the block sees of the slab: the slab view itself, or, over a Face,
647
+ the slab view under that Face, as `a[...]` would give it. The slab
648
+ view is moved per slab in place, and the Face reads through it, so one
649
+ is built per walk. */
650
+ static VALUE
651
+ ca_slab_piece (ca_slab_iter_state_t *st)
652
+ {
653
+ CArray *src;
654
+ TypedData_Get_Struct(st->self, CArray, &carray_data_type, src);
655
+ if ( ! ca_is_face(src) ) {
656
+ return st->slab_view;
657
+ }
658
+ return ca_face_lift(st->slab_view, st->self);
659
+ }
660
+
629
661
  static VALUE
630
662
  ca_slab_run_map (ca_slab_iter_state_t *st)
631
663
  {
632
664
  CArray *src;
665
+ volatile VALUE piece;
633
666
  CArray *out;
634
667
  uint32_t flags_in;
635
668
  uint32_t flags_out;
@@ -715,6 +748,7 @@ ca_slab_run_map (ca_slab_iter_state_t *st)
715
748
  TypedData_Get_Struct(st->output_slab_view, CAWrap,
716
749
  &cawrap_data_type, out_slab_cw);
717
750
 
751
+ piece = ca_slab_piece(st);
718
752
  while ( ca_iter_state_next_slab_axes(st->t1, &p_in, &m_in) &&
719
753
  ca_iter_state_next_slab_axes(st->t1_out, &p_out, &m_out) ) {
720
754
  VALUE result;
@@ -732,7 +766,7 @@ ca_slab_run_map (ca_slab_iter_state_t *st)
732
766
  ? st->own_out_scratch : p_out;
733
767
  out_slab_cw->ptr = write_target;
734
768
 
735
- result = rb_yield(st->slab_view);
769
+ result = rb_yield(piece);
736
770
 
737
771
  /* Strict shape check. */
738
772
  if ( rb_obj_is_kind_of(result, rb_cCArray) ) {
@@ -898,10 +932,36 @@ ca_slab_reduce_setup (ca_slab_iter_state_t *st, CArray **out_src,
898
932
  /* Per-slab block form: block receives the slab CArray, returns a
899
933
  scalar; each scalar is cast into the corresponding output cell.
900
934
  (`init:` absent ⇒ this path) */
935
+ /* Write one reduced value into the reduction output. UNDEF from the
936
+ block masks the cell, as storing UNDEF into an array does; obj2ptr
937
+ would read it as 0. */
938
+ static void
939
+ ca_slab_store_reduced (ca_slab_iter_state_t *st, CArray *out,
940
+ ca_size_t out_idx, VALUE value, char *scratch)
941
+ {
942
+ char *dst = out->ptr + out_idx * st->out_bytes;
943
+ if ( value == CA_UNDEF ) {
944
+ if ( out->mask == NULL ) {
945
+ ca_create_mask(out);
946
+ }
947
+ ((boolean8_t *) out->mask->ptr)[out_idx] = 1;
948
+ if ( out->data_type == CA_OBJECT ) {
949
+ *(VALUE *) dst = INT2FIX(0);
950
+ }
951
+ else {
952
+ memset(dst, 0, st->out_bytes);
953
+ }
954
+ return;
955
+ }
956
+ rb_ca_obj2ptr(st->output, value, scratch);
957
+ memcpy(dst, scratch, st->out_bytes);
958
+ }
959
+
901
960
  static VALUE
902
961
  ca_slab_run_reduce_slab (ca_slab_iter_state_t *st)
903
962
  {
904
963
  CArray *src;
964
+ volatile VALUE piece;
905
965
  CArray *out;
906
966
  ca_size_t fiber_len;
907
967
  char *p_in;
@@ -927,13 +987,14 @@ ca_slab_run_reduce_slab (ca_slab_iter_state_t *st)
927
987
  scratch = (st->out_bytes <= (ca_size_t) sizeof(buf))
928
988
  ? buf : ALLOCV_N(char, scratch_holder, st->out_bytes);
929
989
 
990
+ piece = ca_slab_piece(st);
930
991
  while ( ca_iter_state_next_slab_axes(st->t1, &p_in, &m_in) ) {
931
992
  VALUE result;
932
993
 
933
994
  ca_slab_advance_slab_view(st, slab_cs, carrier_cw,
934
995
  src, p_in, m_in, in_alias);
935
996
 
936
- result = rb_yield(st->slab_view);
997
+ result = rb_yield(piece);
937
998
 
938
999
  /* Strict scalar contract: block must return a scalar Ruby value
939
1000
  (Numeric / Object / etc.). A CArray is a
@@ -952,8 +1013,7 @@ ca_slab_run_reduce_slab (ca_slab_iter_state_t *st)
952
1013
  res->elements == 1 ? "" : "s");
953
1014
  }
954
1015
 
955
- rb_ca_obj2ptr(st->output, result, scratch);
956
- memcpy(out->ptr + out_idx * st->out_bytes, scratch, st->out_bytes);
1016
+ ca_slab_store_reduced(st, out, out_idx, result, scratch);
957
1017
  out_idx++;
958
1018
  }
959
1019
 
@@ -968,6 +1028,7 @@ static VALUE
968
1028
  ca_slab_run_reduce_fiber (ca_slab_iter_state_t *st)
969
1029
  {
970
1030
  CArray *src;
1031
+ volatile VALUE piece;
971
1032
  CArray *out;
972
1033
  ca_size_t fiber_len;
973
1034
  char *p_in;
@@ -993,6 +1054,7 @@ ca_slab_run_reduce_fiber (ca_slab_iter_state_t *st)
993
1054
  scratch = (st->out_bytes <= (ca_size_t) sizeof(buf))
994
1055
  ? buf : ALLOCV_N(char, scratch_holder, st->out_bytes);
995
1056
 
1057
+ piece = ca_slab_piece(st);
996
1058
  while ( ca_iter_state_next_slab_axes(st->t1, &p_in, &m_in) ) {
997
1059
  VALUE acc;
998
1060
  ca_size_t i;
@@ -1010,7 +1072,7 @@ ca_slab_run_reduce_fiber (ca_slab_iter_state_t *st)
1010
1072
  ca_size_t total = st->t1->slab_elements;
1011
1073
  for ( i = 0; i < total; i++ ) {
1012
1074
  VALUE x;
1013
- x = rb_ca_fetch_index(st->slab_view, kidx);
1075
+ x = rb_ca_fetch_index(piece, kidx);
1014
1076
  acc = rb_yield_values(2, acc, x);
1015
1077
  /* Increment kidx[] row-major. */
1016
1078
  for ( k = slab_cs->ndim - 1; k >= 0; k-- ) {
@@ -1021,8 +1083,7 @@ ca_slab_run_reduce_fiber (ca_slab_iter_state_t *st)
1021
1083
  (void) fiber_len;
1022
1084
  }
1023
1085
 
1024
- rb_ca_obj2ptr(st->output, acc, scratch);
1025
- memcpy(out->ptr + out_idx * st->out_bytes, scratch, st->out_bytes);
1086
+ ca_slab_store_reduced(st, out, out_idx, acc, scratch);
1026
1087
  out_idx++;
1027
1088
  }
1028
1089
 
@@ -1049,6 +1110,7 @@ static VALUE
1049
1110
  ca_slab_run_each (ca_slab_iter_state_t *st)
1050
1111
  {
1051
1112
  CArray *src;
1113
+ volatile VALUE piece;
1052
1114
  char *p_in;
1053
1115
  boolean8_t *m_in;
1054
1116
  CAStride *slab_cs;
@@ -1082,10 +1144,11 @@ ca_slab_run_each (ca_slab_iter_state_t *st)
1082
1144
  TypedData_Get_Struct(st->carrier, CAWrap, &cawrap_data_type, carrier_cw);
1083
1145
  }
1084
1146
 
1147
+ piece = ca_slab_piece(st);
1085
1148
  while ( ca_iter_state_next_slab_axes(st->t1, &p_in, &m_in) ) {
1086
1149
  ca_slab_advance_slab_view(st, slab_cs, carrier_cw,
1087
1150
  src, p_in, m_in, in_alias);
1088
- rb_yield(st->slab_view); /* return value discarded */
1151
+ rb_yield(piece); /* return value discarded */
1089
1152
  }
1090
1153
 
1091
1154
  return st->self;
data/ext/carray_sort.c CHANGED
@@ -172,31 +172,10 @@ rb_ca_s_sort_addr_kw (int argc, VALUE *argv, VALUE ropt, VALUE self)
172
172
  if ( elements != NUM2SIZE(rb_ca_elements(argv[j])) ) {
173
173
  rb_raise(rb_eArgError, "elements mismatch");
174
174
  }
175
- /* Face gate: an ORDERABLE Face sorts by its storage, so it descends to
176
- it (attach / comparator / template output then run on plain storage,
177
- which also avoids a face-lifted NULL-ptr index output). A fixlen
178
- storage that is not ORDERABLE (CAConstString's offsets, a record's
179
- bytes) has no order in its bytes; the Face's own order stands in for
180
- it, as a dense rank, so ties stay ties for the next key. A Face that
181
- has no order (CARecord without order_by:) raises there. Any other
182
- storage raises here. */
183
175
  TypedData_Get_Struct(argv[j], CArray, &carray_data_type, ca_j);
184
176
  if ( ca_is_face(ca_j) ) {
185
- int orderable = ca_test_flag(ca_j, CA_FLAG_FACE_ORDERABLE_STORAGE);
186
- VALUE stripped = rb_ca_strip_face_value(argv[j]);
187
- CArray *sc;
188
- TypedData_Get_Struct(stripped, CArray, &carray_data_type, sc);
189
- if ( sc->data_type == CA_FIXLEN && ! orderable ) {
190
- VALUE opts = rb_hash_new();
191
- rb_hash_aset(opts, ID2SYM(rb_intern("method")), ID2SYM(rb_intern("dense")));
192
- argv[j] = rb_funcallv_kw(argv[j], rb_intern("order"), 1, &opts,
193
- RB_PASS_KEYWORDS);
194
- continue;
195
- }
196
- if ( ! orderable ) {
197
- ca_face_refuse_order("sort_addr", argv[j], "order");
198
- }
199
- argv[j] = stripped;
177
+ argv[j] = ca_order_key(argv[j], "sort_addr");
178
+ continue;
200
179
  }
201
180
  {
202
181
  CArray *k;
@@ -442,14 +421,9 @@ rb_ca_axis2addr_c (VALUE self, VALUE vindices, VALUE vaxis)
442
421
  TypedData_Get_Struct(self, CArray, &carray_data_type, ca);
443
422
  TypedData_Get_Struct(vindices, CArray, &carray_data_type, idx_ca);
444
423
 
445
- /* axis normalization + range check. */
446
- int axis_raw = NIL_P(vaxis) ? 0 : (int) ca_axis_integer(vaxis, NULL);
447
- int axis = (axis_raw < 0) ? ((int) ca->ndim + axis_raw) : axis_raw;
448
- if ( axis < 0 || axis >= ca->ndim ) {
449
- rb_raise(rb_eArgError,
450
- "%s: axis %d out of range for ndim %d",
451
- ca_calling_method_name(), axis_raw, (int) ca->ndim);
452
- }
424
+ long axis_l = ca_normalize_axis_arg(NIL_P(vaxis) ? INT2FIX(0) : vaxis,
425
+ (int) ca->ndim, NULL);
426
+ int axis = (int) axis_l;
453
427
 
454
428
  /* Shape rule: indices.ndim == self.ndim, dims match except at axis. */
455
429
  if ( idx_ca->ndim != ca->ndim ) {
@@ -470,10 +444,11 @@ rb_ca_axis2addr_c (VALUE self, VALUE vindices, VALUE vaxis)
470
444
 
471
445
  /* indices data_type: integer kind accepted; cast to CA_SIZE if needed
472
446
  (zero-copy when already CA_SIZE via to_type identity). */
447
+ ca_check_index_array(vindices);
473
448
  if ( ! ca_is_integer_type(idx_ca) ) {
474
449
  rb_raise(rb_eArgError,
475
- "%s: indices data_type must be integer kind (got %d)",
476
- ca_calling_method_name(), (int) idx_ca->data_type);
450
+ "%s: indices data_type must be integer kind (got %s)",
451
+ ca_calling_method_name(), ca_type_name[idx_ca->data_type]);
477
452
  }
478
453
  volatile VALUE vidx_cast = vindices;
479
454
  CArray *idx_cast = idx_ca;
@@ -535,6 +510,31 @@ rb_ca_axis2addr (int argc, VALUE *argv, VALUE self)
535
510
  }
536
511
 
537
512
 
513
+ /* The array the order of v is read from, for a method that orders v's
514
+ cells (a sort, a selection, a ranking, an extremum by key). A plain
515
+ array is its own key. An ORDERABLE Face orders by its storage, so the
516
+ key is the storage. A Face that is not ORDERABLE but defines its own
517
+ #order (CAConstString, whose cells are offsets; CARecord, whose bytes
518
+ are no order) is keyed by that order, as a dense rank, so ties stay ties
519
+ and a masked cell stays masked. A Face that has no order (a CARecord
520
+ without order_by:) raises in that order; any other Face raises here. */
521
+ VALUE
522
+ ca_order_key (VALUE v, const char *name)
523
+ {
524
+ CArray *ca;
525
+ TypedData_Get_Struct(v, CArray, &carray_data_type, ca);
526
+ if ( ! ca_is_face(ca) ) {
527
+ return v;
528
+ }
529
+ if ( ca_face_orders_by_itself(v) ) {
530
+ VALUE opts = rb_hash_new();
531
+ rb_hash_aset(opts, ID2SYM(rb_intern("method")), ID2SYM(rb_intern("dense")));
532
+ return rb_funcallv_kw(v, rb_intern("order"), 1, &opts, RB_PASS_KEYWORDS);
533
+ }
534
+ ca_face_require_orderable(v, name, "order");
535
+ return rb_ca_strip_face_value(v);
536
+ }
537
+
538
538
  /* ===== sort / sort_copy (value & view surface, sibling of the
539
539
  sort_addr / axis2addr block above). =============== */
540
540
 
@@ -717,8 +717,7 @@ rb_ca_sort_copy (int argc, VALUE *argv, VALUE self)
717
717
  TypedData_Get_Struct(target, CArray, &carray_data_type, cat);
718
718
 
719
719
  /* Normalize axis (negative -> +ndim, range check). */
720
- int axis = rb_ca_normalize_axis_for_ndim(ca_axis_integer(vaxis_use, NULL),
721
- cat->ndim, NULL);
720
+ int axis = ca_normalize_axis_arg(vaxis_use, cat->ndim, NULL);
722
721
 
723
722
  /* Allocate output: same shape and data_type as target
724
723
  (bytes=0 = preserve native bytes). */
@@ -805,6 +804,7 @@ rb_ca_sort_copy (int argc, VALUE *argv, VALUE self)
805
804
  ALLOCV_END(aux_holder);
806
805
 
807
806
  RB_GC_GUARD(target);
807
+ rb_ca_inherit_attr(vout, self);
808
808
  return vout;
809
809
  }
810
810