carray 3.0.5 → 3.0.6

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 (119) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +233 -0
  3. data/README.md +1 -1
  4. data/carray.gemspec +3 -2
  5. data/ext/ca_axis_dispatch.c +6 -0
  6. data/ext/ca_axis_group.c +1 -1
  7. data/ext/ca_categorical_iterator.c +7 -7
  8. data/ext/ca_kernel_iterator.c +116 -231
  9. data/ext/ca_kernel_iterator.h +410 -254
  10. data/ext/ca_obj_bincmp.c +9 -1
  11. data/ext/ca_obj_binop.c +10 -1
  12. data/ext/ca_obj_bitfield.c +2 -0
  13. data/ext/ca_obj_byte_swap.c +2 -0
  14. data/ext/ca_obj_face.c +2 -0
  15. data/ext/ca_obj_fake.c +3 -1
  16. data/ext/ca_obj_grid.c +167 -12
  17. data/ext/ca_obj_meld.c +69 -113
  18. data/ext/ca_obj_moncmp.c +9 -1
  19. data/ext/ca_obj_monop.c +40 -2
  20. data/ext/ca_obj_object.c +54 -20
  21. data/ext/ca_obj_reduce.c +15 -1
  22. data/ext/ca_obj_refer.c +65 -20
  23. data/ext/ca_obj_remap.c +69 -10
  24. data/ext/ca_obj_roll.c +1 -0
  25. data/ext/ca_obj_select.c +4 -1
  26. data/ext/ca_obj_select_axis.c +1 -0
  27. data/ext/ca_obj_shift.c +1 -0
  28. data/ext/ca_obj_stack.c +310 -111
  29. data/ext/ca_obj_stride.c +131 -45
  30. data/ext/ca_obj_string.c +18 -2
  31. data/ext/ca_obj_tile.c +1 -0
  32. data/ext/ca_obj_triop.c +9 -1
  33. data/ext/ca_obj_window.c +1 -0
  34. data/ext/ca_op_ipower.c +8 -8
  35. data/ext/caframe_csv_reader.c +1 -1
  36. data/ext/carray.h +2 -1
  37. data/ext/carray_access.c +27 -64
  38. data/ext/carray_address_basis.c +134 -0
  39. data/ext/carray_attribute.c +8 -0
  40. data/ext/carray_broadcast.c +3 -4
  41. data/ext/carray_build_flags.h +1 -1
  42. data/ext/carray_cast.c +8 -11
  43. data/ext/carray_conversion.c +9 -5
  44. data/ext/carray_core.c +811 -72
  45. data/ext/carray_element.c +13 -9
  46. data/ext/carray_histogram.c +25 -3
  47. data/ext/carray_internal.h +147 -12
  48. data/ext/carray_kernels_bincmp.c +2007 -3
  49. data/ext/carray_kernels_binop.c +25365 -5199
  50. data/ext/carray_kernels_init.c +8 -8
  51. data/ext/carray_kernels_monop.c +10696 -3678
  52. data/ext/carray_kernels_reduce_aggregate.c +4429 -702
  53. data/ext/carray_kernels_reduce_boolean.c +1533 -15
  54. data/ext/carray_kernels_reduce_cumulative.c +1808 -248
  55. data/ext/carray_kernels_reduce_extreme.c +4508 -511
  56. data/ext/carray_kernels_reduce_variance.c +2284 -152
  57. data/ext/carray_kernels_scan.c +240 -240
  58. data/ext/carray_kernels_search.c +4211 -264
  59. data/ext/carray_kernels_sort.c +367 -450
  60. data/ext/carray_lazy.c +47 -0
  61. data/ext/carray_loop.c +1 -1
  62. data/ext/carray_mask.c +130 -139
  63. data/ext/carray_median_percentile.c +4 -4
  64. data/ext/carray_memory_view.c +13 -7
  65. data/ext/carray_operator.c +147 -44
  66. data/ext/carray_order.c +3 -2
  67. data/ext/carray_random.c +4 -1
  68. data/ext/carray_slab.c +97 -21
  69. data/ext/carray_sort.c +1 -1
  70. data/ext/carray_test.c +74 -19
  71. data/ext/carray_time_text.c +2 -2
  72. data/ext/extconf.rb +11 -0
  73. data/ext/mkkernel.rb +346 -94
  74. data/ext/ruby_carray.c +4 -1
  75. data/ext/version.h +4 -4
  76. data/lib/carray/attribute.rb +25 -1
  77. data/lib/carray/autoload_carray.rb +3 -3
  78. data/lib/carray/autoload_method_extension.rb +5 -3
  79. data/lib/carray/axis_group.rb +13 -7
  80. data/lib/carray/basics.rb +1 -1
  81. data/lib/carray/block_iterator.rb +17 -3
  82. data/lib/carray/categorical.rb +6 -8
  83. data/lib/carray/categorical_iterator.rb +2 -2
  84. data/lib/carray/complex.rb +2 -2
  85. data/lib/carray/conditional.rb +3 -2
  86. data/lib/carray/frame/column_verbs.rb +45 -14
  87. data/lib/carray/frame/convert.rb +19 -0
  88. data/lib/carray/frame/csv_parser.rb +32 -32
  89. data/lib/carray/frame/describe.rb +4 -2
  90. data/lib/carray/frame/frame.rb +399 -78
  91. data/lib/carray/frame/group.rb +679 -53
  92. data/lib/carray/frame/io.rb +209 -71
  93. data/lib/carray/frame/join.rb +146 -37
  94. data/lib/carray/frame/records.rb +3 -3
  95. data/lib/carray/frame/reshape.rb +89 -11
  96. data/lib/carray/frame/sort.rb +57 -9
  97. data/lib/carray/frame/verbs.rb +384 -49
  98. data/lib/carray/frame/windows.rb +44 -6
  99. data/lib/carray/inspect.rb +7 -2
  100. data/lib/carray/iterator.rb +32 -0
  101. data/lib/carray/lazy.rb +17 -8
  102. data/lib/carray/mask_gap_fill.rb +37 -17
  103. data/lib/carray/meld_reduce.rb +1 -1
  104. data/lib/carray/methods/choose.rb +3 -2
  105. data/lib/carray/methods/meshgrid.rb +3 -1
  106. data/lib/carray/runtime.rb +47 -8
  107. data/lib/carray/segment_iterator.rb +23 -8
  108. data/lib/carray/serialize.rb +34 -17
  109. data/lib/carray/stack.rb +18 -6
  110. data/lib/carray/time.rb +5 -0
  111. data/lib/carray/window_iterator.rb +16 -4
  112. data/yard-stubs/ca_obj_array.rb +26 -1
  113. data/yard-stubs/carray_cast.rb +8 -5
  114. data/yard-stubs/carray_conversion.rb +8 -2
  115. data/yard-stubs/carray_mask.rb +20 -1
  116. data/yard-stubs/carray_math.rb +31 -16
  117. data/yard-stubs/carray_memory_view.rb +7 -0
  118. data/yard-stubs/carray_test.rb +7 -4
  119. metadata +18 -4
data/ext/carray_element.c CHANGED
@@ -82,8 +82,9 @@ elem_probe_mask (CArray *ca)
82
82
 
83
83
  /* Entity-array fast path: compute flat addr from idx[] and access ptr directly.
84
84
  Bypasses ca_func[ARRAY].xfer_index function-pointer dispatch.
85
- Caller must ensure ca_is_entity(ca) and ca->ptr is attached (= entity
86
- arrays are always attached by definition). */
85
+ Caller must ensure ca_is_entity(ca) and ca->ptr != NULL: an entity can
86
+ leave ptr NULL at rest (a CASource), and then the dispatched path is
87
+ the way in. */
87
88
  static inline ca_size_t
88
89
  elem_entity_addr (CArray *ca, ca_size_t *idx)
89
90
  {
@@ -273,7 +274,7 @@ rb_ca_elem_store (VALUE self, VALUE ridx, VALUE obj)
273
274
  Bypasses rb_ca_store_index/addr -> ca_update_mask + rb_ca_obj2ptr ->
274
275
  cast_table dispatch. Falls through to general path for: views,
275
276
  existing mask, non-numeric data type, UNDEF, or non-Numeric obj. */
276
- if ( ca_is_entity(ca) && ! ca->mask && obj != CA_UNDEF ) {
277
+ if ( ca_is_entity(ca) && ca->ptr && ! ca->mask && obj != CA_UNDEF ) {
277
278
  int8_t dt = ca->data_type;
278
279
  if ( dt == CA_FLOAT64 || dt == CA_FLOAT32 ||
279
280
  dt == CA_INT64 || dt == CA_INT32 ||
@@ -383,7 +384,7 @@ rb_ca_elem_fetch (VALUE self, VALUE ridx)
383
384
  /* Super-hot path: entity + common numeric data type + no mask.
384
385
  Bypass ca_fetch_index dispatch AND rb_ca_ptr2obj_storage cast-table dispatch.
385
386
  Covers the bench's float64 / int64 / int32 cases. */
386
- if ( ca_is_entity(ca) && ! ca->mask ) {
387
+ if ( ca_is_entity(ca) && ca->ptr && ! ca->mask ) {
387
388
  ca_size_t addr = elem_entity_addr(ca, idx);
388
389
  char *p = ca->ptr + ca->bytes * addr;
389
390
  switch ( ca->data_type ) {
@@ -406,7 +407,7 @@ rb_ca_elem_fetch (VALUE self, VALUE ridx)
406
407
  TypedData_Get_Struct). Semantics identical to rb_ca_fetch_index. */
407
408
  if ( ca->bytes <= 64 ) {
408
409
  char v[64];
409
- if ( ca_is_entity(ca) ) {
410
+ if ( ca_is_entity(ca) && ca->ptr ) {
410
411
  ca_size_t addr = elem_entity_addr(ca, idx);
411
412
  memcpy(v, ca->ptr + ca->bytes * addr, ca->bytes);
412
413
  }
@@ -439,12 +440,15 @@ rb_ca_elem_fetch (VALUE self, VALUE ridx)
439
440
  }
440
441
 
441
442
  CA_FACE_STORAGE_TO_ELEMENT_IF_FACE(out, self, ca);
443
+ if ( ca->data_type == CA_BOOLEAN ) {
444
+ out = RTEST(out) && out != INT2FIX(0) ? Qtrue : Qfalse;
445
+ }
442
446
  return out;
443
447
  }
444
448
  else {
445
449
  ca_size_t addr = elem_parse_idx_scalar(ca, ridx);
446
450
  /* Super-hot flat-addr path: entity + numeric + no mask. */
447
- if ( ca_is_entity(ca) && ! ca->mask ) {
451
+ if ( ca_is_entity(ca) && ca->ptr && ! ca->mask ) {
448
452
  char *p = ca->ptr + ca->bytes * addr;
449
453
  switch ( ca->data_type ) {
450
454
  case CA_FLOAT64: return rb_float_new(*(double *)p);
@@ -493,7 +497,7 @@ rb_ca_elem_incr (VALUE self, VALUE ridx1)
493
497
 
494
498
  /* Super-hot path: entity + no mask + integer data type.
495
499
  Reads p directly, modifies in place — no separate fetch/store dispatch. */
496
- if ( ca_is_entity(ca) && ! has_mask ) {
500
+ if ( ca_is_entity(ca) && ca->ptr && ! has_mask ) {
497
501
  ca_size_t addr;
498
502
  if ( TYPE(ridx1) == T_ARRAY ) {
499
503
  elem_parse_idx_array(ca, ridx1, idx1);
@@ -583,7 +587,7 @@ rb_ca_elem_decr (VALUE self, VALUE ridx1)
583
587
  has_mask = elem_probe_mask(ca);
584
588
 
585
589
  /* Super-hot path: mirror of elem_incr. */
586
- if ( ca_is_entity(ca) && ! has_mask ) {
590
+ if ( ca_is_entity(ca) && ca->ptr && ! has_mask ) {
587
591
  ca_size_t addr;
588
592
  if ( TYPE(ridx1) == T_ARRAY ) {
589
593
  elem_parse_idx_array(ca, ridx1, idx1);
@@ -798,7 +802,7 @@ rb_ca_elem_unmask (VALUE self, VALUE ridx)
798
802
  general path, which compares in Ruby and stores as `self[idx] = v`
799
803
  does, so a value that wins but does not fit raises. */
800
804
  #define ELEM_MINMAX_BODY(OP) do { \
801
- if ( ca_is_entity(ca) ) { \
805
+ if ( ca_is_entity(ca) && ca->ptr ) { \
802
806
  ca_size_t addr; \
803
807
  if ( TYPE(ridx) == T_ARRAY ) { \
804
808
  elem_parse_idx_array(ca, ridx, idx); \
@@ -25,6 +25,7 @@
25
25
  #include "carray.h"
26
26
  #include "carray_internal.h" /* ca_attach_window */
27
27
  #include <math.h>
28
+ #include <float.h>
28
29
 
29
30
  /* Edges are the bin boundaries and must read as such: no masked edge (its
30
31
  stored value is no boundary), no NaN, and no edge below the one before
@@ -88,6 +89,8 @@ typedef struct {
88
89
  const double *ep;
89
90
  ca_size_t n; /* number of bins */
90
91
  double lo, hi, inv_dx;
92
+ double window; /* fraction of a bin within which the
93
+ linearised floor is checked */
91
94
  int uniform;
92
95
  int include_max;
93
96
  } histbin_axis_t;
@@ -115,7 +118,7 @@ histbin_index (const histbin_axis_t *ax, double v)
115
118
  Interior values (the bulk) skip the data-dependent edge loads, keeping
116
119
  the fast path vectorisable. */
117
120
  frac = pos - (double) k;
118
- if ( frac < 1e-9 || frac > 1.0 - 1e-9 ) {
121
+ if ( frac < ax->window || frac > 1.0 - ax->window ) {
119
122
  while ( k + 1 < ax->n && v >= ax->ep[k + 1] ) k++;
120
123
  while ( k > 0 && v < ax->ep[k] ) k--;
121
124
  }
@@ -136,7 +139,7 @@ histbin_axis_setup (histbin_axis_t *ax, const double *ep, ca_size_t ne,
136
139
  int include_max)
137
140
  {
138
141
  ca_size_t kk;
139
- double dx, tol;
142
+ double dx, tol, maxdev;
140
143
 
141
144
  ax->ep = ep;
142
145
  ax->n = ne - 1;
@@ -149,8 +152,27 @@ histbin_axis_setup (histbin_axis_t *ax, const double *ep, ca_size_t ne,
149
152
  width infinite, and a subnormal width makes the reciprocal infinite.
150
153
  Either turns the position into NaN, which has no integer. */
151
154
  ax->uniform = isfinite(dx) && dx != 0.0 && isfinite(1.0 / dx);
155
+ maxdev = 0.0;
152
156
  for ( kk = 0; ax->uniform && kk <= ax->n; kk++ ) {
153
- if ( fabs(ax->ep[kk] - (ax->lo + (double) kk * dx)) > tol ) { ax->uniform = 0; break; }
157
+ double dev = fabs(ax->ep[kk] - (ax->lo + (double) kk * dx));
158
+ if ( dev > tol ) { ax->uniform = 0; break; }
159
+ if ( dev > maxdev ) maxdev = dev;
160
+ }
161
+ /* A value can land on the wrong side of the linearised floor by as much
162
+ as an edge strays from the straight line, plus the rounding of the
163
+ position itself: v - lo is rounded at the scale of the edges' magnitude
164
+ (an ulp of lo or hi, which can be most of a bin when the edges are
165
+ close together far from zero), and the product grows with the number
166
+ of bins. Values within that much of a boundary are checked against the
167
+ stored edges. When the window covers a good part of a bin, the binary
168
+ search is cheaper. */
169
+ ax->window = ( ax->uniform )
170
+ ? maxdev / fabs(dx)
171
+ + 4.0 * DBL_EPSILON * (fabs(ax->lo) + fabs(ax->hi)) / fabs(dx)
172
+ + (double) (ax->n + 2) * 8.0 * DBL_EPSILON + 1e-9
173
+ : 0.0;
174
+ if ( ax->window > 0.25 ) {
175
+ ax->uniform = 0;
154
176
  }
155
177
  ax->inv_dx = (dx != 0.0) ? 1.0 / dx : 0.0;
156
178
  ax->include_max = include_max;
@@ -35,6 +35,18 @@
35
35
  CArray#initialize_dup / #initialize_clone. */
36
36
  void ca_keep_chain_flags (CArray *view, CArray *src);
37
37
 
38
+ /* A reshape / flatten folded onto the root of its source (CA_FLAG_FOLDED,
39
+ ca_obj_refer.c). The source is not on the view's C parent chain, so a
40
+ read-only flag it takes after the view is made would not be seen by
41
+ ca_is_readonly. The view records its source here; ca_is_readonly asks the
42
+ source of a view that carries the flag. The source stays alive while the
43
+ view does (the view's @parent). ca_fold_source answers NULL for a view
44
+ with no record; ca_fold_forget drops the record when the view is freed. */
45
+ #define CA_FLAG_FOLDED 8192
46
+ void ca_fold_note_source (CArray *view, CArray *src);
47
+ CArray *ca_fold_source (CArray *view);
48
+ void ca_fold_forget (CArray *view);
49
+
38
50
  /* ---- bulk bit pack / unpack (ca_obj_bitarray.c) --------------------------
39
51
 
40
52
  Drive the CABitarray whole-view xfer_all / xfer_addrs fast paths, which
@@ -188,9 +200,24 @@ ca_mask_from_bytes (CArray *ca, const boolean8_t *m)
188
200
 
189
201
  void ca_attach_all (CArray **list, int32_t n);
190
202
  void ca_sync_all (CArray **list, int32_t n);
203
+ void ca_sync_carry_on (CArray *ca, volatile VALUE *error);
191
204
  VALUE ca_attach_window (int32_t n, CArray **list, const char *modes,
192
205
  VALUE (*body)(VALUE), VALUE arg);
193
206
 
207
+ /* A window that yields to a block can be left open for good: Ruby does not
208
+ run the ensure of a suspended Fiber that is collected (an Enumerator
209
+ stopped with #next and dropped). ca_window_guard_arm makes a guard that
210
+ runs cleanup(arg) from a finalizer when that happens; the window disarms
211
+ it when it closes normally. arg is heap memory the guard frees, and
212
+ mark marks the VALUEs it holds. ca_attach_window_yielding is
213
+ ca_attach_window with such a guard; owners keeps the arrays alive. */
214
+ VALUE ca_window_guard_arm (void (*cleanup)(void *), void (*mark)(void *),
215
+ void *arg, VALUE *sentinel);
216
+ void ca_window_guard_disarm (VALUE guard, VALUE sentinel);
217
+ VALUE ca_attach_window_yielding (VALUE owners, int32_t n, CArray **list,
218
+ const char *modes, VALUE (*body)(VALUE),
219
+ VALUE arg);
220
+
194
221
  /* ---- Filling a result Ruby does not own yet (carray_copy.c) ---------------
195
222
 
196
223
  A C builder that allocates its result with carray_new / ca_template and
@@ -272,6 +299,8 @@ int ca_shares_root (void *ap, void *aq);
272
299
  view's shape (carray_mask.c). Installed directly in operation tables. */
273
300
 
274
301
  void ca_view_func_create_mask (void *ap);
302
+ void ca_view_sync_mask_after_parent (void *ap);
303
+ void ca_view_pass_mask_to_parent (void *ap);
275
304
 
276
305
  /* Running count of the present cells of any data type along axis (Qnil:
277
306
  the flattened array), read off the mask alone (carray_mask.c). The
@@ -307,6 +336,21 @@ int ca_lazy_req_is_packed (const ca_lazy_req_t *req, void *view);
307
336
  void ca_lazy_req_pull (void *operand, const ca_lazy_req_t *req,
308
337
  void *buf, int dir);
309
338
  boolean8_t *ca_lazy_req_mask (const ca_lazy_req_t *req, int n, CArray **ops);
339
+
340
+ /* Runs eval(node, req, data, dir), the evaluation of a lazy node over the
341
+ cells req names. When the node or one of its operands ops[0..n) is an
342
+ object array, the evaluation calls Ruby on each cell, and a cell holding
343
+ an expression over the node comes back into it: the node, and the nodes
344
+ of its chain in chain[0..nchain), are then recorded as computed over
345
+ (ca_computing_run_nodes), so coming back is refused as cyclic. The
346
+ operands are not recorded: an expression over the same operand that does
347
+ not need the node is no cycle. Otherwise eval runs as it is. */
348
+ typedef void (*ca_lazy_eval_fn) (void *node, const ca_lazy_req_t *req,
349
+ void *data, int dir);
350
+ void ca_lazy_eval_recorded (void *node, int n, CArray **ops,
351
+ int nchain, CArray **chain,
352
+ ca_lazy_eval_fn eval, const ca_lazy_req_t *req,
353
+ void *data, int dir);
310
354
  VALUE ca_lazy_settle_scalar (VALUE operand, int was_scalar);
311
355
 
312
356
  /* Non-zero iff the array a view's cells come from -- the parent, or the
@@ -425,6 +469,27 @@ void ca_check_rank_fits (long ndim, const char *name);
425
469
  #define CA_FLAG_LENT 2048
426
470
  #define CA_FLAG_LENT_VIEWED 4096
427
471
 
472
+ /* A computation over object arrays. A cell whose value is the array
473
+ itself or a view of it makes the kernel call back into a computation
474
+ over the same cells; entering an array that the same method is already
475
+ computing over, in this thread and fiber, is refused as cyclic, as
476
+ reading such a cell is (ca_test_cyclic_check). ca_computing_run runs
477
+ body(arg) with the object arrays among ops[0..n) recorded
478
+ (rb_exec_recursive, carray_core.c); nothing is written to the arrays. */
479
+ VALUE ca_computing_run (int n, CArray **ops, VALUE (*body)(VALUE), VALUE arg);
480
+ VALUE ca_computing_run_nodes (int n, CArray **ops, VALUE (*body)(VALUE), VALUE arg);
481
+ /* Refuses as cyclic a cell among cells[0..n) that is an array reaching a
482
+ lazy node being evaluated: computing it would come back into the node. */
483
+ void ca_refuse_cells_reaching_evaluation (ca_size_t n, const VALUE *cells);
484
+
485
+ /* Registers a view type whose transfers only move cells between its parents
486
+ and the caller (an index view: CAGrid, CASelect, CAWindow, CAStack, ...),
487
+ calling no Ruby. A transfer through such views down to entities cannot
488
+ come back into an array, and is not recorded (ca_with_cyclic_check). */
489
+ void ca_register_cell_moving_view (int8_t obj_type);
490
+ VALUE ca_iter_ensure_computing (CArray *ca, int32_t n, ca_iter_state *states,
491
+ VALUE (*body)(VALUE), VALUE arg);
492
+
428
493
  #define CA_CHECK_DATA_TYPE_GIVEN(data_type) \
429
494
  do { \
430
495
  int ca_dt_ = (data_type); \
@@ -696,19 +761,41 @@ CArray *ca_multi_parent_mask (CArray *p, CArray **standin);
696
761
  void ca_multi_parent_mask_refresh (CArray *mask, CArray **parents,
697
762
  CArray **standins, int32_t n);
698
763
 
699
- /* Before a write to a multi-parent view's mask: when a parent that can
700
- never carry a mask (a value array, a mask array) is among the parents,
701
- run the write `body(arg)` once against blank all-false masks in place of
702
- every parent's mask and refuse it with that TypeError if it put a true
703
- into such a parent's part. A refused write then changes no parent.
704
- Does nothing when no such parent is there. */
705
- void ca_multi_parent_mask_refuse_first (CArray *mask, CArray **parents,
706
- CArray **standins, int32_t n,
707
- VALUE (*body)(VALUE),
708
- VALUE arg);
764
+ void ca_multi_parent_check_part (CArray *part, const char *name);
765
+
766
+ /* A write to a stack or meld goes to its parts one after another, and a
767
+ part that converts the value on its way (a writable cast) can refuse it
768
+ after the parts before it were written. ca_multi_parent_value_trial
769
+ gives a stack or meld laid out as `ca` over scratch arrays standing for
770
+ its parts (held by `keep`), to run the write against first, when
771
+ ca_multi_parent_value_needs_trial says such a part is there. */
772
+ int ca_multi_parent_value_needs_trial (CArray *ca);
773
+ CArray *ca_multi_parent_value_trial (CArray *ca, VALUE keep);
774
+
775
+ /* A write through the view `ca` that reaches a stack or meld needing the
776
+ trial, in pieces: ca_multi_parent_value_trial_below gives the stack or
777
+ meld, and ca_multi_parent_value_trial_through runs the write against its
778
+ stand-ins first. */
779
+ CArray *ca_multi_parent_value_trial_below (CArray *ca);
780
+ void ca_multi_parent_value_trial_through (CArray *m, VALUE (*body)(VALUE),
781
+ VALUE arg);
782
+
783
+ #define CA_MULTI_PARENT_VALUE_TRIAL(ap, t, call) \
784
+ do { \
785
+ if ( ca_multi_parent_value_needs_trial((CArray *) (ap)) ) { \
786
+ volatile VALUE keep_ = rb_ary_new(); \
787
+ CArray *t = ca_multi_parent_value_trial((CArray *) (ap), keep_); \
788
+ call; \
789
+ RB_GC_GUARD(keep_); \
790
+ } \
791
+ } while (0)
792
+ int ca_monop_writable_cast_target (CArray *ca, int8_t *target);
793
+
794
+ void ca_multi_parent_mask_adopt (CArray *mask, CArray **parents,
795
+ CArray **standins, int32_t n);
709
796
 
710
797
  /* ca_mask_standin_settle for each part of a multi-parent view's mask
711
- still reading its stand-in, the parts that can have no mask first. */
798
+ still reading its stand-in. */
712
799
  void ca_multi_parent_mask_settle (CArray *mask, CArray **parents,
713
800
  CArray **standins, int32_t n);
714
801
 
@@ -774,7 +861,7 @@ ca_obj_equal (VALUE a, VALUE b)
774
861
  /* ---- fast paths (carray_core.c) --------------------------------------------
775
862
 
776
863
  A fast path answers what a general path also answers, by a different
777
- algorithm. Each one tests ca_fast_paths_enabled() alongside its own
864
+ algorithm. Each one tests ca_fast_path(CA_SC_name) alongside its own
778
865
  condition, so a development build run with CARRAY_GENERAL_PATH=1 in the
779
866
  environment takes the general path everywhere and the test suite
780
867
  compares the two. A release build has no switch: the test is the
@@ -794,13 +881,61 @@ ca_obj_equal (VALUE a, VALUE b)
794
881
 
795
882
  ---------------------------------------------------------------------------- */
796
883
 
884
+ /* The number of obj_types carray installs for itself (carray_core.c). */
885
+ extern int ca_obj_num_builtin;
886
+
887
+ /* Each fast path has a name, so that a development build can switch it off
888
+ alone: CARRAY_SHORTCUTS_OFF=stack,stack_outer_k in the environment turns
889
+ off the ones named, CARRAY_GENERAL_PATH=1 all of them. A fast path tests
890
+ ca_fast_path(CA_SC_name) alongside its own condition. The names are in
891
+ ca_shortcut_names (carray_core.c) in this order; the Ruby ones are asked
892
+ through CArray.__fast_paths_enabled__(:name). */
893
+ enum ca_shortcut {
894
+ CA_SC_SHIFT_WHOLE_VIEW,
895
+ CA_SC_STACK,
896
+ CA_SC_STACK_OUTER_K,
897
+ CA_SC_FIBER_FUSED_CONVERSION,
898
+ CA_SC_PER_SLAB_HOIST,
899
+ CA_SC_PROTECT_ONLY_MAY_RAISE,
900
+ CA_SC_STACK_ADDRS_K0,
901
+ CA_SC_STACK_STRIDE_BOX,
902
+ CA_SC_STACK_STRIDE_RELOC,
903
+ CA_SC_STACK_PARENTS_K0,
904
+ CA_SC_STACK_FOLD,
905
+ CA_SC_MELD_STRIDE_BOX,
906
+ CA_SC_MELD_PARENTS_AXIS0,
907
+ CA_SC_MEDIAN_PARTITION,
908
+ CA_SC_PERCENTILE_PARTITION,
909
+ CA_SC_LOOP_INTERCHANGE,
910
+ CA_SC_STACK_LOOP_INTERCHANGE,
911
+ CA_SC_LAZY_STREAMING_REDUCE,
912
+ CA_SC_SHORT_FIBER_REDUCE,
913
+ /* asked from lib/carray */
914
+ CA_SC_MELD_REDUCE,
915
+ CA_SC_SEGMENT_NUNIQUE,
916
+ CA_SC_SEGMENT_SCAN_KERNEL,
917
+ CA_SC_SEGMENT_EDGE_KERNEL,
918
+ CA_SC_SEGMENT_REDUCE_KERNEL,
919
+ CA_SC_SEGMENT_ALL_ANY_KERNEL,
920
+ CA_SC_CATEGORICAL_GROUP_BY_INDEX,
921
+ CA_SC_AXIS_GROUP_COUNT_KERNEL,
922
+ CA_SC_AXIS_GROUP_WHOLE_BLOCK,
923
+ CA_SC_AXIS_GROUP_REDUCE_KERNEL,
924
+ CA_SC_AXIS_GROUP_SCAN_KERNEL,
925
+ CA_SC_WINDOW_FOLD_BY_OFFSET,
926
+ CA_SC_COUNT
927
+ };
928
+
797
929
  #ifdef CARRAY_DEV_BUILD
798
930
  extern int ca_fast_paths_on;
799
931
  extern int ca_iter_transfer_only_on;
932
+ extern unsigned char ca_shortcut_off[CA_SC_COUNT];
800
933
  #define ca_fast_paths_enabled() (ca_fast_paths_on)
934
+ #define ca_fast_path(id) (ca_fast_paths_on && ! ca_shortcut_off[id])
801
935
  #define ca_iter_by_transfer_only() (ca_iter_transfer_only_on)
802
936
  #else
803
937
  #define ca_fast_paths_enabled() (1)
938
+ #define ca_fast_path(id) (1)
804
939
  #define ca_iter_by_transfer_only() (0)
805
940
  #endif
806
941