carray 3.0.3 → 3.0.5

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 (221) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +469 -0
  3. data/README.md +3 -2
  4. data/carray.gemspec +1 -1
  5. data/ext/ca_array_pool.c +25 -1
  6. data/ext/ca_axis_descriptor.h +27 -24
  7. data/ext/ca_axis_dispatch.c +14 -26
  8. data/ext/ca_axis_group.c +27 -27
  9. data/ext/ca_binop_dispatch.c +1 -29
  10. data/ext/ca_binop_dispatch.h +0 -1
  11. data/ext/ca_categorical_iterator.c +231 -153
  12. data/ext/ca_compare.c +9 -20
  13. data/ext/ca_composite_dispatch.c +9 -9
  14. data/ext/ca_group_iter.c +16 -17
  15. data/ext/ca_kernel_iterator.c +1240 -2000
  16. data/ext/ca_kernel_iterator.h +502 -519
  17. data/ext/ca_monop_dispatch.c +82 -59
  18. data/ext/ca_monop_dispatch.h +1 -0
  19. data/ext/ca_obj_array.c +152 -45
  20. data/ext/ca_obj_bincmp.c +25 -21
  21. data/ext/ca_obj_binop.c +16 -26
  22. data/ext/ca_obj_bitarray.c +8 -3
  23. data/ext/ca_obj_bitfield.c +63 -49
  24. data/ext/ca_obj_block.c +18 -9
  25. data/ext/ca_obj_byte_swap.c +7 -7
  26. data/ext/ca_obj_const_string.c +188 -22
  27. data/ext/ca_obj_face.c +280 -58
  28. data/ext/ca_obj_face.h +19 -0
  29. data/ext/ca_obj_fake.c +15 -29
  30. data/ext/ca_obj_farray.c +6 -3
  31. data/ext/ca_obj_field.c +30 -8
  32. data/ext/ca_obj_fixlen_string.c +2 -3
  33. data/ext/ca_obj_grid.c +455 -134
  34. data/ext/ca_obj_meld.c +172 -34
  35. data/ext/ca_obj_moncmp.c +23 -8
  36. data/ext/ca_obj_monop.c +113 -67
  37. data/ext/ca_obj_object.c +210 -52
  38. data/ext/ca_obj_record.c +15 -7
  39. data/ext/ca_obj_reduce.c +17 -8
  40. data/ext/ca_obj_refer.c +42 -35
  41. data/ext/ca_obj_remap.c +30 -9
  42. data/ext/ca_obj_repeat.c +9 -10
  43. data/ext/ca_obj_roll.c +31 -10
  44. data/ext/ca_obj_select.c +48 -14
  45. data/ext/ca_obj_select_axis.c +15 -47
  46. data/ext/ca_obj_shift.c +41 -7
  47. data/ext/ca_obj_stack.c +180 -34
  48. data/ext/ca_obj_stride.c +190 -134
  49. data/ext/ca_obj_string.c +80 -4
  50. data/ext/ca_obj_tile.c +32 -11
  51. data/ext/ca_obj_time.c +22 -6
  52. data/ext/ca_obj_timedelta.c +22 -6
  53. data/ext/ca_obj_transpose.c +13 -7
  54. data/ext/ca_obj_triop.c +26 -32
  55. data/ext/ca_obj_window.c +267 -74
  56. data/ext/ca_op_ipower.c +4 -4
  57. data/ext/ca_sort_kernels.h +6 -3
  58. data/ext/ca_sweep_engine.c +2 -2
  59. data/ext/caframe_csv_reader.c +80 -1
  60. data/ext/carray.h +214 -75
  61. data/ext/carray_access.c +200 -99
  62. data/ext/carray_address_basis.c +343 -87
  63. data/ext/carray_attribute.c +113 -6
  64. data/ext/carray_bincount.c +10 -8
  65. data/ext/carray_broadcast.c +13 -8
  66. data/ext/carray_build_flags.h +3 -0
  67. data/ext/carray_call_cfunc.c +108 -0
  68. data/ext/carray_cast.c +375 -96
  69. data/ext/carray_cast_func.rb +90 -20
  70. data/ext/carray_class.c +4 -3
  71. data/ext/carray_config.h +28 -0
  72. data/ext/carray_conversion.c +18 -9
  73. data/ext/carray_copy.c +51 -14
  74. data/ext/carray_core.c +150 -55
  75. data/ext/carray_count.c +56 -20
  76. data/ext/carray_element.c +81 -33
  77. data/ext/carray_factorize.c +138 -84
  78. data/ext/carray_generate.c +199 -52
  79. data/ext/carray_histogram.c +63 -18
  80. data/ext/carray_hold.c +12 -15
  81. data/ext/carray_index_classifier.c +388 -176
  82. data/ext/carray_internal.h +348 -29
  83. data/ext/carray_kernels_bincmp.c +4453 -0
  84. data/ext/carray_kernels_binop.c +11368 -0
  85. data/ext/carray_kernels_init.c +1734 -0
  86. data/ext/carray_kernels_moncmp.c +2104 -0
  87. data/ext/carray_kernels_monop.c +18369 -0
  88. data/ext/carray_kernels_reduce_aggregate.c +23818 -0
  89. data/ext/carray_kernels_reduce_boolean.c +406 -0
  90. data/ext/carray_kernels_reduce_cumulative.c +10086 -0
  91. data/ext/carray_kernels_reduce_extreme.c +17634 -0
  92. data/ext/carray_kernels_reduce_variance.c +3849 -0
  93. data/ext/carray_kernels_scan.c +4302 -0
  94. data/ext/carray_kernels_search.c +33829 -0
  95. data/ext/carray_kernels_sort.c +10897 -0
  96. data/ext/carray_kernels_triop.c +1864 -0
  97. data/ext/carray_key_windows.c +220 -0
  98. data/ext/carray_lazy.c +170 -111
  99. data/ext/carray_mask.c +337 -94
  100. data/ext/carray_median_percentile.c +82 -28
  101. data/ext/carray_memory_view.c +669 -561
  102. data/ext/carray_operator.c +129 -52
  103. data/ext/carray_order.c +604 -77
  104. data/ext/carray_partition.c +28 -27
  105. data/ext/carray_random.c +85 -31
  106. data/ext/carray_round.c +273 -0
  107. data/ext/carray_scatter.c +87 -45
  108. data/ext/carray_segment.c +3 -3
  109. data/ext/carray_slab.c +123 -37
  110. data/ext/carray_sort.c +259 -51
  111. data/ext/carray_sort_kernel.c +5 -7
  112. data/ext/carray_struct.c +54 -61
  113. data/ext/carray_test.c +26 -5
  114. data/ext/carray_time_text.c +4 -2
  115. data/ext/carray_undef.c +4 -0
  116. data/ext/carray_utils.c +309 -66
  117. data/ext/mk_call_cfunc.rb +19 -2
  118. data/ext/mkkernel.rb +403 -350
  119. data/ext/ruby_carray.c +10 -4
  120. data/ext/version.h +4 -4
  121. data/lib/carray/attribute.rb +139 -22
  122. data/lib/carray/autoload_carray.rb +8 -31
  123. data/lib/carray/autoload_method_extension.rb +14 -5
  124. data/lib/carray/axis_group.rb +255 -106
  125. data/lib/carray/basics.rb +105 -17
  126. data/lib/carray/bincount_nd.rb +12 -2
  127. data/lib/carray/block_iterator.rb +126 -23
  128. data/lib/carray/boolean_reduce.rb +17 -17
  129. data/lib/carray/categorical.rb +195 -59
  130. data/lib/carray/categorical_iterator.rb +132 -29
  131. data/lib/carray/complex.rb +5 -5
  132. data/lib/carray/conditional.rb +67 -18
  133. data/lib/carray/const_string.rb +24 -5
  134. data/lib/carray/construct.rb +15 -11
  135. data/lib/carray/data_type_extension.rb +9 -1
  136. data/lib/carray/distance_limit.rb +77 -0
  137. data/lib/carray/fixlen_string.rb +48 -3
  138. data/lib/carray/frame/column_verbs.rb +71 -0
  139. data/lib/carray/frame/concat.rb +17 -3
  140. data/lib/carray/frame/convert.rb +69 -0
  141. data/lib/carray/frame/csv_parser.rb +111 -17
  142. data/lib/carray/frame/describe.rb +3 -2
  143. data/lib/carray/frame/frame.rb +252 -28
  144. data/lib/carray/frame/group.rb +603 -101
  145. data/lib/carray/frame/io.rb +117 -28
  146. data/lib/carray/frame/join.rb +312 -51
  147. data/lib/carray/frame/records.rb +83 -7
  148. data/lib/carray/frame/reshape.rb +42 -35
  149. data/lib/carray/frame/sort.rb +7 -19
  150. data/lib/carray/frame/verbs.rb +188 -48
  151. data/lib/carray/frame/windows.rb +117 -0
  152. data/lib/carray/frame.rb +2 -0
  153. data/lib/carray/fuse_source.rb +42 -6
  154. data/lib/carray/fusion.rb +40 -16
  155. data/lib/carray/histogram.rb +6 -1
  156. data/lib/carray/inspect.rb +31 -8
  157. data/lib/carray/iterator.rb +85 -7
  158. data/lib/carray/key_windows.rb +620 -0
  159. data/lib/carray/lazy.rb +217 -183
  160. data/lib/carray/mask_gap_fill.rb +63 -57
  161. data/lib/carray/meld_reduce.rb +12 -14
  162. data/lib/carray/methods/bin.rb +26 -18
  163. data/lib/carray/methods/bincount.rb +16 -0
  164. data/lib/carray/methods/bit_string.rb +1 -1
  165. data/lib/carray/methods/choose.rb +16 -3
  166. data/lib/carray/methods/composition.rb +3 -1
  167. data/lib/carray/methods/gather_nd.rb +14 -5
  168. data/lib/carray/methods/insert_block.rb +2 -2
  169. data/lib/carray/methods/is_between.rb +59 -0
  170. data/lib/carray/methods/is_in.rb +61 -37
  171. data/lib/carray/methods/join.rb +1 -6
  172. data/lib/carray/methods/lag.rb +49 -0
  173. data/lib/carray/methods/locate_addr.rb +47 -51
  174. data/lib/carray/methods/mode.rb +1 -1
  175. data/lib/carray/methods/nunique.rb +11 -6
  176. data/lib/carray/methods/resize.rb +3 -3
  177. data/lib/carray/methods/value_counts.rb +4 -11
  178. data/lib/carray/record_order.rb +3 -3
  179. data/lib/carray/rng.rb +3 -0
  180. data/lib/carray/runtime.rb +53 -8
  181. data/lib/carray/segment_iterator.rb +323 -88
  182. data/lib/carray/serialize.rb +45 -14
  183. data/lib/carray/slab_iterator.rb +50 -0
  184. data/lib/carray/stack.rb +15 -12
  185. data/lib/carray/string.rb +113 -5
  186. data/lib/carray/string_operation_extension.rb +21 -6
  187. data/lib/carray/struct.rb +9 -21
  188. data/lib/carray/struct_builder.rb +24 -11
  189. data/lib/carray/time.rb +819 -158
  190. data/lib/carray/window_iterator.rb +122 -16
  191. data/lib/carray.rb +7 -0
  192. data/yard-stubs/ca_obj_bitfield.rb +1 -1
  193. data/yard-stubs/ca_obj_field.rb +4 -0
  194. data/yard-stubs/ca_obj_grid.rb +21 -10
  195. data/yard-stubs/ca_obj_refer.rb +2 -1
  196. data/yard-stubs/ca_obj_roll.rb +2 -2
  197. data/yard-stubs/ca_obj_shift.rb +5 -1
  198. data/yard-stubs/ca_obj_tile.rb +1 -1
  199. data/yard-stubs/ca_obj_transpose.rb +5 -4
  200. data/yard-stubs/ca_obj_window.rb +8 -3
  201. data/yard-stubs/carray_access.rb +6 -1
  202. data/yard-stubs/carray_attribute.rb +3 -6
  203. data/yard-stubs/carray_cast.rb +12 -0
  204. data/yard-stubs/carray_conversion.rb +7 -2
  205. data/yard-stubs/carray_copy.rb +4 -2
  206. data/yard-stubs/carray_count.rb +4 -4
  207. data/yard-stubs/carray_element.rb +17 -10
  208. data/yard-stubs/carray_generate.rb +21 -1
  209. data/yard-stubs/carray_mask.rb +32 -3
  210. data/yard-stubs/carray_math.rb +41 -5
  211. data/yard-stubs/carray_median_percentile.rb +5 -2
  212. data/yard-stubs/carray_memory_view.rb +40 -23
  213. data/yard-stubs/carray_order.rb +129 -22
  214. data/yard-stubs/carray_random.rb +13 -4
  215. data/yard-stubs/carray_scatter.rb +17 -10
  216. data/yard-stubs/carray_slab.rb +9 -2
  217. data/yard-stubs/carray_sort.rb +6 -5
  218. data/yard-stubs/carray_test.rb +5 -3
  219. metadata +25 -3
  220. data/lib/carray/arrow_tensor.rb +0 -401
  221. data/lib/carray/table.rb +0 -108
data/ext/carray_access.c CHANGED
@@ -76,7 +76,7 @@ rb_ca_fetch_index (VALUE self, ca_size_t *idx)
76
76
  volatile VALUE holder;
77
77
  char *v = ALLOCV_N(char, holder, ca->bytes);
78
78
  ca_fetch_index(ca, idx, v);
79
- out = rb_ca_ptr2obj(self, v);
79
+ out = rb_ca_ptr2obj_storage(self, v);
80
80
  ALLOCV_END(holder);
81
81
  }
82
82
 
@@ -94,7 +94,7 @@ rb_ca_fetch_index (VALUE self, ca_size_t *idx)
94
94
  CA_FACE_STORAGE_TO_ELEMENT_IF_FACE(out, self, ca);
95
95
 
96
96
  /* Boolean scalar access yields true/false, not Integer 0/1. Bulk paths
97
- (to_a / cast / serialize via rb_ca_ptr2obj) keep 0/1. Gate on the raw
97
+ (cast / serialize) keep 0/1. Gate on the raw
98
98
  INT2FIX(0/1) so a boolean-storage Face that decoded to its own object is
99
99
  left untouched; note INT2FIX(0) is truthy in Ruby, so map by value, not
100
100
  by RTEST. */
@@ -158,7 +158,7 @@ rb_ca_fetch_addr (VALUE self, ca_size_t addr)
158
158
  volatile VALUE holder;
159
159
  char *v = ALLOCV_N(char, holder, ca->bytes);
160
160
  ca_fetch_addr(ca, addr, v);
161
- out = rb_ca_ptr2obj(self, v);
161
+ out = rb_ca_ptr2obj_storage(self, v);
162
162
  ALLOCV_END(holder);
163
163
  }
164
164
 
@@ -193,6 +193,14 @@ rb_ca_fill (VALUE self, VALUE rval)
193
193
  TypedData_Get_Struct(self, CArray, &carray_data_type, ca);
194
194
 
195
195
  if ( ca_is_empty(ca) ) {
196
+ /* No cell to fill, but a value no cell could hold is refused, as an
197
+ assignment to the empty array refuses it. */
198
+ if ( rval != CA_UNDEF && ca->bytes > 0 ) {
199
+ volatile VALUE holder;
200
+ char *fval = ALLOCV_N(char, holder, ca->bytes);
201
+ rb_ca_obj2ptr(self, rval, fval);
202
+ ALLOCV_END(holder);
203
+ }
196
204
  return self; /* empty = no-op fill; return self for chaining */
197
205
  }
198
206
 
@@ -229,9 +237,7 @@ static void
229
237
  ary_guess_shape (VALUE ary, int level, int *max_level, ca_size_t *dim)
230
238
  {
231
239
  volatile VALUE ary0;
232
- if ( level > CA_RANK_MAX ) {
233
- rb_raise(rb_eArgError, "too deep level array for conversion to carray");
234
- }
240
+ ca_check_rank_fits(level + 1, NULL);
235
241
 
236
242
  if ( TYPE(ary) == T_ARRAY ) {
237
243
  if ( RARRAY_LEN(ary) == 0 && level == 0 ) {
@@ -476,26 +482,6 @@ ca_id_expression_evaluator (void)
476
482
  return id;
477
483
  }
478
484
 
479
- /* `a[:sym, args] = v` computes the index by calling Ruby with self held
480
- attached for the reads, then stores through it. */
481
-
482
- typedef struct {
483
- VALUE self;
484
- VALUE dest; /* what is stored into: self, or a Face's storage */
485
- ID mid;
486
- int argc;
487
- VALUE *argv;
488
- VALUE rval;
489
- } ca_store_method_call_ctx_t;
490
-
491
- static VALUE
492
- ca_store_method_call_body (VALUE arg)
493
- {
494
- ca_store_method_call_ctx_t *c = (ca_store_method_call_ctx_t *) arg;
495
- volatile VALUE idx = rb_funcallv_public(c->self, c->mid, c->argc, c->argv);
496
- return rb_ca_store(c->dest, idx, c->rval);
497
- }
498
-
499
485
  /* The casting stores below convert into the destination inside a window,
500
486
  and a conversion from Ruby objects can raise part way. Such a window
501
487
  attaches rather than allocates: the cells converted before the raise
@@ -586,6 +572,10 @@ ca_store_all_list_body (VALUE arg)
586
572
  (boolean8_t*)ca->mask->ptr);
587
573
  }
588
574
  else {
575
+ /* Every cell gets a value, so none stays masked. */
576
+ if ( ca->mask ) {
577
+ memset(ca->mask->ptr, 0, ca->elements);
578
+ }
589
579
  ca_cast_block(ca->elements, c->ico, (VALUE *)RARRAY_CONST_PTR(list), cast_target, ca->ptr);
590
580
  }
591
581
  return Qnil;
@@ -654,6 +644,38 @@ ca_face_source_for_store (VALUE self, CArray *ca, VALUE rval)
654
644
  return rb_funcall(rval, rb_intern("to_type"), 1, INT2NUM(CA_OBJECT));
655
645
  }
656
646
 
647
+ /* A store into no cells writes nothing, but the value is checked as it
648
+ would be for any cells: the same statement does not raise or pass
649
+ depending on how many cells the index picks. */
650
+ static void
651
+ ca_store_all_check_empty (VALUE self, VALUE rval)
652
+ {
653
+ CArray *ca;
654
+ TypedData_Get_Struct(self, CArray, &carray_data_type, ca);
655
+ if ( rb_obj_is_cscalar(rval) ) {
656
+ rval = rb_ca_fetch_addr(rval, 0);
657
+ }
658
+ rval = ca_face_source_for_store(self, ca, rval);
659
+ if ( ! rb_obj_is_carray(rval) && TYPE(rval) != T_ARRAY &&
660
+ rb_respond_to(rval, id_to_ca) ) {
661
+ rval = rb_funcall(rval, id_to_ca, 0);
662
+ }
663
+ if ( rb_obj_is_carray(rval) ) {
664
+ ca_broadcast_to_destination(self, (volatile VALUE *) &rval);
665
+ }
666
+ else if ( TYPE(rval) == T_ARRAY ) {
667
+ if ( NIL_P(rb_ary_flatten_for_elements(rval, ca->elements, ca)) ) {
668
+ rb_raise(rb_eArgError, "failed to guess data size of given array");
669
+ }
670
+ }
671
+ else if ( rval != CA_UNDEF ) {
672
+ volatile VALUE holder;
673
+ char *fval = ALLOCV_N(char, holder, ca->bytes);
674
+ rb_ca_obj2ptr(self, rval, fval);
675
+ ALLOCV_END(holder);
676
+ }
677
+ }
678
+
657
679
  VALUE
658
680
  rb_ca_store_all (VALUE self, VALUE rval)
659
681
  {
@@ -661,10 +683,14 @@ rb_ca_store_all (VALUE self, VALUE rval)
661
683
 
662
684
  rb_ca_modify(self);
663
685
 
664
- if ( self == rval || rb_ca_is_empty(self) ) {
686
+ if ( self == rval ) {
687
+ return rval;
688
+ }
689
+ if ( rb_ca_is_empty(self) ) {
690
+ ca_store_all_check_empty(self, rval);
665
691
  return rval;
666
692
  }
667
-
693
+
668
694
  if ( rb_obj_is_cscalar(rval) ) {
669
695
  rval = rb_ca_fetch_addr(rval, 0);
670
696
  }
@@ -689,6 +715,7 @@ rb_ca_store_all (VALUE self, VALUE rval)
689
715
  && ca->data_type == cv->data_type
690
716
  && ca->ndim == cv->ndim
691
717
  && ca->elements == cv->elements
718
+ && ca_in_main_ractor()
692
719
  && RTEST(rb_attr_get(rb_cCArray, ca_id_expression_evaluator())) ) {
693
720
  int8_t k;
694
721
  int same = 1;
@@ -865,9 +892,9 @@ rb_ca_index_restruct_block (int16_t *ndimp, ca_size_t *shrink, ca_size_t *dim,
865
892
 
866
893
  offset = 0;
867
894
 
868
- if ( idx[0] == -1 ) {
869
- j = 0;
870
- while ( idx[++j] ) {
895
+ if ( ndim0 > 0 && idx[0] == -1 ) {
896
+ /* j = the number of leading axes collapsed by a scalar index */
897
+ for (j=1; j<ndim0 && idx[j] == -1; j++) {
871
898
  ;
872
899
  }
873
900
 
@@ -1022,7 +1049,9 @@ extern VALUE rb_ca_remap_new (VALUE cary, VALUE rmapper);
1022
1049
  (b) Otherwise -> normalize chain (a.flatten[mapper.flatten]
1023
1050
  .reshape). A 1-D mapper is already routed to CAGrid upstream.
1024
1051
 
1025
- Both routes raise on a masked mapper. */
1052
+ A masked mapper cell selects nothing: the cell keeps its position and
1053
+ reads as masked (a CAGrid hole); the same-shape fast path is left to a
1054
+ mapper with no masked cell. */
1026
1055
  static VALUE
1027
1056
  rb_ca_fancy_index_chain (VALUE self, VALUE rmapper)
1028
1057
  {
@@ -1034,15 +1063,24 @@ rb_ca_fancy_index_chain (VALUE self, VALUE rmapper)
1034
1063
  TypedData_Get_Struct(self, CArray, &carray_data_type, ca);
1035
1064
  TypedData_Get_Struct(rmapper, CArray, &carray_data_type, cm);
1036
1065
 
1037
- if ( ca_is_any_masked(cm) ) {
1038
- rb_raise(rb_eArgError, "mapper in ca[mapper] should not be masked");
1039
- }
1040
-
1041
- /* (a) same-shape fast path: ndim + per-axis dim + CA_SIZE data_type. */
1066
+ /* (a) same-shape fast path: ndim + per-axis dim + CA_SIZE data_type.
1067
+ The view reads the index array it is given, so it is given a copy: the
1068
+ indices are the ones the caller passed at this call, as every other
1069
+ route reads them, and each is checked against self now, so an index out
1070
+ of range raises here and not at the first read. */
1042
1071
  if ( cm->data_type == CA_SIZE
1072
+ && ! ca_is_any_masked(cm)
1043
1073
  && cm->ndim == ca->ndim
1044
1074
  && memcmp(cm->dim, ca->dim, ca->ndim * sizeof(ca_size_t)) == 0 ) {
1045
- return rb_ca_remap_new(self, rmapper);
1075
+ volatile VALUE snapshot = rb_ca_copy(rmapper);
1076
+ CArray *cs;
1077
+ ca_size_t k, *p;
1078
+ TypedData_Get_Struct(snapshot, CArray, &carray_data_type, cs);
1079
+ p = (ca_size_t *) cs->ptr;
1080
+ for (k = 0; k < cs->elements; k++) {
1081
+ CA_CHECK_INDEX(p[k], ca->elements);
1082
+ }
1083
+ return rb_ca_remap_new(self, snapshot);
1046
1084
  }
1047
1085
 
1048
1086
  /* (b) Normalize chain fallback. A Face is flattened through its storage
@@ -1070,10 +1108,9 @@ rb_ca_fancy_index_chain (VALUE self, VALUE rmapper)
1070
1108
  return result;
1071
1109
  }
1072
1110
 
1073
- /* The CAMapping class is gone; rb_ca_mapping_new and rb_ca_mapping build
1074
- the normalize chain via rb_ca_fancy_index_chain. The public C API
1075
- signatures are preserved so external ext gems keep linking; the runtime
1076
- class of the returned VALUE is CARefer (chain outermost), not CAMapping. */
1111
+ /* rb_ca_mapping_new and rb_ca_mapping index cary by a mapper array through
1112
+ rb_ca_fancy_index_chain: the result is a CAGrid over the flattened array,
1113
+ reshaped (CARefer, the outermost layer) to the mapper's shape. */
1077
1114
 
1078
1115
  VALUE
1079
1116
  rb_ca_mapping_new (VALUE cary, CArray *mapper)
@@ -1099,6 +1136,18 @@ rb_ca_mapping (int argc, VALUE *argv, VALUE self)
1099
1136
  the optimal view class (CAStride for stride bases, CARefer for fancy
1100
1137
  bases). */
1101
1138
 
1139
+ /* a[:name, args] calls a method of the array, and its arguments are the
1140
+ method's, whatever they are: the scans for :_, :> and group indices
1141
+ below look at index keys only. */
1142
+ static int
1143
+ ca_argv_is_method_call (int argc, VALUE *argv)
1144
+ {
1145
+ const char *name;
1146
+ if ( argc < 1 || ! SYMBOL_P(argv[0]) ) return 0;
1147
+ name = rb_id2name(SYM2ID(argv[0]));
1148
+ return ( name != NULL && strlen(name) > 1 );
1149
+ }
1150
+
1102
1151
  static int
1103
1152
  ca_argv_has_newaxis (int argc, VALUE *argv)
1104
1153
  {
@@ -1140,6 +1189,10 @@ rb_ca_fetch_method (int argc, VALUE *argv, VALUE self)
1140
1189
 
1141
1190
  TypedData_Get_Struct(self, CArray, &carray_data_type, ca);
1142
1191
 
1192
+ if ( ca_argv_is_method_call(argc, argv) ) {
1193
+ goto classify;
1194
+ }
1195
+
1143
1196
  /* newaxis (:_) interception (cheap pointer scan; zero overhead beyond
1144
1197
  the loop when no :_ is present). */
1145
1198
  if ( ca_argv_has_newaxis(argc, argv) ) {
@@ -1163,6 +1216,7 @@ rb_ca_fetch_method (int argc, VALUE *argv, VALUE self)
1163
1216
  return rb_ca_fetch_group(argc, argv, self);
1164
1217
  }
1165
1218
 
1219
+ classify:
1166
1220
  info.range_check = 1;
1167
1221
  rb_ca_scan_index(ca->ndim, ca->dim, ca->elements, argc, argv, &info);
1168
1222
 
@@ -1244,11 +1298,6 @@ rb_ca_fetch_method (int argc, VALUE *argv, VALUE self)
1244
1298
  }
1245
1299
  break;
1246
1300
  }
1247
- case CA_REG_ATTRIBUTE: {
1248
- obj = rb_funcall(self, rb_intern("attribute"), 0);
1249
- obj = rb_hash_aref(obj, info.symbol);
1250
- break;
1251
- }
1252
1301
  default:
1253
1302
  rb_raise(rb_eIndexError, "invalid index specified");
1254
1303
  }
@@ -1292,7 +1341,7 @@ rb_ca_fetch_newaxis (int argc, VALUE *argv, VALUE self, CArray *ca)
1292
1341
  nunder++;
1293
1342
  continue;
1294
1343
  }
1295
- if ( FIXNUM_P(a) || RB_TYPE_P(a, T_BIGNUM) ) {
1344
+ if ( ca_index_key_is_scalar(a) ) {
1296
1345
  nscalar++;
1297
1346
  }
1298
1347
  if ( a == sym_gt || a == sym_perc
@@ -1301,7 +1350,7 @@ rb_ca_fetch_newaxis (int argc, VALUE *argv, VALUE self, CArray *ca)
1301
1350
  "newaxis (:_) cannot be combined with :>, :%%, "
1302
1351
  "or rubber dim (false / :~)");
1303
1352
  }
1304
- if ( nclean >= CA_RANK_MAX ) {
1353
+ if ( nclean >= CA_RANK_MAX ) { /* rank-check: a count of indices */
1305
1354
  rb_raise(rb_eIndexError, "too many indices");
1306
1355
  }
1307
1356
  clean[nclean++] = a;
@@ -1324,6 +1373,7 @@ rb_ca_fetch_newaxis (int argc, VALUE *argv, VALUE self, CArray *ca)
1324
1373
  element, which is what the shape rules then hold it to. */
1325
1374
  if ( nscalar == nclean ) {
1326
1375
  VALUE blocks[CA_RANK_MAX];
1376
+ ca_check_rank_fits(nunder, NULL); /* the result has the :_ axes only */
1327
1377
  for (i = 0; i < nclean; i++) {
1328
1378
  blocks[i] = rb_assoc_new(clean[i], INT2FIX(1));
1329
1379
  }
@@ -1338,22 +1388,45 @@ rb_ca_fetch_newaxis (int argc, VALUE *argv, VALUE self, CArray *ca)
1338
1388
 
1339
1389
  TypedData_Get_Struct(base, CArray, &carray_data_type, cb);
1340
1390
 
1391
+ /* The result has the base's axes and one for each :_, before any is
1392
+ written into final_dims. */
1393
+ ca_check_rank_fits((long) nunder + cb->ndim, NULL);
1394
+
1341
1395
  /* Reconstruct final shape: walk original argv. :_ -> size-1, scalar
1342
- (Integer) -> dropped (skip), else -> base.dim[cursor]. */
1343
- base_cursor = 0;
1344
- for (i = 0; i < argc; i++) {
1345
- VALUE a = argv[i];
1346
- if ( a == sym_under ) {
1347
- final_dims[nfinal++] = 1;
1348
- }
1349
- else if ( FIXNUM_P(a) || RB_TYPE_P(a, T_BIGNUM) ) {
1350
- /* scalar index -> axis dropped in base; contributes no output axis */
1396
+ (Integer) -> dropped (skip), else -> base.dim[cursor]. A key gives one
1397
+ axis, except an integer CArray read alone (a mapping), which gives as
1398
+ many as it has: the axes the base has beyond one per key are its. */
1399
+ {
1400
+ int nkeys = 0, extra, ncarray = 0;
1401
+ for (i = 0; i < nclean; i++) {
1402
+ if ( ! ca_index_key_is_scalar(clean[i]) ) {
1403
+ nkeys++;
1404
+ if ( rb_obj_is_carray(clean[i]) ) ncarray++;
1405
+ }
1351
1406
  }
1352
- else {
1353
- if ( base_cursor >= cb->ndim ) {
1354
- rb_raise(rb_eIndexError, "newaxis: base axis count mismatch");
1407
+ extra = cb->ndim - nkeys;
1408
+ base_cursor = ( extra != 0 && ncarray != 1 ) ? -1 : 0;
1409
+ for (i = 0; i < argc && base_cursor >= 0; i++) {
1410
+ VALUE a = argv[i];
1411
+ int take = 1, t;
1412
+ if ( a == sym_under ) {
1413
+ final_dims[nfinal++] = 1;
1414
+ continue;
1415
+ }
1416
+ if ( ca_index_key_is_scalar(a) ) {
1417
+ /* scalar index -> axis dropped in base; contributes no output axis */
1418
+ continue;
1419
+ }
1420
+ if ( rb_obj_is_carray(a) ) {
1421
+ take += extra;
1422
+ }
1423
+ for (t = 0; t < take; t++) {
1424
+ if ( base_cursor >= cb->ndim || nfinal >= CA_RANK_MAX ) { /* rank-check: a count of indices */
1425
+ base_cursor = -1;
1426
+ break;
1427
+ }
1428
+ final_dims[nfinal++] = cb->dim[base_cursor++];
1355
1429
  }
1356
- final_dims[nfinal++] = cb->dim[base_cursor++];
1357
1430
  }
1358
1431
  }
1359
1432
  if ( base_cursor != cb->ndim ) {
@@ -1397,7 +1470,7 @@ rb_ca_slab_iter_new (VALUE self, CAIndexInfo *info)
1397
1470
  align with the post-expansion ndim, replace :> with nil (full axis),
1398
1471
  promote Integer to length-1 Range so the base view preserves ndim,
1399
1472
  recurse through the regular indexer dispatch to build the base
1400
- reference (CABlock / CASelectAxis / CAGrid / CAMapping / ...), then
1473
+ reference (CABlock / CASelectAxis / CAGrid / ...), then
1401
1474
  wrap in CASlabIterator with the recorded slab_axes positions. */
1402
1475
  static VALUE
1403
1476
  rb_ca_fetch_slab_sigil (int argc, VALUE *argv, VALUE self)
@@ -1451,7 +1524,7 @@ rb_ca_fetch_slab_sigil (int argc, VALUE *argv, VALUE self)
1451
1524
  eargv = expanded;
1452
1525
  }
1453
1526
 
1454
- if ( eargc > CA_RANK_MAX ) {
1527
+ if ( eargc > CA_RANK_MAX ) { /* rank-check: a count of indices */
1455
1528
  rb_raise(rb_eIndexError, "too many indices");
1456
1529
  }
1457
1530
 
@@ -1461,7 +1534,7 @@ rb_ca_fetch_slab_sigil (int argc, VALUE *argv, VALUE self)
1461
1534
  rb_ary_push(slab_axes, INT2NUM(i));
1462
1535
  clean[i] = Qnil;
1463
1536
  }
1464
- else if ( FIXNUM_P(a) || RB_TYPE_P(a, T_BIGNUM) ) {
1537
+ else if ( ca_index_key_is_scalar(a) ) {
1465
1538
  /* Promote Integer to length-1 Range so the recursed view keeps
1466
1539
  the axis (matches the existing :> dispatcher semantics where
1467
1540
  scalars become length-1 BLOCKs via finalize_reg). */
@@ -1545,11 +1618,6 @@ rb_cs_fetch_method (int argc, VALUE *argv, VALUE self)
1545
1618
  }
1546
1619
  break;
1547
1620
  }
1548
- case CA_REG_ATTRIBUTE: {
1549
- obj = rb_funcall(self, rb_intern("attribute"), 0);
1550
- obj = rb_hash_aref(obj, info.symbol);
1551
- break;
1552
- }
1553
1621
  default:
1554
1622
  rb_raise(rb_eIndexError, "invalid index specified");
1555
1623
  }
@@ -1592,6 +1660,23 @@ ca_face_convert_store_rval (VALUE self, CArray *ca, VALUE val)
1592
1660
  return ca_face_convert_store_rval(self, ca,
1593
1661
  rb_funcall(val, rb_intern("to_a"), 0));
1594
1662
  }
1663
+ /* A CAString takes Strings. A text Face goes in as the CAString its
1664
+ to_string gives, and an object array as itself wrapped in a CAString,
1665
+ which checks every cell that is not masked; either keeps its shape,
1666
+ so it meets the shape rule of a store as an object array would. Any
1667
+ other array goes in as its values, each through the write hook. */
1668
+ if ( ca->obj_type == CA_OBJ_STRING && cval->obj_type != CA_OBJ_STRING ) {
1669
+ if ( ca_is_face(cval) && rb_respond_to(val, rb_intern("to_string")) ) {
1670
+ return ca_face_convert_store_rval(self, ca,
1671
+ rb_funcall(val, rb_intern("to_string"), 0));
1672
+ }
1673
+ if ( ! ca_is_face(cval) && cval->data_type == CA_OBJECT ) {
1674
+ return ca_face_convert_store_rval(self, ca,
1675
+ rb_funcall(rb_obj_class(self), rb_intern("wrap"), 1, val));
1676
+ }
1677
+ return ca_face_convert_store_rval(self, ca,
1678
+ rb_funcall(val, rb_intern("to_a"), 0));
1679
+ }
1595
1680
  /* Face RHS: reconcile to self's unit and descend to storage. Mirror of
1596
1681
  the scalar path (ca_face_element_to_storage) for a bulk CArray source.
1597
1682
  Guarded on self having to_comparable so a Face without a reconcile
@@ -1610,11 +1695,17 @@ ca_face_convert_store_rval (VALUE self, CArray *ca, VALUE val)
1610
1695
  {
1611
1696
  CArray *storage = ca_strip_face(ca);
1612
1697
  if ( storage && ca_is_integer_type(storage) && ! ca_is_integer_type(cval) ) {
1698
+ const char *method = ca_face_storage_method(self);
1699
+ if ( method ) {
1700
+ rb_raise(rb_eTypeError,
1701
+ "%s cannot store a bare %s array as raw storage "
1702
+ "(write the %s storage, which #%s gives, directly)",
1703
+ rb_obj_classname(self), ca_type_name[cval->data_type],
1704
+ ca_type_name[storage->data_type], method);
1705
+ }
1613
1706
  rb_raise(rb_eTypeError,
1614
- "%s cannot store a bare %s array as raw storage "
1615
- "(use ca.parent to write the %s storage directly)",
1616
- rb_obj_classname(self), ca_type_name[cval->data_type],
1617
- ca_type_name[storage->data_type]);
1707
+ "%s cannot store a bare %s array as raw storage",
1708
+ rb_obj_classname(self), ca_type_name[cval->data_type]);
1618
1709
  }
1619
1710
  }
1620
1711
  return val;
@@ -1676,7 +1767,7 @@ rb_ca_store_method (int argc, VALUE *argv, VALUE self)
1676
1767
  /* newaxis (:_) interception on store. Build the newaxis view (a
1677
1768
  writable reshape aliasing self), then assign rval into the whole view
1678
1769
  (writes propagate to self). */
1679
- if ( ca_argv_has_newaxis(argc, argv) ) {
1770
+ if ( ! ca_argv_is_method_call(argc, argv) && ca_argv_has_newaxis(argc, argv) ) {
1680
1771
  volatile VALUE view = rb_ca_fetch_newaxis(argc, argv, self, ca);
1681
1772
  rb_ca_store_all(view, rval);
1682
1773
  return rval;
@@ -1685,7 +1776,7 @@ rb_ca_store_method (int argc, VALUE *argv, VALUE self)
1685
1776
  /* slab sigil on store is rejected regardless of outer-slot type. Catch
1686
1777
  :> before scan_index so mask/fancy outer slots get the same "not
1687
1778
  supported" message instead of a misleading TypeError. */
1688
- if ( ca_argv_has_slab_sigil(argc, argv) ) {
1779
+ if ( ! ca_argv_is_method_call(argc, argv) && ca_argv_has_slab_sigil(argc, argv) ) {
1689
1780
  rb_raise(rb_eIndexError,
1690
1781
  "assignment through a slab iterator (:>) is not supported "
1691
1782
  "(use a block index, e.g. ca[range, nil] = val, or each_slab)");
@@ -1704,6 +1795,16 @@ rb_ca_store_method (int argc, VALUE *argv, VALUE self)
1704
1795
  dest = rb_ca_strip_face_value(self);
1705
1796
  }
1706
1797
 
1798
+ /* One cell of a CAString holds a String. Stored into the object storage,
1799
+ a Ruby Array would become the cell's value, which a plain object array
1800
+ allows. */
1801
+ if ( ca->obj_type == CA_OBJ_STRING && TYPE(rval) == T_ARRAY &&
1802
+ ( info.type == CA_REG_ADDRESS || info.type == CA_REG_POINT ) ) {
1803
+ rb_raise(rb_eTypeError,
1804
+ "%s: an Array is not a String (one cell of a CAString holds "
1805
+ "a String; UNDEF masks a cell)", ca_calling_method_name());
1806
+ }
1807
+
1707
1808
  switch ( info.type ) {
1708
1809
  case CA_REG_ADDRESS_COMPLEX:
1709
1810
  self = rb_ca_refer_new_flatten(self);
@@ -1755,15 +1856,12 @@ rb_ca_store_method (int argc, VALUE *argv, VALUE self)
1755
1856
  break;
1756
1857
  }
1757
1858
  case CA_REG_METHOD_CALL: {
1758
- ca_store_method_call_ctx_t c;
1759
- TypedData_Get_Struct(self, CArray, &carray_data_type, ca);
1760
- c.self = self;
1761
- c.dest = dest;
1762
- c.mid = SYM2ID(info.symbol);
1763
- c.argc = (int)(argc-1);
1764
- c.argv = argv+1;
1765
- c.rval = rval;
1766
- obj = ca_attach_window(1, &ca, "r", ca_store_method_call_body, (VALUE) &c);
1859
+ /* a[:eq, 0] = v is a[a.eq(0)] = v: the index is computed first and the
1860
+ store then goes through it, as the read does -- only the cells it
1861
+ picks are written. */
1862
+ volatile VALUE idx;
1863
+ idx = rb_funcallv_public(self, SYM2ID(info.symbol), argc-1, argv+1);
1864
+ obj = rb_ca_store(dest, idx, rval);
1767
1865
  break;
1768
1866
  }
1769
1867
  case CA_REG_MEMBER: {
@@ -1778,11 +1876,6 @@ rb_ca_store_method (int argc, VALUE *argv, VALUE self)
1778
1876
  }
1779
1877
  break;
1780
1878
  }
1781
- case CA_REG_ATTRIBUTE: {
1782
- obj = rb_funcall(self, rb_intern("attribute"), 0);
1783
- obj = rb_hash_aset(obj, info.symbol, rval);
1784
- break;
1785
- }
1786
1879
  }
1787
1880
 
1788
1881
  return obj;
@@ -1812,7 +1905,9 @@ rb_ca_store (VALUE self, VALUE index, VALUE rval)
1812
1905
  case T_ARRAY:
1813
1906
  index = rb_obj_clone(index);
1814
1907
  rb_ary_push(index, rval);
1815
- return rb_ca_store_method((int)RARRAY_LEN(index), (VALUE *)RARRAY_CONST_PTR(index), self);
1908
+ rval = rb_ca_store_method((int)RARRAY_LEN(index), (VALUE *)RARRAY_CONST_PTR(index), self);
1909
+ RB_GC_GUARD(index); /* the store reads its arguments out of index */
1910
+ return rval;
1816
1911
  default: {
1817
1912
  VALUE rindex[2] = { index, rval };
1818
1913
  return rb_ca_store_method(2, rindex, self);
@@ -1825,7 +1920,9 @@ rb_ca_store2 (VALUE self, int n, VALUE *rindex, VALUE rval)
1825
1920
  {
1826
1921
  volatile VALUE index = rb_ary_new4(n, rindex);
1827
1922
  rb_ary_push(index, rval);
1828
- return rb_ca_store_method((int)RARRAY_LEN(index), (VALUE *)RARRAY_CONST_PTR(index), self);
1923
+ rval = rb_ca_store_method((int)RARRAY_LEN(index), (VALUE *)RARRAY_CONST_PTR(index), self);
1924
+ RB_GC_GUARD(index); /* the store reads its arguments out of index */
1925
+ return rval;
1829
1926
  }
1830
1927
 
1831
1928
  static VALUE
@@ -1842,11 +1939,11 @@ rb_ca_s_scan_index (VALUE self, VALUE rdim, VALUE ridx)
1842
1939
  Check_Type(ridx, T_ARRAY);
1843
1940
 
1844
1941
  ndim = (int) RARRAY_LEN(rdim);
1845
- CA_CHECK_RANK(ndim);
1942
+ CA_CHECK_RANK_GIVEN(ndim);
1846
1943
  for (i=0; i<ndim; i++) {
1847
1944
  dim[i] = NUM2SIZE(rb_ary_entry(rdim, i));
1848
1945
  }
1849
- CA_CHECK_DIM(ndim, dim);
1946
+ CA_CHECK_DIM_GIVEN(ndim, dim);
1850
1947
  elements = ca_shape_elements(ndim, dim, 1);
1851
1948
 
1852
1949
  info.range_check = 1;
@@ -1919,8 +2016,7 @@ rb_ca_s_scan_index (VALUE self, VALUE rdim, VALUE ridx)
1919
2016
  case CA_REG_GRID:
1920
2017
  case CA_REG_MAPPING:
1921
2018
  case CA_REG_METHOD_CALL:
1922
- case CA_REG_MEMBER:
1923
- case CA_REG_ATTRIBUTE:
2019
+ case CA_REG_MEMBER:
1924
2020
  break;
1925
2021
  default:
1926
2022
  rb_raise(rb_eArgError, "unknown index specification");
@@ -2016,10 +2112,11 @@ parse_shape_kwarg (VALUE rshape, int *out_ndim, ca_size_t *dim,
2016
2112
 
2017
2113
  Check_Type(rshape, T_ARRAY);
2018
2114
  ndim = (int) RARRAY_LEN(rshape);
2019
- if ( ndim < 1 || ndim > CA_RANK_MAX ) {
2115
+ if ( ndim < 1 ) {
2020
2116
  rb_raise(rb_eArgError, "invalid shape ndim %d (must be 1..%d)",
2021
2117
  ndim, CA_RANK_MAX);
2022
2118
  }
2119
+ ca_check_rank_fits(ndim, NULL);
2023
2120
  for (i = 0; i < ndim; i++) {
2024
2121
  dim[i] = NUM2SIZE(rb_ary_entry(rshape, i));
2025
2122
  if ( dim[i] < 0 ) {
@@ -2103,6 +2200,7 @@ addr2index_do (int ndim, ca_size_t *dim, ca_size_t elements, VALUE raddr)
2103
2200
  }
2104
2201
 
2105
2202
  ca_detach(cin);
2203
+ RB_GC_GUARD(raddr); /* the CA_SIZE view is reached only through cin */
2106
2204
  return out;
2107
2205
  }
2108
2206
  }
@@ -2306,7 +2404,7 @@ rb_ca_s_addr2index (int argc, VALUE *argv, VALUE klass)
2306
2404
  static VALUE
2307
2405
  rb_ca_s_index2addr (int argc, VALUE *argv, VALUE klass)
2308
2406
  {
2309
- VALUE rest, opts;
2407
+ VALUE rest, opts, out;
2310
2408
  ID kw_ids[1];
2311
2409
  VALUE kw_vals[1];
2312
2410
  int ndim;
@@ -2320,7 +2418,10 @@ rb_ca_s_index2addr (int argc, VALUE *argv, VALUE klass)
2320
2418
  }
2321
2419
  rb_get_kwargs(opts, kw_ids, 1, 0, kw_vals);
2322
2420
  parse_shape_kwarg(kw_vals[0], &ndim, dim, &elements);
2323
- return index2addr_do(ndim, dim, (int) RARRAY_LEN(rest), RARRAY_PTR(rest));
2421
+ /* index2addr_do reads, and replaces, its arguments in rest */
2422
+ out = index2addr_do(ndim, dim, (int) RARRAY_LEN(rest), RARRAY_PTR(rest));
2423
+ RB_GC_GUARD(rest);
2424
+ return out;
2324
2425
  }
2325
2426
 
2326
2427