carray 3.0.1 → 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 (104) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +520 -0
  3. data/README.md +2 -2
  4. data/carray.gemspec +1 -1
  5. data/ext/ca_axis_dispatch.c +33 -4
  6. data/ext/ca_axis_group.c +202 -96
  7. data/ext/ca_categorical_iterator.c +108 -54
  8. data/ext/ca_kernel_iterator.c +317 -51
  9. data/ext/ca_kernel_iterator.h +142 -35
  10. data/ext/ca_obj_array.c +62 -20
  11. data/ext/ca_obj_block.c +4 -4
  12. data/ext/ca_obj_const_string.c +85 -26
  13. data/ext/ca_obj_face.c +24 -0
  14. data/ext/ca_obj_face.h +15 -0
  15. data/ext/ca_obj_fixlen_string.c +18 -5
  16. data/ext/ca_obj_meld.c +123 -25
  17. data/ext/ca_obj_object.c +8 -0
  18. data/ext/ca_obj_select.c +49 -34
  19. data/ext/ca_obj_stack.c +3 -8
  20. data/ext/ca_obj_stride.c +72 -1
  21. data/ext/ca_obj_string.c +8 -4
  22. data/ext/ca_obj_window.c +8 -2
  23. data/ext/ca_op_ipower.c +1 -2
  24. data/ext/ca_rng_normal.h +42 -0
  25. data/ext/ca_rng_xoshiro256pp.h +105 -0
  26. data/ext/ca_sweep_engine.c +307 -143
  27. data/ext/ca_sweep_engine.h +26 -5
  28. data/ext/carray.h +21 -2
  29. data/ext/carray_access.c +32 -20
  30. data/ext/carray_address_basis.c +590 -0
  31. data/ext/carray_broadcast.c +3 -3
  32. data/ext/carray_call_cfunc.c +667 -483
  33. data/ext/carray_cast.c +115 -41
  34. data/ext/carray_copy.c +55 -30
  35. data/ext/carray_core.c +83 -3
  36. data/ext/carray_count.c +9 -10
  37. data/ext/carray_factorize.c +46 -25
  38. data/ext/carray_internal.h +17 -0
  39. data/ext/carray_kernels_reduce_aggregate.c +168 -0
  40. data/ext/carray_kernels_reduce_cumulative.c +270 -1
  41. data/ext/carray_kernels_reduce_extreme.c +554 -8
  42. data/ext/carray_kernels_scan.c +4 -4
  43. data/ext/carray_kernels_search.c +94 -14
  44. data/ext/carray_loop.c +7 -1
  45. data/ext/carray_mask.c +23 -8
  46. data/ext/carray_median_percentile.c +55 -0
  47. data/ext/carray_operator.c +4 -4
  48. data/ext/carray_order.c +1 -1
  49. data/ext/carray_random.c +384 -40
  50. data/ext/carray_slab.c +13 -0
  51. data/ext/carray_sort.c +20 -22
  52. data/ext/mk_call_cfunc.rb +103 -116
  53. data/ext/mkkernel.rb +297 -29
  54. data/ext/ruby_carray.c +10 -1
  55. data/ext/version.h +4 -4
  56. data/lib/carray/autoload_carray.rb +5 -3
  57. data/lib/carray/autoload_method_extension.rb +12 -0
  58. data/lib/carray/axis_group.rb +77 -0
  59. data/lib/carray/basics.rb +4 -0
  60. data/lib/carray/block_iterator.rb +92 -16
  61. data/lib/carray/categorical.rb +150 -33
  62. data/lib/carray/categorical_iterator.rb +207 -80
  63. data/lib/carray/const_string.rb +131 -27
  64. data/lib/carray/construct.rb +40 -0
  65. data/lib/carray/data_type_extension.rb +3 -0
  66. data/lib/carray/data_type_limits.rb +91 -0
  67. data/lib/carray/fixlen_string.rb +1 -1
  68. data/lib/carray/frame/csv_parser.rb +11 -4
  69. data/lib/carray/frame/frame.rb +81 -10
  70. data/lib/carray/frame/group.rb +36 -3
  71. data/lib/carray/frame/io.rb +67 -15
  72. data/lib/carray/frame/records.rb +18 -4
  73. data/lib/carray/frame/verbs.rb +14 -11
  74. data/lib/carray/inspect.rb +42 -9
  75. data/lib/carray/iterator.rb +143 -0
  76. data/lib/carray/lazy.rb +0 -37
  77. data/lib/carray/mask_gap_fill.rb +3 -1
  78. data/lib/carray/methods/discovery_along.rb +74 -0
  79. data/lib/carray/methods/factorize.rb +50 -0
  80. data/lib/carray/methods/is_in.rb +13 -2
  81. data/lib/carray/methods/locate_addr.rb +75 -2
  82. data/lib/carray/methods/mask_duplicates.rb +35 -1
  83. data/lib/carray/methods/nunique.rb +22 -1
  84. data/lib/carray/methods/repeat.rb +110 -0
  85. data/lib/carray/methods/unique.rb +41 -1
  86. data/lib/carray/rng.rb +86 -0
  87. data/lib/carray/slab_iterator.rb +58 -13
  88. data/lib/carray/string_operation_extension.rb +5 -1
  89. data/lib/carray/time.rb +18 -2
  90. data/lib/carray/window_iterator.rb +142 -20
  91. data/lib/carray.rb +2 -0
  92. data/yard-stubs/ca_obj_block.rb +2 -7
  93. data/yard-stubs/ca_obj_window.rb +10 -2
  94. data/yard-stubs/carray_access.rb +1 -1
  95. data/yard-stubs/carray_broadcast.rb +1 -1
  96. data/yard-stubs/carray_core.rb +0 -80
  97. data/yard-stubs/carray_count.rb +7 -2
  98. data/yard-stubs/carray_lazy.rb +205 -0
  99. data/yard-stubs/carray_math.rb +1486 -3
  100. data/yard-stubs/carray_median_percentile.rb +16 -2
  101. data/yard-stubs/carray_order.rb +9 -69
  102. data/yard-stubs/carray_slab.rb +9 -7
  103. data/yard-stubs/carray_sort.rb +7 -5
  104. metadata +9 -1
data/ext/ca_obj_face.h CHANGED
@@ -259,6 +259,21 @@ void ca_face_register_state_portable (int obj_type, int portable);
259
259
  (= falls through to default). */
260
260
  int ca_face_state_portable (int obj_type, VALUE klass);
261
261
 
262
+ /* Bring a Face *operand* into the reference's storage space, or refuse.
263
+
264
+ The gate asks `fz_face_descend`'s question -- "is your storage your
265
+ surface?" -- of the receiver, and for a long time asked nothing at all of
266
+ the operand: a COMPARABLE receiver stripped any Face handed to it, and got
267
+ back that Face's encoding rather than its values. Where the two encodings
268
+ happened to be the same width the comparison then answered, wrongly and
269
+ silently. Call this instead of stripping an operand by hand.
270
+
271
+ A Face passes only if it declares COMPARABLE_STORAGE, i.e. its storage
272
+ cells *are* the values it shows. Anything else -- a byte range, a code, a
273
+ tick -- is refused rather than compared as bytes. Non-Face operands are
274
+ returned untouched. `name` opens the message. */
275
+ VALUE ca_face_operand_descend (VALUE operand, const char *name);
276
+
262
277
  #define CA_FACE_STORAGE_TO_SCALAR_IF_FACE(obj, self, ca) do { \
263
278
  if ( ca_is_face(ca) && (obj) != CA_UNDEF && (obj) != Qnil \
264
279
  && ! rb_obj_is_kind_of((obj), rb_cCArray) ) { \
@@ -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
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 */
@@ -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
 
@@ -601,21 +616,6 @@ ca_meld_func_xfer_stride (void *ap, ca_size_t *starts, ca_size_t *counts,
601
616
  s = ca->bytes;
602
617
  for ( i = ca->ndim - 1; i >= 0; i-- ) { native[i] = s; s *= ca->dim[i]; }
603
618
 
604
- /* Bound check on meld_axis. The step along the axis is strides[ma] /
605
- native[ma] and it can be negative -- a reversed read starts at the far
606
- end and walks down -- so the region runs between the first and last
607
- index, which is not the same as starts[ma] .. starts[ma] + counts[ma]. */
608
- {
609
- ca_size_t step = strides[ma] / native[ma];
610
- ca_size_t last = starts[ma] + (counts[ma] - 1) * step;
611
- ca_size_t req_lo = ( last < starts[ma] ) ? last : starts[ma];
612
- ca_size_t req_hi = (( last < starts[ma] ) ? starts[ma] : last) + 1;
613
- if ( counts[ma] > 0 && ( req_lo < 0 || req_hi > ca->dim[ma] ) ) {
614
- rb_raise(rb_eIndexError,
615
- "CAMeld xfer_stride meld_axis (axis %d) [%lld, %lld) out of range [0, %lld)",
616
- (int) ma, (long long) req_lo, (long long) req_hi, (long long) ca->dim[ma]);
617
- }
618
- }
619
619
  for ( i = 0; i < ca->ndim; i++ ) {
620
620
  if ( strides[i] != native[i] ) { structural = 0; break; }
621
621
  }
@@ -624,6 +624,23 @@ ca_meld_func_xfer_stride (void *ap, ca_size_t *starts, ca_size_t *counts,
624
624
  ca_meld_xfer_stride_per_cell(ca, starts, counts, strides, data, dir);
625
625
  return;
626
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
+
627
644
  if ( ma == 0 ) {
628
645
  ca_meld_xfer_stride_ma0(ca, starts, counts, strides, data, dir);
629
646
  } else {
@@ -770,10 +787,7 @@ static void
770
787
  ca_meld_func_allocate (void *ap)
771
788
  {
772
789
  CAMeld *ca = (CAMeld *) ap;
773
- int32_t k;
774
- for ( k = 0; k < ca->n_parents; k++ ) {
775
- ca_attach(ca->parents[k]);
776
- }
790
+ ca_attach_all(ca->parents, ca->n_parents);
777
791
  ca->ptr = xmalloc(ca_length(ca));
778
792
  }
779
793
 
@@ -781,10 +795,7 @@ static void
781
795
  ca_meld_func_attach (void *ap)
782
796
  {
783
797
  CAMeld *ca = (CAMeld *) ap;
784
- int32_t k;
785
- for ( k = 0; k < ca->n_parents; k++ ) {
786
- ca_attach(ca->parents[k]);
787
- }
798
+ ca_attach_all(ca->parents, ca->n_parents);
788
799
  ca->ptr = xmalloc(ca_length(ca));
789
800
  ca_meld_func_xfer_all(ca, ca->ptr, CA_XFER_GET);
790
801
  }
@@ -887,6 +898,17 @@ ca_operation_function_t ca_meld_func = {
887
898
  /* Ruby surface */
888
899
  /* ------------------------------------------------------------------- */
889
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
+
890
912
  VALUE
891
913
  rb_ca_meld_new (VALUE parents_ary, int8_t meld_axis)
892
914
  {
@@ -910,7 +932,7 @@ rb_ca_meld_new (VALUE parents_ary, int8_t meld_axis)
910
932
  ca = ca_meld_new((int32_t) n, parents, meld_axis);
911
933
  obj = ca_wrap_struct(ca);
912
934
  rb_ivar_set(obj, id_parents, rb_ary_dup(parents_ary));
913
- 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)));
914
936
  ALLOCV_END(holder);
915
937
  return obj;
916
938
  }
@@ -967,11 +989,86 @@ rb_ca_meld_initialize (int argc, VALUE *argv, VALUE self)
967
989
  }
968
990
  ca_meld_setup(ca, (int32_t) n, parents, meld_axis);
969
991
  rb_ivar_set(self, id_parents, rb_ary_dup(list));
970
- 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)));
971
993
  ALLOCV_END(holder);
972
994
  return self;
973
995
  }
974
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
+
975
1072
  static VALUE
976
1073
  rb_ca_meld_n_parents (VALUE self)
977
1074
  {
@@ -1027,6 +1124,7 @@ Init_ca_obj_meld (void)
1027
1124
  id_parents = rb_intern("parents");
1028
1125
 
1029
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);
1030
1128
  rb_define_method(rb_cCAMeld, "initialize",
1031
1129
  rb_ca_meld_initialize, -1);
1032
1130
  rb_define_method(rb_cCAMeld, "initialize_copy",
data/ext/ca_obj_object.c CHANGED
@@ -738,6 +738,14 @@ ca_object_dispatch_fill (CAObject *ca, void *ptr)
738
738
  volatile VALUE rval = rb_ca_ptr2obj(ca->self, ptr);
739
739
  rb_funcall(ca->self, rb_intern("fill_data"), 1, rval);
740
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
+ }
741
749
  else {
742
750
  ca_size_t addr;
743
751
  for ( addr = 0; addr < ca->elements; addr++ ) {
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
  }
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
@@ -836,6 +836,59 @@ ca_stride_region_axes (CAStride *ca, ca_size_t base, int8_t ndim,
836
836
  root in units root does not share, so there is no address to hand over
837
837
  and the per-cell descent stands in. */
838
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
+
839
892
  static void
840
893
  ca_stride_func_fill_stride (void *ap, ca_size_t base, int8_t ndim,
841
894
  ca_size_t *counts, ca_size_t *steps, void *ptr)
@@ -1003,6 +1056,15 @@ ca_stride_func_allocate (void *ap)
1003
1056
  }
1004
1057
  }
1005
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
+
1006
1068
  static void
1007
1069
  ca_stride_func_attach (void *ap)
1008
1070
  {
@@ -1018,7 +1080,15 @@ ca_stride_func_attach (void *ap)
1018
1080
  view with a live ptr is what makes the per-cell dispatchers bypass the
1019
1081
  transfer slots. */
1020
1082
  char *buf = xmalloc(ca_length(ca));
1021
- 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
+ }
1022
1092
  ca->ptr = buf;
1023
1093
  return;
1024
1094
  }
@@ -1483,6 +1553,7 @@ ca_operation_function_t ca_stride_func = {
1483
1553
  sizeof(CAStride), /* struct_size: pool framework */
1484
1554
  ca_stride_pool_bytes, /* pool_bytes */
1485
1555
  ca_stride_pool_init, /* pool_init */
1556
+ .fill_addrs = ca_stride_func_fill_addrs,
1486
1557
  .fill_stride = ca_stride_func_fill_stride,
1487
1558
  };
1488
1559
 
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;
data/ext/ca_obj_window.c CHANGED
@@ -1416,6 +1416,12 @@ rb_ca_window (int argc, VALUE *argv, VALUE self)
1416
1416
  }
1417
1417
 
1418
1418
  if ( ! NIL_P(rbounds) ) {
1419
+ /* A Symbol says the same thing as the String, and is what the window
1420
+ iterator (CArray#windows) is given -- accept both so the two spellings
1421
+ of one policy do not diverge. */
1422
+ if ( SYMBOL_P(rbounds) ) {
1423
+ rbounds = rb_sym2str(rbounds);
1424
+ }
1419
1425
  switch ( TYPE(rbounds) ) {
1420
1426
  case T_STRING:
1421
1427
  cbounds = StringValuePtr(rbounds);
@@ -1597,7 +1603,6 @@ rb_ca_window_get_bounds (VALUE self)
1597
1603
  }
1598
1604
 
1599
1605
  static VALUE rb_cw_get_attr_ary(start);
1600
- static VALUE rb_cw_get_attr_ary(count);
1601
1606
  static VALUE rb_cw_get_attr_ary(size0);
1602
1607
 
1603
1608
  #ifdef CARRAY_DEV_BUILD
@@ -1680,8 +1685,9 @@ Init_ca_obj_window (void)
1680
1685
 
1681
1686
  rb_define_method(rb_cCAWindow, "bounds", rb_ca_window_get_bounds, 0);
1682
1687
 
1688
+ /* No `count` accessor: the per-axis window widths are what `shape`
1689
+ already answers, and the name belongs to CArray#count. */
1683
1690
  rb_define_method(rb_cCAWindow, "start", rb_cw_start, 0);
1684
- rb_define_method(rb_cCAWindow, "count", rb_cw_count, 0);
1685
1691
  rb_define_method(rb_cCAWindow, "size0", rb_cw_size0, 0);
1686
1692
 
1687
1693
  #ifdef CARRAY_DEV_BUILD
data/ext/ca_op_ipower.c CHANGED
@@ -273,8 +273,7 @@ rb_ca_ipower_bang (VALUE self, VALUE other)
273
273
  /* CArray#pow(other) (alias `**`) — Float/Complex ** Integer takes the
274
274
  * ipower fast path in this file; everything else falls through to the
275
275
  * mkkernel-generated general pow/cpow (rb_ca_power in
276
- * ext/carray_kernels.c). Non-bang variant preserves UnboundRepeat
277
- * wrapping on the result. */
276
+ * ext/carray_kernels.c). */
278
277
  static VALUE rb_ca_pow (VALUE self, VALUE other)
279
278
  {
280
279
  volatile VALUE obj;
@@ -0,0 +1,42 @@
1
+ /* ---------------------------------------------------------------------------
2
+
3
+ ca_rng_normal.h -- uniform to standard normal, as text
4
+
5
+ Read the two ways ca_rng_xoshiro256pp.h is read: compiled into this
6
+ extension, and handed out through CArray::Rng::COMMON_SOURCE for
7
+ another gem to paste. The same rules apply -- no include guard,
8
+ nothing beyond <math.h>, `static inline` only.
9
+
10
+ Separate from any generator's file because it belongs to none of
11
+ them: it takes two uniforms and gives a normal, whichever generator
12
+ the uniforms came from. Whoever pastes it pastes it once however
13
+ many generators are drawing, which is why it is not simply repeated
14
+ inside each generator's text.
15
+
16
+ --------------------------------------------------------------------------- */
17
+
18
+ /* One standard normal from two uniforms in [0.0, 1.0), by Box-Muller.
19
+ *
20
+ * Exactly two, always. The classical form takes two uniforms and gives
21
+ * two normals, and keeping the second would make the cost one uniform
22
+ * apiece -- but the spare has to live somewhere between calls, and the
23
+ * place it would live is the generator's state. A kernel draws one
24
+ * number per cell and `CArray#random!` fills whole arrays, so a spare
25
+ * held across that boundary is a second kind of state to keep in step,
26
+ * on top of the one this design exists to keep in step. Two uniforms
27
+ * and no spare costs an extra draw, at about a nanosecond, and buys a
28
+ * rule with nothing behind it: one normal is two draws, wherever it is
29
+ * taken.
30
+ *
31
+ * `1.0 - u1` rather than `u1`, so the argument to log is in (0.0, 1.0]
32
+ * and never zero. Redrawing on a zero -- which is what the paired form
33
+ * does -- would make the number of uniforms per normal depend on the
34
+ * draw, and then "where is this generator" has no answer that can be
35
+ * worked out rather than run. */
36
+ static inline double
37
+ ca_rng_normal (double u1, double u2)
38
+ {
39
+ const double radius = sqrt(-2.0 * log(1.0 - u1));
40
+ const double theta = 2.0 * M_PI * u2;
41
+ return radius * cos(theta);
42
+ }