carray 3.0.0 → 3.0.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (202) hide show
  1. checksums.yaml +4 -4
  2. data/.yardopts +2 -1
  3. data/CHANGELOG.md +845 -3
  4. data/{NEWS.md → CHANGELOG.v1.md} +1 -1
  5. data/README.md +33 -33
  6. data/carray.gemspec +12 -8
  7. data/ext/ca_axis_dispatch.c +33 -4
  8. data/ext/ca_axis_group.c +256 -114
  9. data/ext/ca_binop_dispatch.c +5 -6
  10. data/ext/ca_binop_dispatch.h +0 -7
  11. data/ext/ca_categorical_iterator.c +121 -67
  12. data/ext/ca_for_each_element.h +6 -8
  13. data/ext/ca_group_iter.c +1 -1
  14. data/ext/ca_kernel_iterator.c +386 -74
  15. data/ext/ca_kernel_iterator.h +145 -45
  16. data/ext/ca_obj_array.c +62 -20
  17. data/ext/ca_obj_bincmp.c +57 -27
  18. data/ext/ca_obj_binop.c +86 -30
  19. data/ext/ca_obj_block.c +4 -4
  20. data/ext/ca_obj_const_string.c +88 -27
  21. data/ext/ca_obj_face.c +104 -0
  22. data/ext/ca_obj_face.h +48 -1
  23. data/ext/ca_obj_fake.c +11 -0
  24. data/ext/ca_obj_fixlen_string.c +21 -6
  25. data/ext/ca_obj_grid.c +11 -3
  26. data/ext/ca_obj_meld.c +125 -22
  27. data/ext/ca_obj_moncmp.c +33 -14
  28. data/ext/ca_obj_monop.c +19 -7
  29. data/ext/ca_obj_object.c +107 -4
  30. data/ext/ca_obj_record.c +3 -1
  31. data/ext/ca_obj_refer.c +30 -6
  32. data/ext/ca_obj_roll.c +13 -5
  33. data/ext/ca_obj_select.c +49 -34
  34. data/ext/ca_obj_select_axis.c +16 -6
  35. data/ext/ca_obj_shift.c +3 -3
  36. data/ext/ca_obj_stack.c +3 -8
  37. data/ext/ca_obj_stride.c +178 -24
  38. data/ext/ca_obj_string.c +11 -5
  39. data/ext/ca_obj_tile.c +13 -5
  40. data/ext/ca_obj_time.c +3 -1
  41. data/ext/ca_obj_timedelta.c +3 -1
  42. data/ext/ca_obj_transpose.c +1 -1
  43. data/ext/ca_obj_triop.c +62 -23
  44. data/ext/ca_obj_window.c +94 -19
  45. data/ext/ca_op_cmplx64.h +123 -0
  46. data/ext/ca_op_ipower.c +1 -5
  47. data/ext/ca_rng_normal.h +42 -0
  48. data/ext/ca_rng_xoshiro256pp.h +105 -0
  49. data/ext/ca_sort_kernels.h +5 -5
  50. data/ext/ca_sweep_engine.c +362 -155
  51. data/ext/ca_sweep_engine.h +39 -9
  52. data/ext/ca_transform_common.c +7 -0
  53. data/ext/carray.h +148 -132
  54. data/ext/carray_access.c +83 -49
  55. data/ext/carray_address_basis.c +590 -0
  56. data/ext/carray_bincount.c +8 -8
  57. data/ext/carray_broadcast.c +102 -9
  58. data/ext/carray_build_flags.h +3 -0
  59. data/ext/carray_call_cfunc.c +2010 -335
  60. data/ext/carray_call_cfunc.h +153 -0
  61. data/ext/carray_cast.c +184 -72
  62. data/ext/carray_conversion.c +22 -22
  63. data/ext/carray_copy.c +67 -32
  64. data/ext/carray_core.c +140 -13
  65. data/ext/carray_count.c +10 -11
  66. data/ext/carray_element.c +7 -7
  67. data/ext/carray_factorize.c +72 -51
  68. data/ext/carray_hold.c +1 -1
  69. data/ext/carray_index_classifier.c +7 -21
  70. data/ext/carray_internal.h +36 -3
  71. data/ext/carray_kernels_bincmp.c +1 -0
  72. data/ext/carray_kernels_binop.c +320 -298
  73. data/ext/carray_kernels_init.c +1095 -0
  74. data/ext/carray_kernels_map.c +1 -0
  75. data/ext/carray_kernels_moncmp.c +1 -0
  76. data/ext/carray_kernels_monop.c +412 -411
  77. data/ext/carray_kernels_reduce_aggregate.c +256 -87
  78. data/ext/carray_kernels_reduce_boolean.c +1 -0
  79. data/ext/carray_kernels_reduce_cumulative.c +324 -54
  80. data/ext/carray_kernels_reduce_extreme.c +605 -58
  81. data/ext/carray_kernels_reduce_variance.c +1 -0
  82. data/ext/carray_kernels_scan.c +5 -4
  83. data/ext/carray_kernels_search.c +187 -106
  84. data/ext/carray_kernels_sort.c +1 -0
  85. data/ext/carray_kernels_triop.c +1 -0
  86. data/ext/carray_lazy.c +175 -5
  87. data/ext/carray_loop.c +7 -1
  88. data/ext/carray_mask.c +29 -13
  89. data/ext/carray_median_percentile.c +56 -1
  90. data/ext/carray_memory_view.c +46 -21
  91. data/ext/carray_operator.c +50 -53
  92. data/ext/carray_order.c +1 -1
  93. data/ext/carray_partition.c +4 -4
  94. data/ext/carray_random.c +391 -47
  95. data/ext/carray_scatter.c +1 -1
  96. data/ext/carray_slab.c +13 -0
  97. data/ext/carray_sort.c +22 -24
  98. data/ext/carray_sort_kernel.c +10 -10
  99. data/ext/carray_test.c +1 -1
  100. data/ext/extconf.rb +18 -0
  101. data/ext/mk_call_cfunc.rb +323 -145
  102. data/ext/mkkernel.rb +651 -129
  103. data/ext/ruby_carray.c +28 -17
  104. data/ext/version.h +4 -4
  105. data/lib/carray/autoload_carray.rb +24 -3
  106. data/lib/carray/autoload_method_extension.rb +14 -1
  107. data/lib/carray/axis_group.rb +84 -7
  108. data/lib/carray/basics.rb +63 -59
  109. data/lib/carray/bincount_nd.rb +12 -12
  110. data/lib/carray/block_iterator.rb +111 -25
  111. data/lib/carray/categorical.rb +154 -37
  112. data/lib/carray/categorical_iterator.rb +300 -151
  113. data/lib/carray/conditional.rb +14 -14
  114. data/lib/carray/const_string.rb +131 -27
  115. data/lib/carray/construct.rb +61 -0
  116. data/lib/carray/core_extensions.rb +62 -5
  117. data/lib/carray/data_type_extension.rb +21 -1
  118. data/lib/carray/data_type_limits.rb +91 -0
  119. data/lib/carray/fixlen_string.rb +1 -1
  120. data/lib/carray/frame/concat.rb +3 -3
  121. data/lib/carray/frame/convert.rb +1 -1
  122. data/lib/carray/frame/csv_parser.rb +11 -4
  123. data/lib/carray/frame/frame.rb +85 -21
  124. data/lib/carray/frame/group.rb +36 -3
  125. data/lib/carray/frame/io.rb +224 -15
  126. data/lib/carray/frame/records.rb +18 -4
  127. data/lib/carray/frame/sort.rb +1 -1
  128. data/lib/carray/frame/verbs.rb +32 -12
  129. data/lib/carray/fuse_source.rb +123 -0
  130. data/lib/carray/fusion.rb +218 -0
  131. data/lib/carray/histogram.rb +16 -16
  132. data/lib/carray/inspect.rb +43 -16
  133. data/lib/carray/iterator.rb +147 -3
  134. data/lib/carray/lazy.rb +88 -73
  135. data/lib/carray/mask_gap_fill.rb +3 -1
  136. data/lib/carray/meld_reduce.rb +2 -2
  137. data/lib/carray/methods/align_addr.rb +1 -1
  138. data/lib/carray/methods/composition.rb +1 -1
  139. data/lib/carray/methods/discovery_along.rb +74 -0
  140. data/lib/carray/methods/factorize.rb +50 -0
  141. data/lib/carray/methods/is_in.rb +25 -14
  142. data/lib/carray/methods/locate_addr.rb +81 -3
  143. data/lib/carray/methods/mask_duplicates.rb +36 -2
  144. data/lib/carray/methods/meshgrid.rb +4 -5
  145. data/lib/carray/methods/mode.rb +2 -2
  146. data/lib/carray/methods/nunique.rb +23 -2
  147. data/lib/carray/methods/repeat.rb +110 -0
  148. data/lib/carray/methods/snap.rb +7 -2
  149. data/lib/carray/methods/unique.rb +44 -4
  150. data/lib/carray/methods/value_counts.rb +2 -2
  151. data/lib/carray/rng.rb +86 -0
  152. data/lib/carray/runtime.rb +0 -19
  153. data/lib/carray/slab_iterator.rb +77 -19
  154. data/lib/carray/string_operation_extension.rb +10 -6
  155. data/lib/carray/time.rb +906 -491
  156. data/lib/carray/window_iterator.rb +421 -27
  157. data/lib/carray.rb +7 -5
  158. data/yard-stubs/ca_obj_array.rb +385 -0
  159. data/yard-stubs/ca_obj_bitarray.rb +38 -0
  160. data/yard-stubs/ca_obj_bitfield.rb +43 -0
  161. data/yard-stubs/ca_obj_block.rb +68 -0
  162. data/yard-stubs/ca_obj_byte_swap.rb +56 -0
  163. data/yard-stubs/ca_obj_fake.rb +31 -0
  164. data/yard-stubs/ca_obj_farray.rb +32 -0
  165. data/yard-stubs/ca_obj_field.rb +45 -0
  166. data/yard-stubs/ca_obj_grid.rb +35 -0
  167. data/yard-stubs/ca_obj_refer.rb +72 -0
  168. data/yard-stubs/ca_obj_roll.rb +45 -0
  169. data/yard-stubs/ca_obj_shift.rb +43 -0
  170. data/yard-stubs/ca_obj_stride.rb +181 -0
  171. data/yard-stubs/ca_obj_tile.rb +29 -0
  172. data/yard-stubs/ca_obj_transpose.rb +40 -0
  173. data/yard-stubs/ca_obj_window.rb +57 -0
  174. data/yard-stubs/carray_access.rb +131 -0
  175. data/yard-stubs/carray_attribute.rb +246 -0
  176. data/yard-stubs/carray_broadcast.rb +37 -0
  177. data/yard-stubs/carray_cast.rb +489 -0
  178. data/yard-stubs/carray_class.rb +65 -0
  179. data/yard-stubs/carray_conversion.rb +76 -0
  180. data/yard-stubs/carray_copy.rb +79 -0
  181. data/yard-stubs/carray_core.rb +34 -0
  182. data/yard-stubs/carray_count.rb +84 -0
  183. data/yard-stubs/carray_element.rb +108 -0
  184. data/yard-stubs/carray_generate.rb +66 -0
  185. data/yard-stubs/carray_lazy.rb +228 -0
  186. data/yard-stubs/carray_loop.rb +140 -0
  187. data/yard-stubs/carray_mask.rb +259 -0
  188. data/yard-stubs/carray_math.rb +1615 -0
  189. data/yard-stubs/carray_mathfunc.rb +45 -0
  190. data/yard-stubs/carray_median_percentile.rb +103 -0
  191. data/yard-stubs/carray_memory_view.rb +163 -0
  192. data/yard-stubs/carray_order.rb +252 -0
  193. data/yard-stubs/carray_random.rb +89 -0
  194. data/yard-stubs/carray_scatter.rb +106 -0
  195. data/yard-stubs/carray_slab.rb +59 -0
  196. data/yard-stubs/carray_sort.rb +165 -0
  197. data/yard-stubs/carray_test.rb +85 -0
  198. data/yard-stubs/carray_undef.rb +64 -0
  199. data/yard-stubs/carray_utils.rb +97 -0
  200. data/yard-stubs/ruby_carray.rb +193 -0
  201. metadata +67 -10
  202. data/ext/ca_obj_unbound_repeat.c +0 -496
data/ext/ca_obj_refer.c CHANGED
@@ -454,7 +454,7 @@ rb_ca_refer (int argc, VALUE *argv, VALUE self)
454
454
  return obj;
455
455
  }
456
456
  }
457
- CA_FACE_LIFT_IF_FACE(obj, self, ca);
457
+ CA_WRAPPER_LIFT(obj, self, ca);
458
458
  return obj;
459
459
  }
460
460
 
@@ -482,7 +482,31 @@ rb_ca_refer_new (VALUE self,
482
482
  rb_set_options(ropt, "bytes,offset", SIZE2NUM(bytes), SIZE2NUM(offset));
483
483
  rb_ary_store(list, 2, ropt);
484
484
 
485
- return rb_ca_refer(3, (VALUE *) RARRAY_CONST_PTR(list), self);
485
+ {
486
+ volatile VALUE obj = rb_ca_refer(3, (VALUE *) RARRAY_CONST_PTR(list), self);
487
+ CArray *co;
488
+
489
+ /* CAREFUL: this is the internal builder -- some fifteen call sites want
490
+ the refer itself, not a wrapper on top of it. The public `refer` it
491
+ delegates to lifts a CALazyMarker, so strip that here.
492
+
493
+ Two things go wrong otherwise. rb_ca_value_array strips the mask off
494
+ what it gets back and marks the level it is handed; with a marker in
495
+ the way the refer underneath keeps its mask and never gets
496
+ CA_FLAG_VALUE_ARRAY, so the values read back as UNDEF. And builders
497
+ that stack further views on the result -- fancy indexing goes refer,
498
+ grid, refer -- end up with a marker buried in the middle of the
499
+ chain, which is the redundant-middle-wrapper that CAFace.md section
500
+ 8.3 exists to prevent.
501
+
502
+ Faces stay lifted: rb_ca_value_array depends on that and says so
503
+ where it marks the storage level. */
504
+ TypedData_Get_Struct(obj, CArray, &carray_data_type, co);
505
+ if ( ca_is_lazy_marker(co) ) {
506
+ obj = rb_ca_parent(obj);
507
+ }
508
+ return obj;
509
+ }
486
510
  }
487
511
 
488
512
  /* CArray#reshape(*newdim) — returns a view of self with the new
@@ -605,7 +629,7 @@ rb_ca_reshape (int argc, VALUE *argv, VALUE self)
605
629
  out_strides, out_base);
606
630
  obj = ca_wrap_struct(cs);
607
631
  rb_ca_set_parent(obj, self);
608
- CA_FACE_LIFT_IF_FACE(obj, self, ca);
632
+ CA_WRAPPER_LIFT(obj, self, ca);
609
633
  return obj;
610
634
  }
611
635
  }
@@ -614,7 +638,7 @@ rb_ca_reshape (int argc, VALUE *argv, VALUE self)
614
638
  new_dim, ca->bytes, 0);
615
639
  obj = ca_wrap_struct(cr);
616
640
  rb_ca_set_parent(obj, self);
617
- CA_FACE_LIFT_IF_FACE(obj, self, ca);
641
+ CA_WRAPPER_LIFT(obj, self, ca);
618
642
  return obj;
619
643
  }
620
644
 
@@ -642,7 +666,7 @@ rb_ca_flatten (VALUE self)
642
666
  1, dim, out_strides, out_base);
643
667
  obj = ca_wrap_struct(cs);
644
668
  rb_ca_set_parent(obj, self);
645
- CA_FACE_LIFT_IF_FACE(obj, self, ca);
669
+ CA_WRAPPER_LIFT(obj, self, ca);
646
670
  return obj;
647
671
  }
648
672
  }
@@ -650,7 +674,7 @@ rb_ca_flatten (VALUE self)
650
674
  cr = ca_refer_new(ca, ca->data_type, 1, dim, ca->bytes, 0);
651
675
  obj = ca_wrap_struct(cr);
652
676
  rb_ca_set_parent(obj, self);
653
- CA_FACE_LIFT_IF_FACE(obj, self, ca);
677
+ CA_WRAPPER_LIFT(obj, self, ca);
654
678
  return obj;
655
679
  }
656
680
 
data/ext/ca_obj_roll.c CHANGED
@@ -232,10 +232,18 @@ ca_roll_func_xfer_stride (void *ap, ca_size_t *starts, ca_size_t *counts,
232
232
  s = ca->bytes;
233
233
  for (k = ndim - 1; k >= 0; k--) { dstride[k] = s; s *= counts[k]; }
234
234
 
235
- for (k = 0; k < ndim; k++) {
236
- if (strides[k] % rnative[k] != 0 || strides[k] / rnative[k] != 1) {
237
- structural = 0;
238
- break;
235
+ /* The request is over the view's addresses, so a transposed / flat request
236
+ is legal and must not be composed axis-by-axis; see
237
+ ca_xfer_stride_request_is_axis_box (carray.h). */
238
+ if ( ! ca_xfer_stride_request_is_axis_box(ca, starts, counts, strides) ) {
239
+ structural = 0;
240
+ }
241
+ else {
242
+ for (k = 0; k < ndim; k++) {
243
+ if (strides[k] % rnative[k] != 0 || strides[k] / rnative[k] != 1) {
244
+ structural = 0;
245
+ break;
246
+ }
239
247
  }
240
248
  }
241
249
 
@@ -529,7 +537,7 @@ rb_ca_roll (int argc, VALUE *argv, VALUE self)
529
537
  }
530
538
  {
531
539
  VALUE obj = rb_ca_roll_new(self, shift);
532
- CA_FACE_LIFT_IF_FACE(obj, self, ca);
540
+ CA_WRAPPER_LIFT(obj, self, ca);
533
541
  return obj;
534
542
  }
535
543
  }
data/ext/ca_obj_select.c CHANGED
@@ -83,22 +83,57 @@ VALUE rb_cCASelectMask;
83
83
 
84
84
  /* ------------------------------------------------------------------- */
85
85
 
86
- /* Snapshot the selector into ca->select (always a copy) and pre-compute
87
- ca->indices (TRUE positions in flat parent order). Masked selector
88
- cells become false in the snapshot. After construction, mutating
89
- the caller's live selector does not affect the view. */
90
- static int
91
- ca_select_setup (CASelect *ca, CArray *parent, CArray *select, int share)
86
+ /* Owned snapshot of the selector: masked selector cells become false.
87
+ After construction, mutating the caller's live selector does not affect
88
+ the view. Reading the selector can raise (a lazy boolean view), so the
89
+ snapshot is taken before the view struct is allocated, and a snapshot
90
+ whose fill raises is freed on the way out. */
91
+ static VALUE
92
+ ca_select_snapshot_masked (VALUE arg)
92
93
  {
93
- int8_t data_type;
94
- ca_size_t bytes;
94
+ CArray **pair = (CArray **) arg;
95
+ CArray *select = pair[0], *snap = pair[1];
96
+ boolean8_t *p, *q, *m;
95
97
  ca_size_t i;
98
+ ca_attach(select);
99
+ q = (boolean8_t *) snap->ptr;
100
+ p = (boolean8_t *) select->ptr;
101
+ m = (boolean8_t *) select->mask->ptr;
102
+ for (i = 0; i < select->elements; i++) {
103
+ *q = ( *m ) ? 0 : *p;
104
+ q++; p++; m++;
105
+ }
106
+ ca_detach(select);
107
+ return Qnil;
108
+ }
96
109
 
110
+ static CArray *
111
+ ca_select_snapshot (CArray *select)
112
+ {
97
113
  if ( ! ca_is_boolean_type(select) ) {
98
114
  rb_raise(rb_eRuntimeError,
99
115
  "selection array for CASelect should be have "
100
116
  "the data_type of CA_BOOLEAN");
101
117
  }
118
+ if ( ca_has_mask(select) ) {
119
+ CArray *pair[2];
120
+ pair[0] = select;
121
+ pair[1] = ca_template(select);
122
+ ca_fill_or_free(pair[1], ca_select_snapshot_masked, (VALUE) pair);
123
+ return pair[1];
124
+ }
125
+ return ca_copy(select);
126
+ }
127
+
128
+ /* Set up the view over `parent` from an owned selector snapshot (see
129
+ ca_select_snapshot) and pre-compute ca->indices (TRUE positions in flat
130
+ parent order). Does not raise. */
131
+ static int
132
+ ca_select_setup (CASelect *ca, CArray *parent, CArray *snapshot)
133
+ {
134
+ int8_t data_type;
135
+ ca_size_t bytes;
136
+ ca_size_t i;
102
137
 
103
138
  data_type = parent->data_type;
104
139
  bytes = parent->bytes;
@@ -114,26 +149,7 @@ ca_select_setup (CASelect *ca, CArray *parent, CArray *select, int share)
114
149
  ca->attach = 0;
115
150
  ca->nosync = 0;
116
151
  ca->indices = NULL;
117
-
118
- /* The `share` argument is preserved for source compatibility but
119
- no longer toggles a live-reference path; both paths copy.
120
- Masked selector cells become false in the snapshot. */
121
- (void) share;
122
- if ( ca_has_mask(select) ) {
123
- boolean8_t *p, *q, *m;
124
- ca->select = ca_template(select);
125
- ca_attach(select);
126
- q = (boolean8_t *) ca->select->ptr;
127
- p = (boolean8_t *) select->ptr;
128
- m = (boolean8_t *) select->mask->ptr;
129
- for (i = 0; i < select->elements; i++) {
130
- *q = ( *m ) ? 0 : *p;
131
- q++; p++; m++;
132
- }
133
- ca_detach(select);
134
- } else {
135
- ca->select = ca_copy(select);
136
- }
152
+ ca->select = snapshot;
137
153
 
138
154
  /* Count TRUE positions and snapshot them into ca->indices. */
139
155
  {
@@ -181,7 +197,7 @@ ca_select_setup (CASelect *ca, CArray *parent, CArray *select, int share)
181
197
  ca->dim = &(ca->_dim);
182
198
  ca->dim[0] = ca->elements;
183
199
 
184
- if ( ca_is_scalar(select) ) {
200
+ if ( ca_is_scalar(snapshot) ) {
185
201
  ca_set_flag(ca, CA_FLAG_SCALAR);
186
202
  }
187
203
 
@@ -191,8 +207,9 @@ ca_select_setup (CASelect *ca, CArray *parent, CArray *select, int share)
191
207
  CArray *
192
208
  ca_select_new (CArray *parent, CArray *select)
193
209
  {
210
+ CArray *snapshot = ca_select_snapshot(select);
194
211
  CASelect *ca = ALLOC(CASelect);
195
- ca_select_setup(ca, parent, select, 0);
212
+ ca_select_setup(ca, parent, snapshot);
196
213
  return (CArray*) ca;
197
214
  }
198
215
 
@@ -202,9 +219,7 @@ ca_select_new (CArray *parent, CArray *select)
202
219
  CArray *
203
220
  ca_select_new_share (CArray *parent, CArray *select)
204
221
  {
205
- CASelect *ca = ALLOC(CASelect);
206
- ca_select_setup(ca, parent, select, 1);
207
- return (CArray*) ca;
222
+ return ca_select_new(parent, select);
208
223
  }
209
224
 
210
225
  static void
@@ -613,7 +628,7 @@ rb_cm_initialize_copy (VALUE self, VALUE other)
613
628
 
614
629
  /* Re-snapshot from the source's selector copy so the two views
615
630
  end up with independent indices buffers. */
616
- ca_select_setup(ca, cs->parent, cs->select, 1);
631
+ ca_select_setup(ca, cs->parent, ca_select_snapshot(cs->select));
617
632
 
618
633
  return self;
619
634
  }
@@ -571,9 +571,17 @@ ca_select_axis_func_xfer_stride (void *ap, ca_size_t *starts, ca_size_t *counts,
571
571
  s = ca->bytes;
572
572
  for (k = ndim - 1; k >= 0; k--) { dstride[k] = s; s *= counts[k]; }
573
573
 
574
- for (k = 0; k < ndim; k++) {
575
- if (strides[k] % cnative[k] != 0) { aligned = 0; break; }
576
- src_step[k] = strides[k] / cnative[k];
574
+ /* The request is over the view's addresses, so a transposed / flat request
575
+ is legal and must not be composed axis-by-axis; see
576
+ ca_xfer_stride_request_is_axis_box (carray.h). */
577
+ if ( ! ca_xfer_stride_request_is_axis_box(ca, starts, counts, strides) ) {
578
+ aligned = 0;
579
+ }
580
+ else {
581
+ for (k = 0; k < ndim; k++) {
582
+ if (strides[k] % cnative[k] != 0) { aligned = 0; break; }
583
+ src_step[k] = strides[k] / cnative[k];
584
+ }
577
585
  }
578
586
 
579
587
  if (!aligned) {
@@ -925,7 +933,7 @@ rb_ca_select_axis_s_new_debug (VALUE klass, VALUE rparent, VALUE rindirect,
925
933
  obj = TypedData_Make_Struct(klass, CASelectAxis, &caselectaxis_data_type, ca);
926
934
  ca_select_axis_setup(ca, parent, indirect_axis, selector,
927
935
  ap_start_buf, ap_count_buf, ap_step_buf);
928
- rb_ivar_set(obj, rb_intern("_parent"), rparent);
936
+ rb_ca_set_parent(obj, rparent);
929
937
  rb_ivar_set(obj, rb_intern("_selector"), rselector);
930
938
  return obj;
931
939
  }
@@ -1066,8 +1074,10 @@ rb_ca_select_axis (int argc, VALUE *argv, VALUE self)
1066
1074
  ca_select_axis_setup(ca, parent, indirect_axis, selector,
1067
1075
  ap_start, ap_count, ap_step);
1068
1076
 
1069
- /* Keep Ruby objects alive (parent and selector references). */
1070
- rb_ivar_set(obj, rb_intern("_parent"), self);
1077
+ /* Keep Ruby objects alive (parent and selector references). The parent
1078
+ goes through rb_ca_set_parent so #parent, #root_array and #ancestors
1079
+ read the same link here as they do through every other view. */
1080
+ rb_ca_set_parent(obj, self);
1071
1081
  rb_ivar_set(obj, rb_intern("_selector"), rselector_keep);
1072
1082
  return obj;
1073
1083
  }
data/ext/ca_obj_shift.c CHANGED
@@ -264,7 +264,7 @@ rb_ca_shift_new (VALUE cary, ca_size_t *shift, char *fill, int8_t *roll,
264
264
  VALUE
265
265
  rb_ca_shift (int argc, VALUE *argv, VALUE self)
266
266
  {
267
- volatile VALUE obj, ropt, rfval = CA_NIL, rroll = Qnil, rcs;
267
+ volatile VALUE obj, ropt, rfval = CA_UNSPECIFIED, rroll = Qnil, rcs;
268
268
  CArray *ca;
269
269
  CScalar *cs;
270
270
  ca_size_t shift[CA_RANK_MAX];
@@ -301,7 +301,7 @@ rb_ca_shift (int argc, VALUE *argv, VALUE self)
301
301
  "use fill_value: kwarg (e.g. shift(1, fill_value: -2))");
302
302
  }
303
303
 
304
- if ( rfval == CA_NIL ) {
304
+ if ( rfval == CA_UNSPECIFIED ) {
305
305
  /* Default fill value = 0 (or INT2NUM(0) for OBJECT type) */
306
306
  rcs = rb_cscalar_new(ca->data_type, ca->bytes, NULL);
307
307
  TypedData_Get_Struct(rcs, CScalar, &cscalar_data_type, cs);
@@ -343,7 +343,7 @@ rb_ca_shift (int argc, VALUE *argv, VALUE self)
343
343
 
344
344
  obj = rb_ca_shift_new(self, shift, fill, roll, fill_mask);
345
345
 
346
- CA_FACE_LIFT_IF_FACE(obj, self, ca);
346
+ CA_WRAPPER_LIFT(obj, self, ca);
347
347
  return obj;
348
348
  }
349
349
 
data/ext/ca_obj_stack.c CHANGED
@@ -18,6 +18,7 @@
18
18
  ---------------------------------------------------------------------------- */
19
19
 
20
20
  #include "carray.h"
21
+ #include "carray_internal.h" /* ca_attach_all */
21
22
  #include "ca_composite_dispatch.h"
22
23
  #include "ca_obj_face.h"
23
24
 
@@ -740,10 +741,7 @@ static void
740
741
  ca_stack_func_allocate (void *ap)
741
742
  {
742
743
  CAStack *ca = (CAStack *) ap;
743
- int32_t k;
744
- for ( k = 0; k < ca->n_parents; k++ ) {
745
- ca_attach(ca->parents[k]);
746
- }
744
+ ca_attach_all(ca->parents, ca->n_parents);
747
745
  ca->ptr = xmalloc(ca_length(ca));
748
746
  }
749
747
 
@@ -751,10 +749,7 @@ static void
751
749
  ca_stack_func_attach (void *ap)
752
750
  {
753
751
  CAStack *ca = (CAStack *) ap;
754
- int32_t k;
755
- for ( k = 0; k < ca->n_parents; k++ ) {
756
- ca_attach(ca->parents[k]);
757
- }
752
+ ca_attach_all(ca->parents, ca->n_parents);
758
753
  ca->ptr = xmalloc(ca_length(ca));
759
754
  ca_stack_func_xfer_all(ca, ca->ptr, CA_XFER_GET);
760
755
  }
data/ext/ca_obj_stride.c CHANGED
@@ -192,6 +192,19 @@ void ca_xfer_stride_tiled_transpose_2d (char *src_base, ca_size_t bytes,
192
192
  ca_size_t *counts, ca_size_t *strides,
193
193
  char *data, int dir);
194
194
 
195
+ /* Public form of the family test the fold walk below uses inline. The
196
+ family is defined by the operation table, not by a class or an
197
+ obj_type list: every member was installed with a copy of
198
+ ca_stride_func, so an externally installed view that shares the
199
+ table answers true as well. See carray.h for what membership does
200
+ and does not promise. */
201
+ int
202
+ ca_is_stride_family (const void *ap)
203
+ {
204
+ const CArray *ca = (const CArray *) ap;
205
+ return ( ca_func[ca->obj_type].attach == ca_stride_func.attach );
206
+ }
207
+
195
208
  /* Compose `leaf->strides` and `leaf->base_offset` (which live in `parent`'s
196
209
  own logical row-major contig byte space) into `out_strides` and
197
210
  `out_base` expressed in `parent->parent`'s byte space.
@@ -616,9 +629,29 @@ ca_stride_func_xfer_addrs (void *ap, ca_size_t n, ca_size_t *addrs,
616
629
  hand the whole region to the root in a SINGLE ca_xfer_stride (entity -> ptr
617
630
  memcpy; boundary view -> one recursion).
618
631
 
619
- Axis-aligned, byte-matching requests only; the byte-mismatch reinterpret
620
- (CAField .real/.imag) and non-aligned access fall back to per-cell
621
- xfer_index (which handles the sub-byte case). */
632
+ CAREFUL: the request is given over this view's linear ADDRESSES (carray.h
633
+ xfer_stride contract), so request axis k does NOT have to be view axis k.
634
+ A caller is free to hand over a transposed region -- counts/strides in one
635
+ order, the packed destination in another -- which is exactly what a
636
+ column-major backend (carray-linalg's Fortran-LAPACK gather) does. Matching
637
+ request axis k to view axis k by dividing strides[k] by the axis-k native
638
+ step looks right and is wrong: an (n, 1) view has the same native step on
639
+ both axes, so a transposed request divides cleanly and then composes the
640
+ n-cell walk onto the length-1 axis, whose parent stride is 0 -- delivering
641
+ the first cell n times, with no error anywhere. Ask ca_stride_region_axes
642
+ which view axis each request axis really moves (the same question
643
+ fill_stride asks), and fall back to the per-cell walk when the region is
644
+ not a box over our axes.
645
+
646
+ Byte-matching requests only; the byte-mismatch reinterpret (CAField
647
+ .real/.imag) and non-box access fall back to per-cell xfer_index (which
648
+ handles the sub-byte case). */
649
+
650
+ static int ca_stride_region_axes (CAStride *ca, ca_size_t base, int8_t ndim,
651
+ ca_size_t *counts, ca_size_t *steps,
652
+ ca_size_t *base_idx, int8_t *axis_of,
653
+ ca_size_t *mult);
654
+
622
655
  static void
623
656
  ca_stride_func_xfer_stride (void *ap, ca_size_t *starts, ca_size_t *counts,
624
657
  ca_size_t *strides, void *data, int dir)
@@ -629,7 +662,12 @@ ca_stride_func_xfer_stride (void *ap, ca_size_t *starts, ca_size_t *counts,
629
662
  ca_size_t composed_base;
630
663
  ca_size_t view_native[CA_RANK_MAX];
631
664
  ca_size_t root_stride[CA_RANK_MAX];
665
+ ca_size_t steps[CA_RANK_MAX];
666
+ ca_size_t base_idx[CA_RANK_MAX];
667
+ ca_size_t mult[CA_RANK_MAX];
668
+ int8_t axis_of[CA_RANK_MAX];
632
669
  ca_size_t root_base;
670
+ ca_size_t base_addr = 0;
633
671
  ca_size_t s;
634
672
  int8_t ndim = ca->ndim, k;
635
673
  int aligned = 1;
@@ -639,26 +677,77 @@ ca_stride_func_xfer_stride (void *ap, ca_size_t *starts, ca_size_t *counts,
639
677
 
640
678
  s = ca->bytes;
641
679
  for (k = ndim - 1; k >= 0; k--) { view_native[k] = s; s *= ca->dim[k]; }
680
+ for (k = 0; k < ndim; k++) base_addr += starts[k] * view_native[k];
642
681
 
643
682
  if (ca->bytes != root->bytes) {
644
683
  aligned = 0;
645
684
  }
646
685
  else {
647
686
  for (k = 0; k < ndim; k++) {
648
- if (strides[k] % view_native[k] != 0) { aligned = 0; break; }
687
+ if ( strides[k] % ca->bytes != 0 ) { aligned = 0; break; }
688
+ steps[k] = strides[k] / ca->bytes;
689
+ }
690
+ if ( aligned ) {
691
+ aligned = ca_stride_region_axes(ca, base_addr / ca->bytes, ndim,
692
+ counts, steps, base_idx, axis_of, mult);
693
+ }
694
+ }
695
+
696
+ if ( aligned ) {
697
+ /* Each request axis now names the view axis it moves (axis_of) and by how
698
+ many of that axis' cells (mult); a count-1 axis moves nothing and gets
699
+ stride 0, which the walk never follows. */
700
+ root_base = composed_base;
701
+ for (k = 0; k < ca->ndim; k++) {
702
+ root_base += base_idx[k] * composed_strides[k];
703
+ }
704
+ for (k = 0; k < ndim; k++) {
705
+ root_stride[k] = ( axis_of[k] >= 0 )
706
+ ? mult[k] * composed_strides[axis_of[k]]
707
+ : 0;
708
+ }
709
+ }
710
+
711
+ /* Cold root that answers regions: compose the request into its addresses
712
+ and hand it over whole, exactly as xfer_all does for the whole view. A
713
+ root with no memory to lend (a lazy transform, a CAObject over a file)
714
+ has no ptr to walk, but it can still produce a region on request -- and
715
+ asking it once beats asking it once per cell, which is what the per-cell
716
+ descent below would do. Chunked consumers (the binop / sweep drivers'
717
+ per-chunk gather) arrive here, so the difference is the whole cost of
718
+ the transfer, not a constant factor.
719
+
720
+ The gate is xfer_all's: the root must have the slot, share this view's
721
+ cell width (else the composed offsets are not whole root elements), and
722
+ carry the same ndim (else its index space cannot hold this request's
723
+ axes). Anything narrower keeps the per-cell descent, which is correct
724
+ for all of them. Direction is not part of the gate: a root that refuses
725
+ writes refuses them per cell as well. */
726
+ if (aligned && !root->ptr && ca_func[root->obj_type].xfer_stride
727
+ && ca->bytes == root->bytes && ndim == root->ndim) {
728
+ ca_size_t rstarts[CA_RANK_MAX];
729
+ if ( root_base % root->bytes == 0 ) {
730
+ ca_size_t raddr = root_base / root->bytes;
731
+ if ( raddr >= 0 && raddr < root->elements ) {
732
+ ca_addr2index(root, raddr, rstarts);
733
+ ca_xfer_stride(root, rstarts, counts, root_stride, d, dir);
734
+ return;
735
+ }
649
736
  }
650
737
  }
651
738
 
652
739
  /* Per-cell fallback (correct, no whole-view attach): byte-mismatch
653
- reinterpret (CAField), non-aligned access, or a cold non-entity root
654
- (whose ndim may differ from the view's -- e.g. a reshape over a boundary).
655
- ca_stride_func_xfer_index composes one hop and delegates to the parent. */
740
+ reinterpret (CAField), a region that is not a box over our axes (a
741
+ transposed request onto a degenerate axis, a flat index over several
742
+ axes), or a cold non-entity root the branch above could not hand a
743
+ region to (its ndim differs from the view's -- e.g. a reshape over a
744
+ boundary -- or it has no region slot). ca_stride_func_xfer_index
745
+ composes one hop and delegates to the parent. */
656
746
  if (!aligned || !root->ptr) {
657
- ca_size_t idx[CA_RANK_MAX], doff = 0, base = 0;
658
- for (k = 0; k < ndim; k++) base += starts[k] * view_native[k];
747
+ ca_size_t idx[CA_RANK_MAX], doff = 0;
659
748
  for (k = 0; k < ndim; k++) idx[k] = 0;
660
749
  while (1) {
661
- ca_size_t off = base, vmidx[CA_RANK_MAX];
750
+ ca_size_t off = base_addr, vmidx[CA_RANK_MAX];
662
751
  for (k = 0; k < ndim; k++) off += idx[k] * strides[k];
663
752
  ca_addr2index((CArray *) ca, off / ca->bytes, vmidx);
664
753
  ca_stride_func_xfer_index(ca, vmidx, d + doff, dir);
@@ -670,17 +759,11 @@ ca_stride_func_xfer_stride (void *ap, ca_size_t *starts, ca_size_t *counts,
670
759
  return;
671
760
  }
672
761
 
673
- /* Structural: root has a live ptr (entity / attached). Translate the
674
- request into root's BYTE space and do a strided memcpy in the VIEW's ndim
675
- (byte offsets into root->ptr -- independent of root's own ndim, so a
676
- reshape view over a 1-D entity works). compose happened once. */
677
- root_base = composed_base;
678
- for (k = 0; k < ndim; k++) {
679
- ca_size_t req_step = strides[k] / view_native[k];
680
- root_base += starts[k] * composed_strides[k];
681
- root_stride[k] = req_step * composed_strides[k];
682
- }
683
- /* Slab-merge, tile-block and the general driver all live in the shared
762
+ /* Structural: root has a live ptr (entity / attached). The request is
763
+ already in root's BYTE space (root_base / root_stride above), so the walk
764
+ runs in the VIEW's ndim -- independent of root's own ndim, which is what
765
+ lets a reshape view over a 1-D entity through. compose happened once.
766
+ Slab-merge, tile-block and the general driver all live in the shared
684
767
  walker, which the central dispatcher's structural path also uses. */
685
768
  ca_xfer_strided_walk(root->ptr + root_base, ca->bytes, ndim,
686
769
  counts, root_stride, d, dir);
@@ -753,6 +836,59 @@ ca_stride_region_axes (CAStride *ca, ca_size_t base, int8_t ndim,
753
836
  root in units root does not share, so there is no address to hand over
754
837
  and the per-cell descent stands in. */
755
838
 
839
+ static void
840
+ ca_stride_func_fill_addrs (void *ap, ca_size_t n, ca_size_t *addrs, void *ptr)
841
+ {
842
+ CAStride *ca = (CAStride *) ap;
843
+ CArray *root;
844
+ ca_size_t composed_strides[CA_RANK_MAX];
845
+ ca_size_t composed_base;
846
+ ca_size_t rbytes;
847
+ ca_size_t *paddrs;
848
+ ca_size_t i;
849
+ int8_t k;
850
+ int all_aligned = 1;
851
+ volatile VALUE holder;
852
+
853
+ /* Addresses name cells one by one, so unlike fill_stride there is nothing
854
+ here that only the whole extent can express: translate each view address
855
+ into the root's and hand the list down in one call. Without this the
856
+ region is still the only thing touched -- ca_fill_addrs_default sees to
857
+ that -- but it costs the parent one call per cell, which is the whole
858
+ difference on a backing where a call is a request. */
859
+ if ( n == 0 ) {
860
+ return;
861
+ }
862
+
863
+ ca_stride_compose_to_root(ca, &root, composed_strides, &composed_base);
864
+ rbytes = root->bytes;
865
+
866
+ if ( ca->bytes != rbytes ) {
867
+ ca_fill_addrs_default(ca, n, addrs, ptr);
868
+ return;
869
+ }
870
+
871
+ paddrs = ALLOCV_N(ca_size_t, holder, n);
872
+ for ( i = 0; i < n; i++ ) {
873
+ ca_size_t idx[CA_RANK_MAX];
874
+ ca_size_t off = composed_base;
875
+ ca_addr2index((CArray *) ca, addrs[i], idx);
876
+ for ( k = 0; k < ca->ndim; k++ ) {
877
+ off += idx[k] * composed_strides[k];
878
+ }
879
+ if ( off % rbytes != 0 ) { all_aligned = 0; break; }
880
+ paddrs[i] = off / rbytes;
881
+ }
882
+
883
+ if ( all_aligned ) {
884
+ ca_fill_addrs(root, n, paddrs, ptr);
885
+ }
886
+ else {
887
+ ca_fill_addrs_default(ca, n, addrs, ptr);
888
+ }
889
+ ALLOCV_END(holder);
890
+ }
891
+
756
892
  static void
757
893
  ca_stride_func_fill_stride (void *ap, ca_size_t base, int8_t ndim,
758
894
  ca_size_t *counts, ca_size_t *steps, void *ptr)
@@ -920,6 +1056,15 @@ ca_stride_func_allocate (void *ap)
920
1056
  }
921
1057
  }
922
1058
 
1059
+ /* Region request into the cold-root attach buffer; a lazy root can raise. */
1060
+ static VALUE
1061
+ ca_stride_cold_fill (VALUE arg)
1062
+ {
1063
+ void **args = (void **) arg;
1064
+ ca_copy_data((CArray *) args[0], (char *) args[1]);
1065
+ return Qnil;
1066
+ }
1067
+
923
1068
  static void
924
1069
  ca_stride_func_attach (void *ap)
925
1070
  {
@@ -935,7 +1080,15 @@ ca_stride_func_attach (void *ap)
935
1080
  view with a live ptr is what makes the per-cell dispatchers bypass the
936
1081
  transfer slots. */
937
1082
  char *buf = xmalloc(ca_length(ca));
938
- ca_copy_data(ca, buf); /* region request, root stays cold */
1083
+ void *args[2];
1084
+ int tag = 0;
1085
+ args[0] = ca;
1086
+ args[1] = buf;
1087
+ rb_protect(ca_stride_cold_fill, (VALUE) args, &tag); /* root stays cold */
1088
+ if (tag) {
1089
+ xfree(buf);
1090
+ rb_jump_tag(tag);
1091
+ }
939
1092
  ca->ptr = buf;
940
1093
  return;
941
1094
  }
@@ -1400,6 +1553,7 @@ ca_operation_function_t ca_stride_func = {
1400
1553
  sizeof(CAStride), /* struct_size: pool framework */
1401
1554
  ca_stride_pool_bytes, /* pool_bytes */
1402
1555
  ca_stride_pool_init, /* pool_init */
1556
+ .fill_addrs = ca_stride_func_fill_addrs,
1403
1557
  .fill_stride = ca_stride_func_fill_stride,
1404
1558
  };
1405
1559
 
@@ -2239,7 +2393,7 @@ rb_ca_flip_build_view (VALUE self, CArray *parent, const int8_t *flip)
2239
2393
 
2240
2394
  VALUE obj = rb_ca_stride_new(self, parent->data_type, parent->bytes,
2241
2395
  ndim, outdim, outstrides, base_offset);
2242
- CA_FACE_LIFT_IF_FACE(obj, self, parent);
2396
+ CA_WRAPPER_LIFT(obj, self, parent);
2243
2397
  return obj;
2244
2398
  }
2245
2399
 
@@ -2462,7 +2616,7 @@ rb_ca_diagonal (int argc, VALUE *argv, VALUE self)
2462
2616
  {
2463
2617
  VALUE obj = rb_ca_stride_new(self, parent->data_type, parent->bytes,
2464
2618
  out_k, outdim, outstrides, base_offset);
2465
- CA_FACE_LIFT_IF_FACE(obj, self, parent);
2619
+ CA_WRAPPER_LIFT(obj, self, parent);
2466
2620
  return obj;
2467
2621
  }
2468
2622
  }
data/ext/ca_obj_string.c CHANGED
@@ -90,10 +90,14 @@ ca_string_setup (CAString *ca, CArray *parent)
90
90
 
91
91
  ca->obj_type = CA_OBJ_STRING;
92
92
  ca->data_type = CA_OBJECT;
93
- /* ORDERABLE: object storage sorts by <=> (= String order on the surface),
94
- so the sort family may descend to storage. COMPARABLE is left off for
95
- now; ordered search (bsearch) is a later phase. */
96
- ca->flags = CA_FLAG_IS_FACE | CA_FLAG_FACE_ORDERABLE_STORAGE;
93
+ /* ORDERABLE + COMPARABLE, and both hold by construction: a storage cell
94
+ IS the Ruby String the surface shows, so storage order is surface
95
+ order and an external String compares against storage directly, with
96
+ nothing to reconcile. (A unit-bearing Face like CATime is the case
97
+ that has to stop at ORDERABLE; this one carries no unit.) */
98
+ ca->flags = CA_FLAG_IS_FACE
99
+ | CA_FLAG_FACE_ORDERABLE_STORAGE
100
+ | CA_FLAG_FACE_COMPARABLE_STORAGE;
97
101
  ca->ndim = parent->ndim;
98
102
  ca->bytes = sizeof(VALUE);
99
103
  ca->elements = parent->elements;
@@ -192,7 +196,9 @@ ca_operation_function_t ca_string_func = {
192
196
  ca_face_xfer_addrs,
193
197
  NULL, /* fold_stride: identity Face is not foldable */
194
198
  ca_face_xfer_stride,
195
- ca_face_xfer_all
199
+ ca_face_xfer_all,
200
+ .fill_addrs = ca_face_fill_addrs,
201
+ .fill_stride = ca_face_fill_stride,
196
202
  };
197
203
 
198
204
  /* ------------------------------------------------------------------- */