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_fake.c CHANGED
@@ -462,6 +462,17 @@ rb_ca_fake_new (VALUE cary, int8_t data_type, ca_size_t bytes)
462
462
  CAFake *ca;
463
463
  rb_check_carray_object(cary);
464
464
  TypedData_Get_Struct(cary, CArray, &carray_data_type, parent);
465
+ if ( ca_is_face(parent) && ( data_type == CA_OBJECT
466
+ || parent->data_type == CA_FIXLEN ) ) {
467
+ /* A Face's cells do not mean their storage bytes, so reading them
468
+ under another data_type hands back what the surface exists to hide.
469
+ Both ways down stay open and say which one they are. A Numeric Face
470
+ is not one of these: its surface is its storage. */
471
+ rb_raise(rb_eTypeError,
472
+ "%s has no view of its values in another data_type: "
473
+ "#to_type gives the values, #parent.fake the raw storage",
474
+ rb_obj_classname(cary));
475
+ }
465
476
  ca = ca_fake_new(parent, data_type, bytes);
466
477
  obj = ca_wrap_struct(ca);
467
478
  rb_ca_set_parent(obj, cary);
@@ -91,11 +91,24 @@ ca_fixlen_string_setup (CAFixlenString *ca, CArray *parent)
91
91
 
92
92
  ca->obj_type = CA_OBJ_FIXLEN_STRING;
93
93
  ca->data_type = CA_FIXLEN;
94
- /* ORDERABLE + COMPARABLE, and both hold by construction: this Face's surface
95
- IS its storage, byte for byte (a cell decodes to its own bytes, padding
96
- included), so the descent is the identity map. memcmp order is therefore
97
- String#<=> order for these cells, and byte equality is cell equality --
98
- which is what the equality families need (docs/topics/CAFace.md §6.3).
94
+ /* ORDERABLE + COMPARABLE. The descent is NOT the identity map -- the
95
+ scalar decode below strips trailing NUL -- but both flags still hold,
96
+ for a reason worth stating exactly, since it is what a Face author
97
+ copying this would need:
98
+
99
+ ORDERABLE: every cell is padded to the same K with NUL, and NUL is
100
+ the smallest byte, so memcmp on the padded cells orders them the same
101
+ way String#<=> orders the stripped strings (a prefix sorts before any
102
+ continuation, which is what the padding reproduces). Stripping is
103
+ order-preserving; it is not order-irrelevant by being absent.
104
+
105
+ COMPARABLE: a String query is padded out to the cell width before it
106
+ is compared, so byte equality on the padded form is equality on the
107
+ stripped form -- for a query that fits. A query longer than K is
108
+ truncated to its own first K bytes by that same padding step, and
109
+ then matches a cell it is not equal to.
110
+
111
+ Which is what the equality families need (docs/topics/CAFace.md §6.3).
99
112
  Without the flags the sort family still worked (it exempts CA_FIXLEN
100
113
  storage from the gate and orders by memcmp), but the value-hash family
101
114
  handed its results back as a plain fixlen array, and search refused a
@@ -202,7 +215,9 @@ ca_operation_function_t ca_fixlen_string_func = {
202
215
  ca_face_xfer_addrs,
203
216
  NULL, /* fold_stride: identity Face is not foldable */
204
217
  ca_face_xfer_stride,
205
- ca_face_xfer_all
218
+ ca_face_xfer_all,
219
+ .fill_addrs = ca_face_fill_addrs,
220
+ .fill_stride = ca_face_fill_stride,
206
221
  };
207
222
 
208
223
  /* ------------------------------------------------------------------- */
data/ext/ca_obj_grid.c CHANGED
@@ -478,9 +478,17 @@ ca_grid_func_xfer_stride (void *ap, ca_size_t *starts, ca_size_t *counts,
478
478
  transposed leaf breaks this (cross-axis / non-multiple strides); fall back
479
479
  to per-cell delivery (correct, still no whole-view attach). The wiring
480
480
  guards ndim == grid->ndim, so counts/strides have ndim entries here. */
481
- for (k = 0; k < ndim; k++) {
482
- if (strides[k] % gnative[k] != 0) { aligned = 0; break; }
483
- src_step[k] = strides[k] / gnative[k];
481
+ /* The request is over the view's addresses, so a transposed / flat request
482
+ is legal and must not be composed axis-by-axis; see
483
+ ca_xfer_stride_request_is_axis_box (carray.h). */
484
+ if ( ! ca_xfer_stride_request_is_axis_box(g, starts, counts, strides) ) {
485
+ aligned = 0;
486
+ }
487
+ else {
488
+ for (k = 0; k < ndim; k++) {
489
+ if (strides[k] % gnative[k] != 0) { aligned = 0; break; }
490
+ src_step[k] = strides[k] / gnative[k];
491
+ }
484
492
  }
485
493
 
486
494
  if (!aligned) {
data/ext/ca_obj_meld.c CHANGED
@@ -39,6 +39,8 @@
39
39
  ---------------------------------------------------------------------------- */
40
40
 
41
41
  #include "carray.h"
42
+ #include "carray_internal.h" /* ca_attach_all */
43
+ #include "ca_obj_face.h" /* ca_face_state_portable */
42
44
 
43
45
  /* ------------------------------------------------------------------- */
44
46
  /* TypedData */
@@ -89,7 +91,7 @@ static ID id_parents;
89
91
  /* uniform check */
90
92
  /* ------------------------------------------------------------------- */
91
93
 
92
- /* Parents must share dtype, ndim, bytes, and all dims except meld_axis;
94
+ /* Parents must share data type, ndim, bytes, and all dims except meld_axis;
93
95
  meld_axis lengths are the ragged dimension (may differ). */
94
96
  static void
95
97
  ca_meld_check_uniform (int32_t n_parents, CArray **parents, int8_t meld_axis)
@@ -150,6 +152,19 @@ ca_meld_setup (CAMeld *ca, int32_t n_parents, CArray **parents, int8_t meld_axis
150
152
  int32_t i;
151
153
  int8_t a;
152
154
 
155
+ /* Pre-strip Face parents one level to storage, as CAStack does, so a
156
+ melded Face lifts to a single-Face chain (CATime[CAMeld[entity, ...]])
157
+ rather than a Face sitting on Face parents. One level, not a full walk,
158
+ preserves any distinct Face a parent melded underneath. A Face is
159
+ storage-transparent, so welding over storage reads the same bytes; the
160
+ lifted top Face (rb_ca_meld_s_new) carries the identity. The @parents
161
+ accessor keeps the originals, which the callers set. */
162
+ for ( i = 0; i < n_parents; i++ ) {
163
+ if ( ca_is_face(parents[i]) ) {
164
+ parents[i] = CAVIEW(parents[i])->parent;
165
+ }
166
+ }
167
+
153
168
  ca_meld_check_uniform(n_parents, parents, meld_axis);
154
169
  ref = parents[0];
155
170
 
@@ -598,19 +613,9 @@ ca_meld_func_xfer_stride (void *ap, ca_size_t *starts, ca_size_t *counts,
598
613
  int8_t i;
599
614
  int structural = 1;
600
615
 
601
- /* Bound check on meld_axis */
602
- {
603
- ca_size_t req_lo = starts[ma];
604
- ca_size_t req_hi = starts[ma] + counts[ma];
605
- if ( req_lo < 0 || req_hi > ca->dim[ma] ) {
606
- rb_raise(rb_eIndexError,
607
- "CAMeld xfer_stride meld_axis (axis %d) [%lld, %lld) out of range [0, %lld)",
608
- (int) ma, (long long) req_lo, (long long) req_hi, (long long) ca->dim[ma]);
609
- }
610
- }
611
-
612
616
  s = ca->bytes;
613
617
  for ( i = ca->ndim - 1; i >= 0; i-- ) { native[i] = s; s *= ca->dim[i]; }
618
+
614
619
  for ( i = 0; i < ca->ndim; i++ ) {
615
620
  if ( strides[i] != native[i] ) { structural = 0; break; }
616
621
  }
@@ -619,6 +624,23 @@ ca_meld_func_xfer_stride (void *ap, ca_size_t *starts, ca_size_t *counts,
619
624
  ca_meld_xfer_stride_per_cell(ca, starts, counts, strides, data, dir);
620
625
  return;
621
626
  }
627
+
628
+ /* Bound check on meld_axis, after the gate and not before it. Only once
629
+ strides == native is established does request axis ma mean view axis ma;
630
+ asked earlier, strides[ma] / native[ma] is not a step along the meld axis
631
+ and legal requests get rejected. Inside the gate the step is 1, so the
632
+ region is starts[ma] .. starts[ma] + counts[ma]. ca_meld_xfer_stride_ma_
633
+ internal below is handed no strides at all and depends on both facts. */
634
+ {
635
+ ca_size_t req_lo = starts[ma];
636
+ ca_size_t req_hi = starts[ma] + counts[ma];
637
+ if ( counts[ma] > 0 && ( req_lo < 0 || req_hi > ca->dim[ma] ) ) {
638
+ rb_raise(rb_eIndexError,
639
+ "CAMeld xfer_stride meld_axis (axis %d) [%lld, %lld) out of range [0, %lld)",
640
+ (int) ma, (long long) req_lo, (long long) req_hi, (long long) ca->dim[ma]);
641
+ }
642
+ }
643
+
622
644
  if ( ma == 0 ) {
623
645
  ca_meld_xfer_stride_ma0(ca, starts, counts, strides, data, dir);
624
646
  } else {
@@ -765,10 +787,7 @@ static void
765
787
  ca_meld_func_allocate (void *ap)
766
788
  {
767
789
  CAMeld *ca = (CAMeld *) ap;
768
- int32_t k;
769
- for ( k = 0; k < ca->n_parents; k++ ) {
770
- ca_attach(ca->parents[k]);
771
- }
790
+ ca_attach_all(ca->parents, ca->n_parents);
772
791
  ca->ptr = xmalloc(ca_length(ca));
773
792
  }
774
793
 
@@ -776,10 +795,7 @@ static void
776
795
  ca_meld_func_attach (void *ap)
777
796
  {
778
797
  CAMeld *ca = (CAMeld *) ap;
779
- int32_t k;
780
- for ( k = 0; k < ca->n_parents; k++ ) {
781
- ca_attach(ca->parents[k]);
782
- }
798
+ ca_attach_all(ca->parents, ca->n_parents);
783
799
  ca->ptr = xmalloc(ca_length(ca));
784
800
  ca_meld_func_xfer_all(ca, ca->ptr, CA_XFER_GET);
785
801
  }
@@ -882,6 +898,17 @@ ca_operation_function_t ca_meld_func = {
882
898
  /* Ruby surface */
883
899
  /* ------------------------------------------------------------------- */
884
900
 
901
+ /* One-level strip of a Face VALUE to its storage-side parent (non-Face as-is).
902
+ Keeps the @parent ivar in step with the pre-stripped C parents, so the
903
+ Ruby-visible chain of a melded Face is single-Face too. */
904
+ static VALUE
905
+ ca_meld_face_parent1 (VALUE v)
906
+ {
907
+ CArray *c;
908
+ TypedData_Get_Struct(v, CArray, &carray_data_type, c);
909
+ return ca_is_face(c) ? rb_ca_parent(v) : v;
910
+ }
911
+
885
912
  VALUE
886
913
  rb_ca_meld_new (VALUE parents_ary, int8_t meld_axis)
887
914
  {
@@ -905,7 +932,7 @@ rb_ca_meld_new (VALUE parents_ary, int8_t meld_axis)
905
932
  ca = ca_meld_new((int32_t) n, parents, meld_axis);
906
933
  obj = ca_wrap_struct(ca);
907
934
  rb_ivar_set(obj, id_parents, rb_ary_dup(parents_ary));
908
- rb_ca_set_parent(obj, rb_ary_entry(parents_ary, 0));
935
+ rb_ca_set_parent(obj, ca_meld_face_parent1(rb_ary_entry(parents_ary, 0)));
909
936
  ALLOCV_END(holder);
910
937
  return obj;
911
938
  }
@@ -962,11 +989,86 @@ rb_ca_meld_initialize (int argc, VALUE *argv, VALUE self)
962
989
  }
963
990
  ca_meld_setup(ca, (int32_t) n, parents, meld_axis);
964
991
  rb_ivar_set(self, id_parents, rb_ary_dup(list));
965
- rb_ca_set_parent(self, rb_ary_entry(list, 0));
992
+ rb_ca_set_parent(self, ca_meld_face_parent1(rb_ary_entry(list, 0)));
966
993
  ALLOCV_END(holder);
967
994
  return self;
968
995
  }
969
996
 
997
+ /* CAMeld.new(list, axis: 0) -- Class#new override, the twin of
998
+ rb_ca_stack_s_new. A melded view has one surface over many parents, so a
999
+ homogeneous Face list is treated the way CAStack treats it: refuse a Face
1000
+ whose state is per-parent (the cells of parent i are only readable against
1001
+ parent i's own state -- a CAConstString's cells are byte ranges into ITS
1002
+ buffer), and lift the rest so the Face survives the weld.
1003
+
1004
+ Without this meld was the one multi-parent constructor that neither asked
1005
+ nor lifted: it returned the raw storage of whatever it was given, which for
1006
+ CAConstString meant 16-byte (start,end) pairs presented as the string cells,
1007
+ and for CATime meant raw int64 ticks. Both looked like data.
1008
+
1009
+ Doing it here rather than in CArray.meld covers CAFrame.meld and a direct
1010
+ CAMeld.new by the same check. */
1011
+ static VALUE
1012
+ rb_ca_meld_s_new (int argc, VALUE *argv, VALUE klass)
1013
+ {
1014
+ VALUE list, kwargs;
1015
+ long n, i;
1016
+ int all_face = 1;
1017
+ VALUE face_class = Qnil;
1018
+ CArray *ref_face = NULL;
1019
+ VALUE obj;
1020
+
1021
+ rb_scan_args(argc, argv, "1:", &list, &kwargs);
1022
+ Check_Type(list, T_ARRAY);
1023
+ n = RARRAY_LEN(list);
1024
+ if ( n <= 0 ) {
1025
+ rb_raise(rb_eArgError, "CAMeld.new requires at least one parent");
1026
+ }
1027
+
1028
+ for ( i = 0; i < n; i++ ) {
1029
+ VALUE p = rb_ary_entry(list, i);
1030
+ CArray *ca;
1031
+ rb_check_carray_object(p);
1032
+ TypedData_Get_Struct(p, CArray, &carray_data_type, ca);
1033
+ if ( !ca_is_face(ca) ) { all_face = 0; break; }
1034
+ if ( i == 0 ) {
1035
+ face_class = rb_obj_class(p);
1036
+ ref_face = ca;
1037
+ } else if ( rb_obj_class(p) != face_class ) {
1038
+ all_face = 0; break;
1039
+ }
1040
+ }
1041
+
1042
+ /* A single parent has nothing to weld against, so its Face rides the
1043
+ chain as it always did; refuse and lift only apply from two up. */
1044
+ if ( all_face && n > 1
1045
+ && !ca_face_state_portable(ref_face->obj_type, face_class) ) {
1046
+ rb_raise(rb_eArgError,
1047
+ "CAMeld.new: %s state is not portable across multiple "
1048
+ "parents (= per-parent storage like CAConstString's buffer); "
1049
+ "strip Face with .parent if a storage-level CAMeld is intended",
1050
+ rb_class2name(face_class));
1051
+ }
1052
+
1053
+ obj = rb_obj_alloc(klass);
1054
+ rb_obj_call_init_kw(obj, argc, argv, RB_PASS_CALLED_KEYWORDS);
1055
+
1056
+ if ( !all_face || n < 2 ) return obj;
1057
+
1058
+ for ( i = 1; i < n; i++ ) {
1059
+ VALUE p = rb_ary_entry(list, i);
1060
+ CArray *ca;
1061
+ TypedData_Get_Struct(p, CArray, &carray_data_type, ca);
1062
+ if ( !ca_face_state_compatible(rb_ary_entry(list, 0), ref_face, p, ca) ) {
1063
+ rb_raise(rb_eArgError,
1064
+ "CAMeld.new: Face state mismatch across parents "
1065
+ "(= %s instance at index %ld differs in state from index 0)",
1066
+ rb_class2name(face_class), i);
1067
+ }
1068
+ }
1069
+ return ca_face_lift(obj, rb_ary_entry(list, 0));
1070
+ }
1071
+
970
1072
  static VALUE
971
1073
  rb_ca_meld_n_parents (VALUE self)
972
1074
  {
@@ -1022,6 +1124,7 @@ Init_ca_obj_meld (void)
1022
1124
  id_parents = rb_intern("parents");
1023
1125
 
1024
1126
  rb_define_alloc_func(rb_cCAMeld, rb_ca_meld_s_allocate);
1127
+ rb_define_singleton_method(rb_cCAMeld, "new", rb_ca_meld_s_new, -1);
1025
1128
  rb_define_method(rb_cCAMeld, "initialize",
1026
1129
  rb_ca_meld_initialize, -1);
1027
1130
  rb_define_method(rb_cCAMeld, "initialize_copy",
data/ext/ca_obj_moncmp.c CHANGED
@@ -2,7 +2,7 @@
2
2
 
3
3
  CAMonCmp — lazy monadic element-wise comparison view: is_nan /
4
4
  is_inf / is_finite / is_invalid / signbit. Output data_type =
5
- CA_BOOLEAN (1 byte); operand keeps its native data_type (per-dtype
5
+ CA_BOOLEAN (1 byte); operand keeps its native data_type (per-type
6
6
  kernels cover integer as well, so is_nan / is_inf on integer parents
7
7
  return const-false and is_finite const-true without a cast layer).
8
8
 
@@ -20,7 +20,7 @@
20
20
  Peak scratch: 1 operand-data_type slab.
21
21
 
22
22
  Mask propagation:
23
- Per-dtype moncmp kernels write only at non-masked positions, so
23
+ Per-type moncmp kernels write only at non-masked positions, so
24
24
  the standard create_mask = parent.mask machinery carries mask bits
25
25
  to the caller-visible result. No in-flight mask handling in
26
26
  xfer_stride.
@@ -121,9 +121,8 @@ ca_moncmp_setup (CAMonCmp *ca, CArray *parent, uint16_t op_id)
121
121
 
122
122
  memcpy(ca->dim, parent->dim, parent->ndim * sizeof(ca_size_t));
123
123
 
124
- if ( ca_has_mask(parent) ) {
125
- ca_create_mask(ca);
126
- }
124
+ /* The mask is NOT built here: ca_has_mask creates a view's mask on
125
+ demand from its parent's. */
127
126
  if ( ca_is_scalar(parent) ) {
128
127
  ca_set_flag(ca, CA_FLAG_SCALAR);
129
128
  }
@@ -252,6 +251,8 @@ ca_moncmp_func_xfer_stride (void *ap, ca_size_t *starts, ca_size_t *counts,
252
251
  int8_t k;
253
252
  void *scratch;
254
253
  ca_size_t operand_bytes;
254
+ ca_size_t operand_strides[CA_RANK_MAX];
255
+ int is_contig;
255
256
 
256
257
  if ( dir != CA_XFER_GET ) {
257
258
  rb_raise(rb_eRuntimeError, "CAMonCmp is read-only (xfer_stride PUT)");
@@ -263,26 +264,44 @@ ca_moncmp_func_xfer_stride (void *ap, ca_size_t *starts, ca_size_t *counts,
263
264
 
264
265
  operand_bytes = mc->parent->bytes;
265
266
 
267
+ /* The operand's cell is operand_bytes wide where this view's is one byte
268
+ (CA_BOOLEAN), so the caller's strides have to be restated at the
269
+ operand's cell size before the pull; strides[k] / bytes is the index
270
+ step, which is what the two spaces share.
271
+
272
+ is_contig says the caller asked for the row-major slab that xfer_all
273
+ and attach send. Only then does the parent's own buffer hold the
274
+ wanted cells in the packed order the kernel reads them in, so it is
275
+ also the only case the leaf in-place path is good for. */
276
+ {
277
+ ca_size_t native = mc->bytes;
278
+ is_contig = 1;
279
+ for ( k = mc->ndim - 1; k >= 0; k-- ) {
280
+ operand_strides[k] = strides[k] / mc->bytes * operand_bytes;
281
+ if ( strides[k] != native ) {
282
+ is_contig = 0;
283
+ }
284
+ native *= mc->dim[k];
285
+ }
286
+ }
287
+
266
288
  /* === 1. pull parent (leaf-opt or scratch) === */
267
289
  int scratch_is_inplace = 0;
268
290
  {
269
291
  char *inplace = NULL;
270
- if ( ca_moncmp_try_leaf_inplace(mc->parent, starts, counts,
292
+ if ( is_contig &&
293
+ ca_moncmp_try_leaf_inplace(mc->parent, starts, counts,
271
294
  operand_bytes, &inplace) ) {
272
295
  scratch = inplace;
273
296
  scratch_is_inplace = 1;
274
297
  ca_moncmp_leaf_inplace_count++;
275
298
  }
276
299
  else {
277
- ca_size_t scratch_strides[CA_RANK_MAX];
278
- ca_size_t s = operand_bytes;
279
- for ( k = mc->ndim - 1; k >= 0; k-- ) {
280
- scratch_strides[k] = s;
281
- s *= counts[k];
282
- }
283
- scratch = ca_lazy_arena_acquire(slab_n * operand_bytes);
300
+ scratch = ( mc->parent->data_type == CA_OBJECT )
301
+ ? ca_lazy_arena_acquire_object(slab_n)
302
+ : ca_lazy_arena_acquire(slab_n * operand_bytes);
284
303
  ca_moncmp_scratch_acquire_count++;
285
- ca_xfer_stride(mc->parent, starts, counts, scratch_strides, scratch,
304
+ ca_xfer_stride(mc->parent, starts, counts, operand_strides, scratch,
286
305
  CA_XFER_GET);
287
306
  }
288
307
  }
data/ext/ca_obj_monop.c CHANGED
@@ -207,9 +207,9 @@ ca_monop_setup (CAMonOp *ca, CArray *parent, uint16_t op_id)
207
207
 
208
208
  memcpy(ca->dim, parent->dim, parent->ndim * sizeof(ca_size_t));
209
209
 
210
- if ( ca_has_mask(parent) ) {
211
- ca_create_mask(ca);
212
- }
210
+ /* The mask is NOT built here: ca_has_mask creates a view's mask on
211
+ demand from its parent's, so an expression whose mask nobody reads
212
+ never allocates one. */
213
213
 
214
214
  if ( ca_is_scalar(parent) ) {
215
215
  ca_set_flag(ca, CA_FLAG_SCALAR);
@@ -380,8 +380,8 @@ ca_size_t ca_monop_materialise_call_count = 0;
380
380
  but they are unobservable: the output mask is built separately via
381
381
  the attach lifecycle (ca_monop_func_create_mask + parent.mask
382
382
  CARefer), so reads of masked cells return UNDEF regardless of byte
383
- contents. This is the design license stated in CLAUDE.md
384
- §"design premises" ("mask is not a protection mechanism").
383
+ contents. The mask marks cells as undefined; it does not guard their
384
+ bytes, so writing garbage into a masked cell is licensed.
385
385
 
386
386
  A "partial mask slow path" (= a cell-wise branch to skip masked-cell
387
387
  compute) is a possible future micro-optimisation; it is not done
@@ -461,7 +461,9 @@ pull_leaf_with_optional_cast (CArray *leaf, uint16_t innermost_op,
461
461
  }
462
462
  }
463
463
  (void) holder;
464
- scratch = ca_lazy_arena_acquire(total_bytes);
464
+ scratch = ( leaf->data_type == CA_OBJECT )
465
+ ? ca_lazy_arena_acquire_object(slab_n)
466
+ : ca_lazy_arena_acquire(total_bytes);
465
467
  ca_monop_scratch_acquire_count++;
466
468
  ca_xfer_stride(leaf, starts, counts, parent_strides, scratch, CA_XFER_GET);
467
469
 
@@ -615,7 +617,9 @@ ca_monop_func_xfer_stride (void *ap, ca_size_t *starts, ca_size_t *counts,
615
617
  → data (in target data_type). Required because in-place cast
616
618
  would overlap source/dest reads when target_bytes > src_bytes
617
619
  (forward-walk reads cells past their own write boundary). */
618
- scratch = ca_lazy_arena_acquire(total_bytes);
620
+ scratch = ( cur_dt == CA_OBJECT )
621
+ ? ca_lazy_arena_acquire_object(slab_n)
622
+ : ca_lazy_arena_acquire(total_bytes);
619
623
  ca_monop_scratch_acquire_count++;
620
624
  memcpy(scratch, data, total_bytes);
621
625
  ca_cast_block(slab_n, &src_stub, scratch, &dst_stub, data);
@@ -728,6 +732,14 @@ ca_monop_func_attach (void *ap)
728
732
  s = ca->bytes;
729
733
  for ( k = ca->ndim - 1; k >= 0; k-- ) { native[k] = s; s *= ca->dim[k]; }
730
734
  for ( k = 0; k < ca->ndim; k++ ) starts[k] = 0;
735
+ /* CA_OBJECT cells are VALUEs and this buffer is about to be marked as
736
+ soon as the view is, so it must not be handed to the GC as raw
737
+ xmalloc garbage. */
738
+ if ( ca->data_type == CA_OBJECT ) {
739
+ VALUE *p = (VALUE *) ca->ptr;
740
+ ca_size_t i;
741
+ for ( i = 0; i < ca->elements; i++ ) *p++ = Qnil;
742
+ }
731
743
  ca_monop_func_xfer_stride(ca, starts, ca->dim, native, ca->ptr, CA_XFER_GET);
732
744
  }
733
745
 
data/ext/ca_obj_object.c CHANGED
@@ -352,8 +352,11 @@ ca_objmask_func_xfer_stride (void *ap, ca_size_t *starts, ca_size_t *counts,
352
352
  if ( n == 0 ) return;
353
353
 
354
354
  if ( rb_obj_respond_to(ca->array, mid_block, Qtrue) ) {
355
- int aligned = 1;
356
- for ( k = 0; k < ndim; k++ ) {
355
+ /* The request is over the view's addresses, so a transposed / flat
356
+ request is legal; the per-axis copy_block dispatch below would
357
+ misread it. See ca_xfer_stride_request_is_axis_box (carray.h). */
358
+ int aligned = ca_xfer_stride_request_is_axis_box(ca, starts, counts, strides);
359
+ for ( k = 0; aligned && k < ndim; k++ ) {
357
360
  if ( strides[k] <= 0 || strides[k] % native[k] != 0 ) { aligned = 0; break; }
358
361
  steps[k] = strides[k] / native[k];
359
362
  }
@@ -735,6 +738,14 @@ ca_object_dispatch_fill (CAObject *ca, void *ptr)
735
738
  volatile VALUE rval = rb_ca_ptr2obj(ca->self, ptr);
736
739
  rb_funcall(ca->self, rb_intern("fill_data"), 1, rval);
737
740
  }
741
+ else if ( rb_obj_respond_to(ca->self, rb_intern("fill_block"), Qtrue)
742
+ || rb_obj_respond_to(ca->self, rb_intern("fill_addrs"), Qtrue) ) {
743
+ /* An author who wrote the region slots but not fill_data would otherwise
744
+ get the per-cell default for the one request that is easiest to batch.
745
+ Hand the whole extent to the region path, which reaches `fill_block` in
746
+ one call or `fill_addrs` in address windows. */
747
+ ca_fill_stride_whole(ca, ptr);
748
+ }
738
749
  else {
739
750
  ca_size_t addr;
740
751
  for ( addr = 0; addr < ca->elements; addr++ ) {
@@ -937,8 +948,11 @@ ca_object_func_xfer_stride (void *ap, ca_size_t *starts, ca_size_t *counts,
937
948
  before the % check
938
949
  - given %==0 + >0, strides/native >= 1 holds automatically */
939
950
  if ( rb_obj_respond_to(ca->self, mid_block, Qtrue) ) {
940
- int aligned = 1;
941
- for ( k = 0; k < ndim; k++ ) {
951
+ /* The request is over the view's addresses, so a transposed / flat
952
+ request is legal; the per-axis copy_block dispatch below would
953
+ misread it. See ca_xfer_stride_request_is_axis_box (carray.h). */
954
+ int aligned = ca_xfer_stride_request_is_axis_box(ca, starts, counts, strides);
955
+ for ( k = 0; aligned && k < ndim; k++ ) {
942
956
  if ( strides[k] <= 0 || strides[k] % native[k] != 0 ) { aligned = 0; break; }
943
957
  steps[k] = strides[k] / native[k]; /* >= 1 by construction */
944
958
  }
@@ -1001,6 +1015,93 @@ ca_object_func_fill_data (void *ap, void *ptr)
1001
1015
  ca_object_dispatch_fill((CAObject *) ap, ptr);
1002
1016
  }
1003
1017
 
1018
+ /* Partial fill. fill_data carries no region and can only say "fill
1019
+ everything I cover", so before these two slots existed the only way to
1020
+ fill part of a CAObject was the per-cell default -- one store_addr per
1021
+ cell. The region arrives in the view's own address space. With
1022
+ `fill_block` defined (and the region an axis-aligned forward sub-box of
1023
+ self) it becomes one call; with `fill_addrs` defined it becomes one call
1024
+ per address window. With neither defined the behaviour is exactly the
1025
+ old default, so an existing subclass sees no change. */
1026
+ static void
1027
+ ca_object_func_fill_addrs (void *ap, ca_size_t n, ca_size_t *addrs, void *ptr)
1028
+ {
1029
+ CAObject *ca = (CAObject *) ap;
1030
+ ID mid = rb_intern("fill_addrs");
1031
+
1032
+ if ( ca_is_face(ca) ) {
1033
+ ca_face_fill_addrs(ap, n, addrs, ptr);
1034
+ return;
1035
+ }
1036
+
1037
+ if ( n > 0 && rb_obj_respond_to(ca->self, mid, Qtrue) ) {
1038
+ volatile VALUE raddrs, rval;
1039
+ ca_size_t dim1[1] = { n };
1040
+ raddrs = ca_object_wrap_transient(CA_SIZE, sizeof(ca_size_t),
1041
+ 1, dim1, addrs, CA_XFER_PUT);
1042
+ rval = rb_ca_ptr2obj(ca->self, ptr);
1043
+ rb_funcall(ca->self, mid, 2, raddrs, rval);
1044
+ return;
1045
+ }
1046
+
1047
+ ca_fill_addrs_default(ap, n, addrs, ptr);
1048
+ }
1049
+
1050
+ /* Gate: one region axis per view axis, forward, and a whole number of
1051
+ elements per step. native is strictly decreasing, so steps[k] =
1052
+ m_k * native[k] with m_k >= 1 admits only the identity permutation --
1053
+ transpose, negative and zero (broadcast) steps, sub-element steps and
1054
+ dimension-dropping regions all fail it and take the addrs route. The
1055
+ bound check then confirms the decomposed box lies inside self. */
1056
+ static void
1057
+ ca_object_func_fill_stride (void *ap, ca_size_t base, int8_t ndim,
1058
+ ca_size_t *counts, ca_size_t *steps, void *ptr)
1059
+ {
1060
+ CAObject *ca = (CAObject *) ap;
1061
+ ca_size_t native[CA_RANK_MAX], istep[CA_RANK_MAX], start[CA_RANK_MAX];
1062
+ ca_size_t s;
1063
+ int8_t k;
1064
+ ID mid = rb_intern("fill_block");
1065
+
1066
+ if ( ca_is_face(ca) ) {
1067
+ ca_face_fill_stride(ap, base, ndim, counts, steps, ptr);
1068
+ return;
1069
+ }
1070
+
1071
+ if ( ndim == ca->ndim && rb_obj_respond_to(ca->self, mid, Qtrue) ) {
1072
+ int aligned = 1;
1073
+ s = 1;
1074
+ for ( k = ca->ndim - 1; k >= 0; k-- ) { native[k] = s; s *= ca->dim[k]; }
1075
+ for ( k = 0; k < ndim; k++ ) {
1076
+ if ( steps[k] <= 0 || steps[k] % native[k] != 0 ) { aligned = 0; break; }
1077
+ istep[k] = steps[k] / native[k];
1078
+ start[k] = ( base / native[k] ) % ca->dim[k];
1079
+ if ( start[k] + ( counts[k] - 1 ) * istep[k] >= ca->dim[k] ) {
1080
+ aligned = 0;
1081
+ break;
1082
+ }
1083
+ }
1084
+ if ( aligned ) {
1085
+ volatile VALUE rstarts, rcounts, rsteps, rval;
1086
+ rstarts = rb_ary_new_capa(ndim);
1087
+ rcounts = rb_ary_new_capa(ndim);
1088
+ rsteps = rb_ary_new_capa(ndim);
1089
+ for ( k = 0; k < ndim; k++ ) {
1090
+ rb_ary_push(rstarts, SIZE2NUM(start[k]));
1091
+ rb_ary_push(rcounts, SIZE2NUM(counts[k]));
1092
+ rb_ary_push(rsteps, SIZE2NUM(istep[k]));
1093
+ }
1094
+ rval = rb_ca_ptr2obj(ca->self, ptr);
1095
+ rb_funcall(ca->self, mid, 4, rstarts, rcounts, rsteps, rval);
1096
+ return;
1097
+ }
1098
+ }
1099
+
1100
+ /* addrs route: address windows -> ca_fill_addrs -> `fill_addrs` when the
1101
+ author defined it, else the per-cell default. */
1102
+ ca_fill_stride_via_addrs(ap, base, ndim, counts, steps, ptr);
1103
+ }
1104
+
1004
1105
  static void
1005
1106
  ca_object_func_create_mask (void *ap)
1006
1107
  {
@@ -1050,6 +1151,8 @@ ca_operation_function_t ca_object_func = {
1050
1151
  NULL, /* fold_stride: never-fold (callback boundary) */
1051
1152
  ca_object_func_xfer_stride,
1052
1153
  ca_object_func_xfer_all,
1154
+ .fill_addrs = ca_object_func_fill_addrs,
1155
+ .fill_stride = ca_object_func_fill_stride,
1053
1156
  };
1054
1157
 
1055
1158
  /* ------------------------------------------------------------------- */
data/ext/ca_obj_record.c CHANGED
@@ -202,7 +202,9 @@ ca_operation_function_t ca_record_func = {
202
202
  ca_face_xfer_addrs,
203
203
  NULL, /* fold_stride: identity Face is not foldable */
204
204
  ca_face_xfer_stride,
205
- ca_face_xfer_all
205
+ ca_face_xfer_all,
206
+ .fill_addrs = ca_face_fill_addrs,
207
+ .fill_stride = ca_face_fill_stride,
206
208
  };
207
209
 
208
210
  /* ------------------------------------------------------------------- */