carray 3.0.3 → 3.0.4

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 (212) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +264 -0
  3. data/README.md +3 -2
  4. data/carray.gemspec +1 -1
  5. data/ext/ca_axis_descriptor.h +27 -24
  6. data/ext/ca_axis_dispatch.c +1 -6
  7. data/ext/ca_axis_group.c +27 -27
  8. data/ext/ca_binop_dispatch.c +1 -29
  9. data/ext/ca_binop_dispatch.h +0 -1
  10. data/ext/ca_categorical_iterator.c +205 -134
  11. data/ext/ca_compare.c +7 -18
  12. data/ext/ca_group_iter.c +16 -17
  13. data/ext/ca_kernel_iterator.c +1234 -1994
  14. data/ext/ca_kernel_iterator.h +391 -437
  15. data/ext/ca_monop_dispatch.c +82 -59
  16. data/ext/ca_monop_dispatch.h +1 -0
  17. data/ext/ca_obj_array.c +96 -39
  18. data/ext/ca_obj_bincmp.c +24 -21
  19. data/ext/ca_obj_binop.c +15 -26
  20. data/ext/ca_obj_bitarray.c +3 -2
  21. data/ext/ca_obj_bitfield.c +5 -6
  22. data/ext/ca_obj_block.c +18 -9
  23. data/ext/ca_obj_byte_swap.c +5 -6
  24. data/ext/ca_obj_const_string.c +173 -19
  25. data/ext/ca_obj_face.c +238 -57
  26. data/ext/ca_obj_face.h +19 -0
  27. data/ext/ca_obj_fake.c +15 -29
  28. data/ext/ca_obj_farray.c +6 -3
  29. data/ext/ca_obj_field.c +17 -5
  30. data/ext/ca_obj_fixlen_string.c +2 -3
  31. data/ext/ca_obj_grid.c +406 -132
  32. data/ext/ca_obj_meld.c +172 -34
  33. data/ext/ca_obj_moncmp.c +23 -8
  34. data/ext/ca_obj_monop.c +67 -57
  35. data/ext/ca_obj_object.c +157 -51
  36. data/ext/ca_obj_record.c +12 -7
  37. data/ext/ca_obj_reduce.c +13 -8
  38. data/ext/ca_obj_refer.c +25 -27
  39. data/ext/ca_obj_remap.c +30 -9
  40. data/ext/ca_obj_repeat.c +9 -10
  41. data/ext/ca_obj_roll.c +6 -5
  42. data/ext/ca_obj_select.c +14 -9
  43. data/ext/ca_obj_select_axis.c +15 -47
  44. data/ext/ca_obj_shift.c +11 -1
  45. data/ext/ca_obj_stack.c +179 -34
  46. data/ext/ca_obj_stride.c +162 -115
  47. data/ext/ca_obj_string.c +71 -4
  48. data/ext/ca_obj_tile.c +6 -5
  49. data/ext/ca_obj_time.c +22 -6
  50. data/ext/ca_obj_timedelta.c +22 -6
  51. data/ext/ca_obj_transpose.c +5 -3
  52. data/ext/ca_obj_triop.c +23 -32
  53. data/ext/ca_obj_window.c +190 -26
  54. data/ext/ca_sweep_engine.c +2 -2
  55. data/ext/caframe_csv_reader.c +80 -1
  56. data/ext/carray.h +181 -75
  57. data/ext/carray_access.c +184 -96
  58. data/ext/carray_address_basis.c +343 -87
  59. data/ext/carray_attribute.c +89 -6
  60. data/ext/carray_bincount.c +10 -8
  61. data/ext/carray_broadcast.c +13 -8
  62. data/ext/carray_build_flags.h +3 -0
  63. data/ext/carray_call_cfunc.c +108 -0
  64. data/ext/carray_cast.c +142 -24
  65. data/ext/carray_cast_func.rb +46 -6
  66. data/ext/carray_class.c +4 -3
  67. data/ext/carray_config.h +28 -0
  68. data/ext/carray_conversion.c +16 -7
  69. data/ext/carray_copy.c +51 -14
  70. data/ext/carray_core.c +147 -55
  71. data/ext/carray_count.c +50 -20
  72. data/ext/carray_element.c +76 -33
  73. data/ext/carray_factorize.c +118 -64
  74. data/ext/carray_generate.c +191 -50
  75. data/ext/carray_histogram.c +62 -17
  76. data/ext/carray_hold.c +12 -15
  77. data/ext/carray_index_classifier.c +388 -176
  78. data/ext/carray_internal.h +336 -29
  79. data/ext/carray_kernels_bincmp.c +4453 -0
  80. data/ext/carray_kernels_binop.c +11368 -0
  81. data/ext/carray_kernels_init.c +1734 -0
  82. data/ext/carray_kernels_moncmp.c +2104 -0
  83. data/ext/carray_kernels_monop.c +18369 -0
  84. data/ext/carray_kernels_reduce_aggregate.c +23818 -0
  85. data/ext/carray_kernels_reduce_boolean.c +406 -0
  86. data/ext/carray_kernels_reduce_cumulative.c +10086 -0
  87. data/ext/carray_kernels_reduce_extreme.c +17634 -0
  88. data/ext/carray_kernels_reduce_variance.c +3849 -0
  89. data/ext/carray_kernels_scan.c +4302 -0
  90. data/ext/carray_kernels_search.c +33829 -0
  91. data/ext/carray_kernels_sort.c +10906 -0
  92. data/ext/carray_kernels_triop.c +1864 -0
  93. data/ext/carray_key_windows.c +216 -0
  94. data/ext/carray_lazy.c +170 -111
  95. data/ext/carray_mask.c +292 -72
  96. data/ext/carray_median_percentile.c +82 -28
  97. data/ext/carray_memory_view.c +669 -561
  98. data/ext/carray_operator.c +124 -47
  99. data/ext/carray_order.c +384 -76
  100. data/ext/carray_partition.c +28 -27
  101. data/ext/carray_random.c +67 -13
  102. data/ext/carray_round.c +273 -0
  103. data/ext/carray_scatter.c +63 -38
  104. data/ext/carray_segment.c +3 -3
  105. data/ext/carray_slab.c +91 -28
  106. data/ext/carray_sort.c +35 -35
  107. data/ext/carray_struct.c +19 -18
  108. data/ext/carray_test.c +26 -5
  109. data/ext/carray_undef.c +4 -0
  110. data/ext/carray_utils.c +246 -45
  111. data/ext/mk_call_cfunc.rb +19 -2
  112. data/ext/mkkernel.rb +198 -240
  113. data/ext/ruby_carray.c +10 -4
  114. data/ext/version.h +4 -4
  115. data/lib/carray/attribute.rb +126 -22
  116. data/lib/carray/autoload_carray.rb +8 -31
  117. data/lib/carray/autoload_method_extension.rb +14 -5
  118. data/lib/carray/axis_group.rb +255 -106
  119. data/lib/carray/basics.rb +78 -10
  120. data/lib/carray/bincount_nd.rb +12 -2
  121. data/lib/carray/block_iterator.rb +126 -23
  122. data/lib/carray/boolean_reduce.rb +17 -17
  123. data/lib/carray/categorical.rb +165 -43
  124. data/lib/carray/categorical_iterator.rb +130 -27
  125. data/lib/carray/complex.rb +5 -5
  126. data/lib/carray/conditional.rb +67 -18
  127. data/lib/carray/const_string.rb +14 -5
  128. data/lib/carray/construct.rb +15 -11
  129. data/lib/carray/data_type_extension.rb +1 -0
  130. data/lib/carray/distance_limit.rb +77 -0
  131. data/lib/carray/fixlen_string.rb +7 -1
  132. data/lib/carray/frame/column_verbs.rb +70 -0
  133. data/lib/carray/frame/convert.rb +69 -0
  134. data/lib/carray/frame/csv_parser.rb +111 -17
  135. data/lib/carray/frame/describe.rb +3 -2
  136. data/lib/carray/frame/frame.rb +246 -21
  137. data/lib/carray/frame/group.rb +510 -95
  138. data/lib/carray/frame/io.rb +115 -28
  139. data/lib/carray/frame/join.rb +312 -51
  140. data/lib/carray/frame/records.rb +83 -7
  141. data/lib/carray/frame/reshape.rb +42 -35
  142. data/lib/carray/frame/sort.rb +7 -19
  143. data/lib/carray/frame/verbs.rb +188 -48
  144. data/lib/carray/frame/windows.rb +117 -0
  145. data/lib/carray/frame.rb +2 -0
  146. data/lib/carray/fuse_source.rb +11 -4
  147. data/lib/carray/fusion.rb +40 -16
  148. data/lib/carray/histogram.rb +6 -1
  149. data/lib/carray/inspect.rb +31 -8
  150. data/lib/carray/iterator.rb +85 -7
  151. data/lib/carray/key_windows.rb +620 -0
  152. data/lib/carray/lazy.rb +182 -183
  153. data/lib/carray/mask_gap_fill.rb +63 -57
  154. data/lib/carray/meld_reduce.rb +12 -14
  155. data/lib/carray/methods/bin.rb +26 -18
  156. data/lib/carray/methods/bincount.rb +16 -0
  157. data/lib/carray/methods/bit_string.rb +1 -1
  158. data/lib/carray/methods/choose.rb +16 -3
  159. data/lib/carray/methods/composition.rb +3 -1
  160. data/lib/carray/methods/gather_nd.rb +14 -5
  161. data/lib/carray/methods/insert_block.rb +2 -2
  162. data/lib/carray/methods/is_between.rb +59 -0
  163. data/lib/carray/methods/is_in.rb +53 -36
  164. data/lib/carray/methods/join.rb +1 -6
  165. data/lib/carray/methods/lag.rb +49 -0
  166. data/lib/carray/methods/locate_addr.rb +47 -51
  167. data/lib/carray/methods/mode.rb +1 -1
  168. data/lib/carray/methods/nunique.rb +11 -6
  169. data/lib/carray/methods/resize.rb +3 -3
  170. data/lib/carray/methods/value_counts.rb +4 -11
  171. data/lib/carray/record_order.rb +3 -3
  172. data/lib/carray/rng.rb +3 -0
  173. data/lib/carray/runtime.rb +53 -8
  174. data/lib/carray/segment_iterator.rb +313 -82
  175. data/lib/carray/serialize.rb +24 -10
  176. data/lib/carray/slab_iterator.rb +50 -0
  177. data/lib/carray/string.rb +105 -5
  178. data/lib/carray/string_operation_extension.rb +8 -1
  179. data/lib/carray/struct.rb +9 -21
  180. data/lib/carray/struct_builder.rb +19 -8
  181. data/lib/carray/time.rb +726 -104
  182. data/lib/carray/window_iterator.rb +122 -16
  183. data/lib/carray.rb +7 -0
  184. data/yard-stubs/ca_obj_bitfield.rb +1 -1
  185. data/yard-stubs/ca_obj_field.rb +4 -0
  186. data/yard-stubs/ca_obj_grid.rb +21 -10
  187. data/yard-stubs/ca_obj_refer.rb +2 -1
  188. data/yard-stubs/ca_obj_roll.rb +2 -2
  189. data/yard-stubs/ca_obj_shift.rb +5 -1
  190. data/yard-stubs/ca_obj_tile.rb +1 -1
  191. data/yard-stubs/ca_obj_window.rb +8 -3
  192. data/yard-stubs/carray_access.rb +6 -1
  193. data/yard-stubs/carray_attribute.rb +0 -5
  194. data/yard-stubs/carray_cast.rb +10 -0
  195. data/yard-stubs/carray_conversion.rb +2 -1
  196. data/yard-stubs/carray_copy.rb +4 -2
  197. data/yard-stubs/carray_count.rb +4 -4
  198. data/yard-stubs/carray_element.rb +10 -4
  199. data/yard-stubs/carray_generate.rb +21 -1
  200. data/yard-stubs/carray_mask.rb +26 -2
  201. data/yard-stubs/carray_math.rb +27 -1
  202. data/yard-stubs/carray_median_percentile.rb +5 -2
  203. data/yard-stubs/carray_memory_view.rb +40 -23
  204. data/yard-stubs/carray_order.rb +29 -18
  205. data/yard-stubs/carray_random.rb +13 -4
  206. data/yard-stubs/carray_scatter.rb +14 -9
  207. data/yard-stubs/carray_slab.rb +9 -2
  208. data/yard-stubs/carray_sort.rb +6 -5
  209. data/yard-stubs/carray_test.rb +5 -3
  210. metadata +25 -3
  211. data/lib/carray/arrow_tensor.rb +0 -401
  212. 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
 
@@ -229,9 +229,7 @@ static void
229
229
  ary_guess_shape (VALUE ary, int level, int *max_level, ca_size_t *dim)
230
230
  {
231
231
  volatile VALUE ary0;
232
- if ( level > CA_RANK_MAX ) {
233
- rb_raise(rb_eArgError, "too deep level array for conversion to carray");
234
- }
232
+ ca_check_rank_fits(level + 1, NULL);
235
233
 
236
234
  if ( TYPE(ary) == T_ARRAY ) {
237
235
  if ( RARRAY_LEN(ary) == 0 && level == 0 ) {
@@ -476,26 +474,6 @@ ca_id_expression_evaluator (void)
476
474
  return id;
477
475
  }
478
476
 
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
477
  /* The casting stores below convert into the destination inside a window,
500
478
  and a conversion from Ruby objects can raise part way. Such a window
501
479
  attaches rather than allocates: the cells converted before the raise
@@ -586,6 +564,10 @@ ca_store_all_list_body (VALUE arg)
586
564
  (boolean8_t*)ca->mask->ptr);
587
565
  }
588
566
  else {
567
+ /* Every cell gets a value, so none stays masked. */
568
+ if ( ca->mask ) {
569
+ memset(ca->mask->ptr, 0, ca->elements);
570
+ }
589
571
  ca_cast_block(ca->elements, c->ico, (VALUE *)RARRAY_CONST_PTR(list), cast_target, ca->ptr);
590
572
  }
591
573
  return Qnil;
@@ -654,6 +636,38 @@ ca_face_source_for_store (VALUE self, CArray *ca, VALUE rval)
654
636
  return rb_funcall(rval, rb_intern("to_type"), 1, INT2NUM(CA_OBJECT));
655
637
  }
656
638
 
639
+ /* A store into no cells writes nothing, but the value is checked as it
640
+ would be for any cells: the same statement does not raise or pass
641
+ depending on how many cells the index picks. */
642
+ static void
643
+ ca_store_all_check_empty (VALUE self, VALUE rval)
644
+ {
645
+ CArray *ca;
646
+ TypedData_Get_Struct(self, CArray, &carray_data_type, ca);
647
+ if ( rb_obj_is_cscalar(rval) ) {
648
+ rval = rb_ca_fetch_addr(rval, 0);
649
+ }
650
+ rval = ca_face_source_for_store(self, ca, rval);
651
+ if ( ! rb_obj_is_carray(rval) && TYPE(rval) != T_ARRAY &&
652
+ rb_respond_to(rval, id_to_ca) ) {
653
+ rval = rb_funcall(rval, id_to_ca, 0);
654
+ }
655
+ if ( rb_obj_is_carray(rval) ) {
656
+ ca_broadcast_to_destination(self, (volatile VALUE *) &rval);
657
+ }
658
+ else if ( TYPE(rval) == T_ARRAY ) {
659
+ if ( NIL_P(rb_ary_flatten_for_elements(rval, ca->elements, ca)) ) {
660
+ rb_raise(rb_eArgError, "failed to guess data size of given array");
661
+ }
662
+ }
663
+ else if ( rval != CA_UNDEF ) {
664
+ volatile VALUE holder;
665
+ char *fval = ALLOCV_N(char, holder, ca->bytes);
666
+ rb_ca_obj2ptr(self, rval, fval);
667
+ ALLOCV_END(holder);
668
+ }
669
+ }
670
+
657
671
  VALUE
658
672
  rb_ca_store_all (VALUE self, VALUE rval)
659
673
  {
@@ -661,10 +675,14 @@ rb_ca_store_all (VALUE self, VALUE rval)
661
675
 
662
676
  rb_ca_modify(self);
663
677
 
664
- if ( self == rval || rb_ca_is_empty(self) ) {
678
+ if ( self == rval ) {
665
679
  return rval;
666
680
  }
667
-
681
+ if ( rb_ca_is_empty(self) ) {
682
+ ca_store_all_check_empty(self, rval);
683
+ return rval;
684
+ }
685
+
668
686
  if ( rb_obj_is_cscalar(rval) ) {
669
687
  rval = rb_ca_fetch_addr(rval, 0);
670
688
  }
@@ -689,6 +707,7 @@ rb_ca_store_all (VALUE self, VALUE rval)
689
707
  && ca->data_type == cv->data_type
690
708
  && ca->ndim == cv->ndim
691
709
  && ca->elements == cv->elements
710
+ && ca_in_main_ractor()
692
711
  && RTEST(rb_attr_get(rb_cCArray, ca_id_expression_evaluator())) ) {
693
712
  int8_t k;
694
713
  int same = 1;
@@ -1022,7 +1041,9 @@ extern VALUE rb_ca_remap_new (VALUE cary, VALUE rmapper);
1022
1041
  (b) Otherwise -> normalize chain (a.flatten[mapper.flatten]
1023
1042
  .reshape). A 1-D mapper is already routed to CAGrid upstream.
1024
1043
 
1025
- Both routes raise on a masked mapper. */
1044
+ A masked mapper cell selects nothing: the cell keeps its position and
1045
+ reads as masked (a CAGrid hole); the same-shape fast path is left to a
1046
+ mapper with no masked cell. */
1026
1047
  static VALUE
1027
1048
  rb_ca_fancy_index_chain (VALUE self, VALUE rmapper)
1028
1049
  {
@@ -1034,15 +1055,24 @@ rb_ca_fancy_index_chain (VALUE self, VALUE rmapper)
1034
1055
  TypedData_Get_Struct(self, CArray, &carray_data_type, ca);
1035
1056
  TypedData_Get_Struct(rmapper, CArray, &carray_data_type, cm);
1036
1057
 
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. */
1058
+ /* (a) same-shape fast path: ndim + per-axis dim + CA_SIZE data_type.
1059
+ The view reads the index array it is given, so it is given a copy: the
1060
+ indices are the ones the caller passed at this call, as every other
1061
+ route reads them, and each is checked against self now, so an index out
1062
+ of range raises here and not at the first read. */
1042
1063
  if ( cm->data_type == CA_SIZE
1064
+ && ! ca_is_any_masked(cm)
1043
1065
  && cm->ndim == ca->ndim
1044
1066
  && memcmp(cm->dim, ca->dim, ca->ndim * sizeof(ca_size_t)) == 0 ) {
1045
- return rb_ca_remap_new(self, rmapper);
1067
+ volatile VALUE snapshot = rb_ca_copy(rmapper);
1068
+ CArray *cs;
1069
+ ca_size_t k, *p;
1070
+ TypedData_Get_Struct(snapshot, CArray, &carray_data_type, cs);
1071
+ p = (ca_size_t *) cs->ptr;
1072
+ for (k = 0; k < cs->elements; k++) {
1073
+ CA_CHECK_INDEX(p[k], ca->elements);
1074
+ }
1075
+ return rb_ca_remap_new(self, snapshot);
1046
1076
  }
1047
1077
 
1048
1078
  /* (b) Normalize chain fallback. A Face is flattened through its storage
@@ -1070,10 +1100,9 @@ rb_ca_fancy_index_chain (VALUE self, VALUE rmapper)
1070
1100
  return result;
1071
1101
  }
1072
1102
 
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. */
1103
+ /* rb_ca_mapping_new and rb_ca_mapping index cary by a mapper array through
1104
+ rb_ca_fancy_index_chain: the result is a CAGrid over the flattened array,
1105
+ reshaped (CARefer, the outermost layer) to the mapper's shape. */
1077
1106
 
1078
1107
  VALUE
1079
1108
  rb_ca_mapping_new (VALUE cary, CArray *mapper)
@@ -1099,6 +1128,18 @@ rb_ca_mapping (int argc, VALUE *argv, VALUE self)
1099
1128
  the optimal view class (CAStride for stride bases, CARefer for fancy
1100
1129
  bases). */
1101
1130
 
1131
+ /* a[:name, args] calls a method of the array, and its arguments are the
1132
+ method's, whatever they are: the scans for :_, :> and group indices
1133
+ below look at index keys only. */
1134
+ static int
1135
+ ca_argv_is_method_call (int argc, VALUE *argv)
1136
+ {
1137
+ const char *name;
1138
+ if ( argc < 1 || ! SYMBOL_P(argv[0]) ) return 0;
1139
+ name = rb_id2name(SYM2ID(argv[0]));
1140
+ return ( name != NULL && strlen(name) > 1 );
1141
+ }
1142
+
1102
1143
  static int
1103
1144
  ca_argv_has_newaxis (int argc, VALUE *argv)
1104
1145
  {
@@ -1140,6 +1181,10 @@ rb_ca_fetch_method (int argc, VALUE *argv, VALUE self)
1140
1181
 
1141
1182
  TypedData_Get_Struct(self, CArray, &carray_data_type, ca);
1142
1183
 
1184
+ if ( ca_argv_is_method_call(argc, argv) ) {
1185
+ goto classify;
1186
+ }
1187
+
1143
1188
  /* newaxis (:_) interception (cheap pointer scan; zero overhead beyond
1144
1189
  the loop when no :_ is present). */
1145
1190
  if ( ca_argv_has_newaxis(argc, argv) ) {
@@ -1163,6 +1208,7 @@ rb_ca_fetch_method (int argc, VALUE *argv, VALUE self)
1163
1208
  return rb_ca_fetch_group(argc, argv, self);
1164
1209
  }
1165
1210
 
1211
+ classify:
1166
1212
  info.range_check = 1;
1167
1213
  rb_ca_scan_index(ca->ndim, ca->dim, ca->elements, argc, argv, &info);
1168
1214
 
@@ -1244,11 +1290,6 @@ rb_ca_fetch_method (int argc, VALUE *argv, VALUE self)
1244
1290
  }
1245
1291
  break;
1246
1292
  }
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
1293
  default:
1253
1294
  rb_raise(rb_eIndexError, "invalid index specified");
1254
1295
  }
@@ -1292,7 +1333,7 @@ rb_ca_fetch_newaxis (int argc, VALUE *argv, VALUE self, CArray *ca)
1292
1333
  nunder++;
1293
1334
  continue;
1294
1335
  }
1295
- if ( FIXNUM_P(a) || RB_TYPE_P(a, T_BIGNUM) ) {
1336
+ if ( ca_index_key_is_scalar(a) ) {
1296
1337
  nscalar++;
1297
1338
  }
1298
1339
  if ( a == sym_gt || a == sym_perc
@@ -1301,7 +1342,7 @@ rb_ca_fetch_newaxis (int argc, VALUE *argv, VALUE self, CArray *ca)
1301
1342
  "newaxis (:_) cannot be combined with :>, :%%, "
1302
1343
  "or rubber dim (false / :~)");
1303
1344
  }
1304
- if ( nclean >= CA_RANK_MAX ) {
1345
+ if ( nclean >= CA_RANK_MAX ) { /* rank-check: a count of indices */
1305
1346
  rb_raise(rb_eIndexError, "too many indices");
1306
1347
  }
1307
1348
  clean[nclean++] = a;
@@ -1339,21 +1380,40 @@ rb_ca_fetch_newaxis (int argc, VALUE *argv, VALUE self, CArray *ca)
1339
1380
  TypedData_Get_Struct(base, CArray, &carray_data_type, cb);
1340
1381
 
1341
1382
  /* 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 */
1383
+ (Integer) -> dropped (skip), else -> base.dim[cursor]. A key gives one
1384
+ axis, except an integer CArray read alone (a mapping), which gives as
1385
+ many as it has: the axes the base has beyond one per key are its. */
1386
+ {
1387
+ int nkeys = 0, extra, ncarray = 0;
1388
+ for (i = 0; i < nclean; i++) {
1389
+ if ( ! ca_index_key_is_scalar(clean[i]) ) {
1390
+ nkeys++;
1391
+ if ( rb_obj_is_carray(clean[i]) ) ncarray++;
1392
+ }
1351
1393
  }
1352
- else {
1353
- if ( base_cursor >= cb->ndim ) {
1354
- rb_raise(rb_eIndexError, "newaxis: base axis count mismatch");
1394
+ extra = cb->ndim - nkeys;
1395
+ base_cursor = ( extra != 0 && ncarray != 1 ) ? -1 : 0;
1396
+ for (i = 0; i < argc && base_cursor >= 0; i++) {
1397
+ VALUE a = argv[i];
1398
+ int take = 1, t;
1399
+ if ( a == sym_under ) {
1400
+ final_dims[nfinal++] = 1;
1401
+ continue;
1402
+ }
1403
+ if ( ca_index_key_is_scalar(a) ) {
1404
+ /* scalar index -> axis dropped in base; contributes no output axis */
1405
+ continue;
1406
+ }
1407
+ if ( rb_obj_is_carray(a) ) {
1408
+ take += extra;
1409
+ }
1410
+ for (t = 0; t < take; t++) {
1411
+ if ( base_cursor >= cb->ndim || nfinal >= CA_RANK_MAX ) { /* rank-check: a count of indices */
1412
+ base_cursor = -1;
1413
+ break;
1414
+ }
1415
+ final_dims[nfinal++] = cb->dim[base_cursor++];
1355
1416
  }
1356
- final_dims[nfinal++] = cb->dim[base_cursor++];
1357
1417
  }
1358
1418
  }
1359
1419
  if ( base_cursor != cb->ndim ) {
@@ -1397,7 +1457,7 @@ rb_ca_slab_iter_new (VALUE self, CAIndexInfo *info)
1397
1457
  align with the post-expansion ndim, replace :> with nil (full axis),
1398
1458
  promote Integer to length-1 Range so the base view preserves ndim,
1399
1459
  recurse through the regular indexer dispatch to build the base
1400
- reference (CABlock / CASelectAxis / CAGrid / CAMapping / ...), then
1460
+ reference (CABlock / CASelectAxis / CAGrid / ...), then
1401
1461
  wrap in CASlabIterator with the recorded slab_axes positions. */
1402
1462
  static VALUE
1403
1463
  rb_ca_fetch_slab_sigil (int argc, VALUE *argv, VALUE self)
@@ -1451,7 +1511,7 @@ rb_ca_fetch_slab_sigil (int argc, VALUE *argv, VALUE self)
1451
1511
  eargv = expanded;
1452
1512
  }
1453
1513
 
1454
- if ( eargc > CA_RANK_MAX ) {
1514
+ if ( eargc > CA_RANK_MAX ) { /* rank-check: a count of indices */
1455
1515
  rb_raise(rb_eIndexError, "too many indices");
1456
1516
  }
1457
1517
 
@@ -1461,7 +1521,7 @@ rb_ca_fetch_slab_sigil (int argc, VALUE *argv, VALUE self)
1461
1521
  rb_ary_push(slab_axes, INT2NUM(i));
1462
1522
  clean[i] = Qnil;
1463
1523
  }
1464
- else if ( FIXNUM_P(a) || RB_TYPE_P(a, T_BIGNUM) ) {
1524
+ else if ( ca_index_key_is_scalar(a) ) {
1465
1525
  /* Promote Integer to length-1 Range so the recursed view keeps
1466
1526
  the axis (matches the existing :> dispatcher semantics where
1467
1527
  scalars become length-1 BLOCKs via finalize_reg). */
@@ -1545,11 +1605,6 @@ rb_cs_fetch_method (int argc, VALUE *argv, VALUE self)
1545
1605
  }
1546
1606
  break;
1547
1607
  }
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
1608
  default:
1554
1609
  rb_raise(rb_eIndexError, "invalid index specified");
1555
1610
  }
@@ -1592,6 +1647,23 @@ ca_face_convert_store_rval (VALUE self, CArray *ca, VALUE val)
1592
1647
  return ca_face_convert_store_rval(self, ca,
1593
1648
  rb_funcall(val, rb_intern("to_a"), 0));
1594
1649
  }
1650
+ /* A CAString takes Strings. A text Face goes in as the CAString its
1651
+ to_string gives, and an object array as itself wrapped in a CAString,
1652
+ which checks every cell that is not masked; either keeps its shape,
1653
+ so it meets the shape rule of a store as an object array would. Any
1654
+ other array goes in as its values, each through the write hook. */
1655
+ if ( ca->obj_type == CA_OBJ_STRING && cval->obj_type != CA_OBJ_STRING ) {
1656
+ if ( ca_is_face(cval) && rb_respond_to(val, rb_intern("to_string")) ) {
1657
+ return ca_face_convert_store_rval(self, ca,
1658
+ rb_funcall(val, rb_intern("to_string"), 0));
1659
+ }
1660
+ if ( ! ca_is_face(cval) && cval->data_type == CA_OBJECT ) {
1661
+ return ca_face_convert_store_rval(self, ca,
1662
+ rb_funcall(rb_obj_class(self), rb_intern("wrap"), 1, val));
1663
+ }
1664
+ return ca_face_convert_store_rval(self, ca,
1665
+ rb_funcall(val, rb_intern("to_a"), 0));
1666
+ }
1595
1667
  /* Face RHS: reconcile to self's unit and descend to storage. Mirror of
1596
1668
  the scalar path (ca_face_element_to_storage) for a bulk CArray source.
1597
1669
  Guarded on self having to_comparable so a Face without a reconcile
@@ -1610,11 +1682,17 @@ ca_face_convert_store_rval (VALUE self, CArray *ca, VALUE val)
1610
1682
  {
1611
1683
  CArray *storage = ca_strip_face(ca);
1612
1684
  if ( storage && ca_is_integer_type(storage) && ! ca_is_integer_type(cval) ) {
1685
+ const char *method = ca_face_storage_method(self);
1686
+ if ( method ) {
1687
+ rb_raise(rb_eTypeError,
1688
+ "%s cannot store a bare %s array as raw storage "
1689
+ "(write the %s storage, which #%s gives, directly)",
1690
+ rb_obj_classname(self), ca_type_name[cval->data_type],
1691
+ ca_type_name[storage->data_type], method);
1692
+ }
1613
1693
  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]);
1694
+ "%s cannot store a bare %s array as raw storage",
1695
+ rb_obj_classname(self), ca_type_name[cval->data_type]);
1618
1696
  }
1619
1697
  }
1620
1698
  return val;
@@ -1676,7 +1754,7 @@ rb_ca_store_method (int argc, VALUE *argv, VALUE self)
1676
1754
  /* newaxis (:_) interception on store. Build the newaxis view (a
1677
1755
  writable reshape aliasing self), then assign rval into the whole view
1678
1756
  (writes propagate to self). */
1679
- if ( ca_argv_has_newaxis(argc, argv) ) {
1757
+ if ( ! ca_argv_is_method_call(argc, argv) && ca_argv_has_newaxis(argc, argv) ) {
1680
1758
  volatile VALUE view = rb_ca_fetch_newaxis(argc, argv, self, ca);
1681
1759
  rb_ca_store_all(view, rval);
1682
1760
  return rval;
@@ -1685,7 +1763,7 @@ rb_ca_store_method (int argc, VALUE *argv, VALUE self)
1685
1763
  /* slab sigil on store is rejected regardless of outer-slot type. Catch
1686
1764
  :> before scan_index so mask/fancy outer slots get the same "not
1687
1765
  supported" message instead of a misleading TypeError. */
1688
- if ( ca_argv_has_slab_sigil(argc, argv) ) {
1766
+ if ( ! ca_argv_is_method_call(argc, argv) && ca_argv_has_slab_sigil(argc, argv) ) {
1689
1767
  rb_raise(rb_eIndexError,
1690
1768
  "assignment through a slab iterator (:>) is not supported "
1691
1769
  "(use a block index, e.g. ca[range, nil] = val, or each_slab)");
@@ -1704,6 +1782,16 @@ rb_ca_store_method (int argc, VALUE *argv, VALUE self)
1704
1782
  dest = rb_ca_strip_face_value(self);
1705
1783
  }
1706
1784
 
1785
+ /* One cell of a CAString holds a String. Stored into the object storage,
1786
+ a Ruby Array would become the cell's value, which a plain object array
1787
+ allows. */
1788
+ if ( ca->obj_type == CA_OBJ_STRING && TYPE(rval) == T_ARRAY &&
1789
+ ( info.type == CA_REG_ADDRESS || info.type == CA_REG_POINT ) ) {
1790
+ rb_raise(rb_eTypeError,
1791
+ "%s: an Array is not a String (one cell of a CAString holds "
1792
+ "a String; UNDEF masks a cell)", ca_calling_method_name());
1793
+ }
1794
+
1707
1795
  switch ( info.type ) {
1708
1796
  case CA_REG_ADDRESS_COMPLEX:
1709
1797
  self = rb_ca_refer_new_flatten(self);
@@ -1755,15 +1843,12 @@ rb_ca_store_method (int argc, VALUE *argv, VALUE self)
1755
1843
  break;
1756
1844
  }
1757
1845
  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);
1846
+ /* a[:eq, 0] = v is a[a.eq(0)] = v: the index is computed first and the
1847
+ store then goes through it, as the read does -- only the cells it
1848
+ picks are written. */
1849
+ volatile VALUE idx;
1850
+ idx = rb_funcallv_public(self, SYM2ID(info.symbol), argc-1, argv+1);
1851
+ obj = rb_ca_store(dest, idx, rval);
1767
1852
  break;
1768
1853
  }
1769
1854
  case CA_REG_MEMBER: {
@@ -1778,11 +1863,6 @@ rb_ca_store_method (int argc, VALUE *argv, VALUE self)
1778
1863
  }
1779
1864
  break;
1780
1865
  }
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
1866
  }
1787
1867
 
1788
1868
  return obj;
@@ -1812,7 +1892,9 @@ rb_ca_store (VALUE self, VALUE index, VALUE rval)
1812
1892
  case T_ARRAY:
1813
1893
  index = rb_obj_clone(index);
1814
1894
  rb_ary_push(index, rval);
1815
- return rb_ca_store_method((int)RARRAY_LEN(index), (VALUE *)RARRAY_CONST_PTR(index), self);
1895
+ rval = rb_ca_store_method((int)RARRAY_LEN(index), (VALUE *)RARRAY_CONST_PTR(index), self);
1896
+ RB_GC_GUARD(index); /* the store reads its arguments out of index */
1897
+ return rval;
1816
1898
  default: {
1817
1899
  VALUE rindex[2] = { index, rval };
1818
1900
  return rb_ca_store_method(2, rindex, self);
@@ -1825,7 +1907,9 @@ rb_ca_store2 (VALUE self, int n, VALUE *rindex, VALUE rval)
1825
1907
  {
1826
1908
  volatile VALUE index = rb_ary_new4(n, rindex);
1827
1909
  rb_ary_push(index, rval);
1828
- return rb_ca_store_method((int)RARRAY_LEN(index), (VALUE *)RARRAY_CONST_PTR(index), self);
1910
+ rval = rb_ca_store_method((int)RARRAY_LEN(index), (VALUE *)RARRAY_CONST_PTR(index), self);
1911
+ RB_GC_GUARD(index); /* the store reads its arguments out of index */
1912
+ return rval;
1829
1913
  }
1830
1914
 
1831
1915
  static VALUE
@@ -1842,11 +1926,11 @@ rb_ca_s_scan_index (VALUE self, VALUE rdim, VALUE ridx)
1842
1926
  Check_Type(ridx, T_ARRAY);
1843
1927
 
1844
1928
  ndim = (int) RARRAY_LEN(rdim);
1845
- CA_CHECK_RANK(ndim);
1929
+ CA_CHECK_RANK_GIVEN(ndim);
1846
1930
  for (i=0; i<ndim; i++) {
1847
1931
  dim[i] = NUM2SIZE(rb_ary_entry(rdim, i));
1848
1932
  }
1849
- CA_CHECK_DIM(ndim, dim);
1933
+ CA_CHECK_DIM_GIVEN(ndim, dim);
1850
1934
  elements = ca_shape_elements(ndim, dim, 1);
1851
1935
 
1852
1936
  info.range_check = 1;
@@ -1919,8 +2003,7 @@ rb_ca_s_scan_index (VALUE self, VALUE rdim, VALUE ridx)
1919
2003
  case CA_REG_GRID:
1920
2004
  case CA_REG_MAPPING:
1921
2005
  case CA_REG_METHOD_CALL:
1922
- case CA_REG_MEMBER:
1923
- case CA_REG_ATTRIBUTE:
2006
+ case CA_REG_MEMBER:
1924
2007
  break;
1925
2008
  default:
1926
2009
  rb_raise(rb_eArgError, "unknown index specification");
@@ -2016,10 +2099,11 @@ parse_shape_kwarg (VALUE rshape, int *out_ndim, ca_size_t *dim,
2016
2099
 
2017
2100
  Check_Type(rshape, T_ARRAY);
2018
2101
  ndim = (int) RARRAY_LEN(rshape);
2019
- if ( ndim < 1 || ndim > CA_RANK_MAX ) {
2102
+ if ( ndim < 1 ) {
2020
2103
  rb_raise(rb_eArgError, "invalid shape ndim %d (must be 1..%d)",
2021
2104
  ndim, CA_RANK_MAX);
2022
2105
  }
2106
+ ca_check_rank_fits(ndim, NULL);
2023
2107
  for (i = 0; i < ndim; i++) {
2024
2108
  dim[i] = NUM2SIZE(rb_ary_entry(rshape, i));
2025
2109
  if ( dim[i] < 0 ) {
@@ -2103,6 +2187,7 @@ addr2index_do (int ndim, ca_size_t *dim, ca_size_t elements, VALUE raddr)
2103
2187
  }
2104
2188
 
2105
2189
  ca_detach(cin);
2190
+ RB_GC_GUARD(raddr); /* the CA_SIZE view is reached only through cin */
2106
2191
  return out;
2107
2192
  }
2108
2193
  }
@@ -2306,7 +2391,7 @@ rb_ca_s_addr2index (int argc, VALUE *argv, VALUE klass)
2306
2391
  static VALUE
2307
2392
  rb_ca_s_index2addr (int argc, VALUE *argv, VALUE klass)
2308
2393
  {
2309
- VALUE rest, opts;
2394
+ VALUE rest, opts, out;
2310
2395
  ID kw_ids[1];
2311
2396
  VALUE kw_vals[1];
2312
2397
  int ndim;
@@ -2320,7 +2405,10 @@ rb_ca_s_index2addr (int argc, VALUE *argv, VALUE klass)
2320
2405
  }
2321
2406
  rb_get_kwargs(opts, kw_ids, 1, 0, kw_vals);
2322
2407
  parse_shape_kwarg(kw_vals[0], &ndim, dim, &elements);
2323
- return index2addr_do(ndim, dim, (int) RARRAY_LEN(rest), RARRAY_PTR(rest));
2408
+ /* index2addr_do reads, and replaces, its arguments in rest */
2409
+ out = index2addr_do(ndim, dim, (int) RARRAY_LEN(rest), RARRAY_PTR(rest));
2410
+ RB_GC_GUARD(rest);
2411
+ return out;
2324
2412
  }
2325
2413
 
2326
2414