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
@@ -14,13 +14,14 @@
14
14
  - a single negative-index normaliser used by all handlers
15
15
  - fast paths (POINT / ADDRESS, ndim == argc, all-FIXNUM) at
16
16
  the top of the entry point
17
- - single-character alphabetic Symbols (:a-:z / :A-:Z) reserved
18
- for future contraction notation; the classifier raises
19
- NotImplementedError when they appear.
17
+ - single-character alphabetic Symbols (:a-:z / :A-:Z) are
18
+ reserved and kept free of meaning; the classifier raises
19
+ IndexError when they appear.
20
20
 
21
21
  --------------------------------------------------------------------------- */
22
22
 
23
23
  #include "carray.h"
24
+ #include <math.h>
24
25
  #include "carray_internal.h" /* ca_index_in_range_at */
25
26
  #include "carray_index_classifier.h"
26
27
 
@@ -96,6 +97,32 @@ ca_classifier_normalize_axis_index (ca_size_t k, ca_size_t dim, int axis, int ra
96
97
  return k;
97
98
  }
98
99
 
100
+ /* Whether a range picks no cells along an axis of length dim: an exclusive
101
+ range that ends where it starts, or an endless one that starts at the end
102
+ of the axis. `last` has been counted from the end already; `start` has
103
+ not. A range in CArray otherwise always picks a cell -- one ending
104
+ before its start reads backwards. */
105
+ static inline int
106
+ ca_classifier_range_is_empty (ca_size_t start, ca_size_t last, int excl,
107
+ int endless, ca_size_t dim)
108
+ {
109
+ ca_size_t s = ( start < 0 ) ? start + dim : start;
110
+ return endless ? ( s == dim ) : ( excl && s == last );
111
+ }
112
+
113
+ /* The first index of a run of cells along an axis. A run of no cells may
114
+ start at the end of the axis (position n), as an empty slice at the end
115
+ of a Ruby Array does; a run with cells has to start on one. */
116
+ static inline ca_size_t
117
+ ca_classifier_run_start (ca_size_t idx, ca_size_t n, int axis, int empty)
118
+ {
119
+ ca_size_t i = ( idx < 0 ) ? idx + n : idx;
120
+ if ( i < 0 || i > n || ( ! empty && i == n ) ) {
121
+ ca_index_out_of_range_at(idx, n, axis);
122
+ }
123
+ return i;
124
+ }
125
+
99
126
  /* Flat-address normalisation for the `argc == 1, ndim > 1,
100
127
  Integer` path. CA_CHECK_INDEX does not carry axis info in its
101
128
  message, which is the intended behaviour on the flat path. */
@@ -108,6 +135,129 @@ ca_classifier_normalize_flat_address (ca_size_t addr, ca_size_t elements, int ra
108
135
  return addr;
109
136
  }
110
137
 
138
+ NORETURN(static void ca_classifier_invalid_key (VALUE v, int axis));
139
+ static void
140
+ ca_classifier_invalid_key (VALUE v, int axis)
141
+ {
142
+ volatile VALUE s = rb_inspect(v);
143
+ rb_raise(rb_eIndexError,
144
+ "%s: object '%s' is invalid for the index for reference at %i-dim",
145
+ ca_calling_method_name(), StringValueCStr(s), axis);
146
+ }
147
+
148
+ /* An Integer past int64 is past the end of any axis, but has no ca_size_t
149
+ to report through ca_index_out_of_range_at; it is reported as given. */
150
+ NORETURN(static void ca_classifier_key_too_large (VALUE v, const char *where));
151
+ static void
152
+ ca_classifier_key_too_large (VALUE v, const char *where)
153
+ {
154
+ volatile VALUE s = rb_inspect(v);
155
+ rb_raise(rb_eIndexError, "%s: index %s is out of range%s",
156
+ ca_calling_method_name(), StringValueCStr(s), where);
157
+ }
158
+
159
+ /* A key names a position when it holds an integer that fits ca_size_t
160
+ (int64): an Integer of that size, Bignum included (an axis of a view that
161
+ holds no memory can be longer than a Fixnum reaches), or a Float with no
162
+ fraction while it is exact (|v| <= 2**53; past that it cannot tell
163
+ neighbouring integers apart, and a count from `sum` is a Float).
164
+ Returns 1 with the position in *out, 2 for an Integer past int64 (past
165
+ every axis, which is no longer than int64), and 0 for anything else (a
166
+ Float with a fraction or past 2**53, NaN, Inf, a Rational, a String). */
167
+ static int
168
+ ca_classifier_key_position (VALUE v, ca_size_t *out)
169
+ {
170
+ if ( FIXNUM_P(v) ) {
171
+ *out = (ca_size_t) FIX2LONG(v);
172
+ return 1;
173
+ }
174
+ if ( RB_FLOAT_TYPE_P(v) ) {
175
+ double d = RFLOAT_VALUE(v);
176
+ if ( isfinite(d) && d == floor(d) && fabs(d) <= 9007199254740992.0 ) {
177
+ *out = (ca_size_t) d;
178
+ return 1;
179
+ }
180
+ return 0;
181
+ }
182
+ if ( RB_TYPE_P(v, T_BIGNUM) ) {
183
+ uint64_t mag;
184
+ int sign = rb_integer_pack(v, &mag, 1, sizeof(mag), 0,
185
+ INTEGER_PACK_NATIVE_BYTE_ORDER);
186
+ if ( sign == 2 || sign == -2
187
+ || ( sign > 0 && mag > (uint64_t) INT64_MAX )
188
+ || ( sign < 0 && mag > (uint64_t) INT64_MAX + 1 ) ) {
189
+ return 2; /* does not fit int64 */
190
+ }
191
+ *out = ( sign < 0 ) ? (ca_size_t) (0 - mag) : (ca_size_t) mag;
192
+ return 1;
193
+ }
194
+ return 0;
195
+ }
196
+
197
+ /* A key that has to name a position: a scalar key, or one inside a Range,
198
+ an Array form or a step. `where` follows the value in the out-of-range
199
+ message. */
200
+ static ca_size_t
201
+ ca_classifier_key_int_where (VALUE v, int axis, const char *where)
202
+ {
203
+ ca_size_t k;
204
+ switch ( ca_classifier_key_position(v, &k) ) {
205
+ case 1:
206
+ return k;
207
+ case 2:
208
+ ca_classifier_key_too_large(v, where);
209
+ default:
210
+ ca_classifier_invalid_key(v, axis);
211
+ }
212
+ }
213
+
214
+ static ca_size_t
215
+ ca_classifier_key_int (VALUE v, int axis)
216
+ {
217
+ char where[32];
218
+ snprintf(where, sizeof(where), " on axis %d", axis);
219
+ return ca_classifier_key_int_where(v, axis, where);
220
+ }
221
+
222
+ int
223
+ ca_index_key_is_scalar (VALUE arg)
224
+ {
225
+ return FIXNUM_P(arg) || RB_FLOAT_TYPE_P(arg) || RB_TYPE_P(arg, T_BIGNUM);
226
+ }
227
+
228
+ /* A step: an integer like any key, except that one past int64 is not past
229
+ anything -- it is a step larger than any axis, so it reaches one cell.
230
+ It is read as the largest int64 of its sign, which does the same. */
231
+ static ca_size_t
232
+ ca_classifier_key_step (VALUE v, int axis)
233
+ {
234
+ ca_size_t k;
235
+ if ( ca_classifier_key_position(v, &k) == 2 ) {
236
+ return rb_big_sign(v) ? (ca_size_t) INT64_MAX : (ca_size_t) INT64_MIN;
237
+ }
238
+ return ca_classifier_key_int(v, axis);
239
+ }
240
+
241
+ /* An axis of length 0 has no cells: a range on it reads none when it starts
242
+ at 0 (as `[][0..-1]` does), and is out of range otherwise. Returns 1 when
243
+ the axis was filled in. */
244
+ static int
245
+ ca_classifier_block_on_empty_axis (CAIndexInfo *info, int axis, ca_size_t dim,
246
+ ca_size_t start)
247
+ {
248
+ if ( dim != 0 ) {
249
+ return 0;
250
+ }
251
+ if ( start != 0 ) {
252
+ ca_index_out_of_range_at(start, dim, axis);
253
+ }
254
+ info->index_type[axis] = CA_IDX_BLOCK;
255
+ info->index[axis].block.start = 0;
256
+ info->index[axis].block.count = 0;
257
+ info->index[axis].block.step = 1;
258
+ return 1;
259
+ }
260
+
111
261
  /* -------------------------------------------------------------------- */
112
262
  /* Fast paths. */
113
263
 
@@ -131,8 +281,8 @@ ca_classifier_try_point_fast_path (ca_classifier_ctx_t *ctx)
131
281
  for (i = 0; i < ctx->argc; i++) {
132
282
  ca_size_t k = FIX2LONG(ctx->argv[i]);
133
283
  if ( ctx->info->range_check ) {
134
- /* Bignum is rejected by FIXNUM_P above; the slow path's
135
- rb_obj_is_kind_of(arg, rb_cInteger) picks it up so the
284
+ /* Bignum and Float are rejected by FIXNUM_P above; the slow path's
285
+ integer-key branch picks them up so the
136
286
  Integer semantics are preserved end-to-end. */
137
287
  k = ca_classifier_normalize_axis_index(k, ctx->ca_dim[i], (int) i,
138
288
  ctx->info->range_check);
@@ -162,12 +312,13 @@ ca_classifier_try_address_fast_path (ca_classifier_ctx_t *ctx)
162
312
 
163
313
  /* -------------------------------------------------------------------- */
164
314
  /* Single-character alphabetic Symbols (`:a`-`:z` / `:A`-`:Z`) are
165
- reserved for future contraction notation. Raises
166
- NotImplementedError rather than the generic IndexError. The
167
- caller passes any Symbol that is not `:_` / `:%`; a
168
- non-reserved Symbol returns without action. */
315
+ reserved: no index form gives them a meaning, and none should
316
+ until one is decided. Raises IndexError saying so, rather than
317
+ the generic "invalid as the index" message. The caller passes
318
+ any Symbol that is not `:_` / `:%`; a non-reserved Symbol
319
+ returns without action. */
169
320
  static inline void
170
- ca_classifier_check_reserved_contraction_symbol (VALUE sym)
321
+ ca_classifier_check_reserved_symbol (VALUE sym)
171
322
  {
172
323
  const char *name;
173
324
  if ( ! SYMBOL_P(sym) ) return;
@@ -178,7 +329,7 @@ ca_classifier_check_reserved_contraction_symbol (VALUE sym)
178
329
  || ( name[0] >= 'A' && name[0] <= 'Z' ) )
179
330
  && name[0] != '_' /* :_ is iterator marker, handled separately */ ) {
180
331
  rb_raise(rb_eIndexError,
181
- "symbol :%s is reserved for future contraction notation",
332
+ "symbol :%s is reserved",
182
333
  name);
183
334
  }
184
335
  }
@@ -206,25 +357,32 @@ ca_classifier_axis_from_range (ca_classifier_ctx_t *ctx, int axis, VALUE arg)
206
357
  iv_end = CA_CLASSIFIER_RANGE_END(arg);
207
358
  iv_excl = CA_CLASSIFIER_RANGE_EXCL(arg);
208
359
 
209
- start = NIL_P(iv_beg) ? 0 : NUM2SIZE(iv_beg);
210
- last = NIL_P(iv_end) ? -1 : NUM2SIZE(iv_end);
360
+ start = NIL_P(iv_beg) ? 0 : ca_classifier_key_int(iv_beg, axis);
361
+ last = NIL_P(iv_end) ? -1 : ca_classifier_key_int(iv_end, axis);
211
362
  excl = RTEST(iv_excl);
212
363
 
213
- info->index_type[axis] = CA_IDX_BLOCK;
214
-
215
- if ( info->range_check ) {
216
- start = ca_index_in_range_at(start, dim, axis);
364
+ if ( ca_classifier_block_on_empty_axis(info, axis, dim, start) ) {
365
+ return;
217
366
  }
367
+ info->index_type[axis] = CA_IDX_BLOCK;
218
368
 
219
369
  if ( last < 0 ) {
220
370
  last += dim; /* don't use CA_CHECK_INDEX for excl */
221
371
  }
222
- if ( excl && start == last ) {
372
+ /* No cells: an exclusive range that ends where it starts, or an endless
373
+ one that starts at the end of the axis. */
374
+ if ( ca_classifier_range_is_empty(start, last, excl, NIL_P(iv_end), dim) ) {
375
+ if ( info->range_check ) {
376
+ start = ca_classifier_run_start(start, dim, axis, 1);
377
+ }
223
378
  info->index[axis].block.start = start;
224
379
  info->index[axis].block.count = 0;
225
380
  info->index[axis].block.step = 1;
226
381
  return;
227
382
  }
383
+ if ( info->range_check ) {
384
+ start = ca_index_in_range_at(start, dim, axis);
385
+ }
228
386
  if ( excl ) {
229
387
  last += ( last >= start ) ? -1 : 1;
230
388
  }
@@ -240,63 +398,66 @@ ca_classifier_axis_from_range (ca_classifier_ctx_t *ctx, int axis, VALUE arg)
240
398
  info->index[axis].block.step = step;
241
399
  }
242
400
 
243
- #ifdef HAVE_RB_ARITHMETIC_SEQUENCE_EXTRACT
244
- /* ArithSeq → BLOCK */
401
+ /* A run of cells from `iv_beg` toward `iv_end` (inclusive unless excl)
402
+ taking every `step`-th: an ArithmeticSequence, or the `[range, step]`
403
+ form. The step says the direction, as it does for Ruby: one that points
404
+ away from the end reads nothing, and an endless run with a negative
405
+ step goes down to the first cell. A begin of nil starts at the end the
406
+ step walks from. */
245
407
  static void
246
- ca_classifier_axis_from_arithseq (ca_classifier_ctx_t *ctx, int axis, VALUE arg)
408
+ ca_classifier_block_stepped (ca_classifier_ctx_t *ctx, int axis,
409
+ VALUE iv_beg, VALUE iv_end, int excl, ca_size_t step)
247
410
  {
248
- ca_size_t start, last, count, step, bound;
249
- int excl;
250
- volatile VALUE iv_beg, iv_end, iv_excl;
251
- rb_arithmetic_sequence_components_t x;
411
+ ca_size_t start, last, count, bound, s;
412
+ int endless = NIL_P(iv_end);
252
413
  CAIndexInfo *info = ctx->info;
253
414
  ca_size_t dim = ctx->ca_dim[axis];
254
415
 
255
- rb_arithmetic_sequence_extract(arg, &x);
256
- iv_beg = x.begin;
257
- iv_end = x.end;
258
- iv_excl = x.exclude_end;
259
- step = NUM2SIZE(x.step);
260
-
261
- start = NIL_P(iv_beg) ? 0 : NUM2SIZE(iv_beg);
262
- last = NIL_P(iv_end) ? -1 : NUM2SIZE(iv_end);
263
- excl = RTEST(iv_excl);
264
-
265
416
  if ( step == 0 ) {
266
417
  rb_raise(rb_eArgError,
267
418
  "step in index equals to 0 in block reference");
268
419
  }
269
-
420
+ start = NIL_P(iv_beg) ? ( step < 0 ? dim - 1 : 0 )
421
+ : ca_classifier_key_int(iv_beg, axis);
422
+ last = endless ? ( step < 0 ? 0 : dim - 1 )
423
+ : ca_classifier_key_int(iv_end, axis);
424
+ if ( NIL_P(iv_beg) && dim == 0 ) {
425
+ start = 0;
426
+ }
427
+ if ( ca_classifier_block_on_empty_axis(info, axis, dim, start) ) {
428
+ return;
429
+ }
270
430
  info->index_type[axis] = CA_IDX_BLOCK;
271
-
272
- /* CAREFUL: always range-check start on the ArithSeq path,
273
- ignoring info->range_check. An ArithSeq with a negative or
274
- out-of-range start silently produces an empty region if this
275
- check is deferred, which upstream callers do not expect. */
276
- start = ca_index_in_range_at(start, dim, axis);
277
-
278
- if ( last < 0 ) {
431
+ if ( ! endless && last < 0 ) {
279
432
  last += dim;
280
433
  }
281
- if ( excl && start == last ) {
282
- info->index[axis].block.start = start;
434
+ /* CAREFUL: start is range-checked here whatever info->range_check says.
435
+ A run that starts outside the axis would otherwise read as an empty
436
+ region, which upstream callers do not expect. */
437
+ s = ( start < 0 ) ? start + dim : start;
438
+ if ( ( endless && step > 0 && s == dim ) || ( ! endless && excl && s == last ) ) {
439
+ info->index[axis].block.start = ca_classifier_run_start(start, dim, axis, 1);
283
440
  info->index[axis].block.count = 0;
284
441
  info->index[axis].block.step = 1;
285
442
  return;
286
443
  }
287
- if ( excl ) {
444
+ start = ca_index_in_range_at(start, dim, axis);
445
+ if ( excl && ! endless ) {
288
446
  last += ( last >= start ) ? -1 : 1;
289
447
  }
290
448
  if ( last < 0 || last >= dim ) {
291
449
  ca_index_out_of_range_at(last, dim, axis);
292
450
  }
451
+ if ( (last > start && step < 0) || (last < start && step > 0) ) {
452
+ info->index[axis].block.start = start;
453
+ info->index[axis].block.count = 0;
454
+ info->index[axis].block.step = 1;
455
+ return;
456
+ }
293
457
  /* start and last are both inside the axis, so their distance fits; the
294
458
  step is taken by magnitude unsigned, since -step overflows at its
295
459
  minimum. (count - 1) * step below is then at most that distance. */
296
- if ( (last > start && step < 0) || (last < start && step > 0) ) {
297
- count = 1;
298
- }
299
- else {
460
+ {
300
461
  unsigned long long span = (unsigned long long)
301
462
  (last >= start ? last - start : start - last);
302
463
  unsigned long long ustep = step < 0 ? 0ULL - (unsigned long long) step
@@ -309,6 +470,17 @@ ca_classifier_axis_from_arithseq (ca_classifier_ctx_t *ctx, int axis, VALUE arg)
309
470
  info->index[axis].block.count = count;
310
471
  info->index[axis].block.step = step;
311
472
  }
473
+
474
+ #ifdef HAVE_RB_ARITHMETIC_SEQUENCE_EXTRACT
475
+ /* ArithSeq → BLOCK */
476
+ static void
477
+ ca_classifier_axis_from_arithseq (ca_classifier_ctx_t *ctx, int axis, VALUE arg)
478
+ {
479
+ rb_arithmetic_sequence_components_t x;
480
+ rb_arithmetic_sequence_extract(arg, &x);
481
+ ca_classifier_block_stepped(ctx, axis, x.begin, x.end, RTEST(x.exclude_end),
482
+ ca_classifier_key_step(x.step, axis));
483
+ }
312
484
  #endif
313
485
 
314
486
  /* T_ARRAY → BLOCK. Accepts [nil] / [Range] / [Integer] (len 1),
@@ -335,7 +507,7 @@ ca_classifier_axis_from_array (ca_classifier_ctx_t *ctx, int axis, VALUE arg)
335
507
  }
336
508
  /* [Integer] */
337
509
  {
338
- ca_size_t given = NUM2SIZE(a0);
510
+ ca_size_t given = ca_classifier_key_int(a0, axis);
339
511
  ca_size_t start = given;
340
512
  start = ca_index_in_range_at(start, dim, axis);
341
513
  info->index[axis].block.start = start;
@@ -350,83 +522,58 @@ ca_classifier_axis_from_array (ca_classifier_ctx_t *ctx, int axis, VALUE arg)
350
522
  VALUE a1 = rb_ary_entry(arg, 1);
351
523
 
352
524
  if ( NIL_P(a0) ) { /* [nil, step] */
353
- ca_size_t start = 0, last, count, step, bound;
354
- step = NUM2SIZE(a1);
355
- if ( step == 0 ) {
356
- rb_raise(rb_eArgError,
357
- "step in index equals to 0 in block reference");
358
- }
359
- last = dim - 1;
360
- count = ( step < 0 ) ? 1 : (last / step + 1);
361
- bound = start + (count - 1) * step;
362
- bound = ca_index_in_range_at(bound, dim, axis);
363
- info->index[axis].block.start = start;
364
- info->index[axis].block.count = count;
365
- info->index[axis].block.step = step;
366
- return;
367
- }
368
-
369
- if ( rb_obj_is_kind_of(a0, rb_cRange) ) { /* [Range, step] */
370
- ca_size_t start, last, count, step, bound;
371
- int excl;
372
- volatile VALUE iv_beg, iv_end, iv_excl;
373
- iv_beg = CA_CLASSIFIER_RANGE_BEG(a0);
374
- iv_end = CA_CLASSIFIER_RANGE_END(a0);
375
- iv_excl = CA_CLASSIFIER_RANGE_EXCL(a0);
376
- start = NIL_P(iv_beg) ? 0 : NUM2SIZE(iv_beg);
377
- last = NIL_P(iv_end) ? -1 : NUM2SIZE(iv_end);
378
- excl = RTEST(iv_excl);
379
- step = NUM2SIZE(a1);
525
+ ca_size_t start = 0, count, step;
526
+ step = ca_classifier_key_step(a1, axis);
380
527
  if ( step == 0 ) {
381
528
  rb_raise(rb_eArgError,
382
529
  "step in index equals to 0 in block reference");
383
530
  }
384
- start = ca_index_in_range_at(start, dim, axis);
385
- if ( last < 0 ) last += dim;
386
- if ( excl && start == last ) {
387
- info->index[axis].block.start = start;
388
- info->index[axis].block.count = 0;
389
- info->index[axis].block.step = 1;
390
- return;
391
- }
392
- if ( excl ) {
393
- last += ( last >= start ) ? -1 : 1;
394
- }
395
- if ( last < 0 || last >= dim ) {
396
- ca_index_out_of_range_at(last, dim, axis);
531
+ /* every cell of the axis from the first at that step: none of an
532
+ axis of length 0. A negative step from the first cell reaches only
533
+ it. */
534
+ if ( dim == 0 ) {
535
+ count = 0;
536
+ step = 1;
397
537
  }
398
- if ( (last > start && step < 0) || (last < start && step > 0) ) {
538
+ else if ( step < 0 ) {
399
539
  count = 1;
400
540
  }
401
541
  else {
402
- unsigned long long span = (unsigned long long)
403
- (last >= start ? last - start : start - last);
404
- unsigned long long ustep = step < 0 ? 0ULL - (unsigned long long) step
405
- : (unsigned long long) step;
406
- count = (ca_size_t) (span / ustep) + 1;
542
+ count = (dim - 1) / step + 1;
407
543
  }
408
- bound = start + (count - 1) * step;
409
- bound = ca_index_in_range_at(bound, dim, axis);
410
544
  info->index[axis].block.start = start;
411
545
  info->index[axis].block.count = count;
412
546
  info->index[axis].block.step = step;
413
547
  return;
414
548
  }
415
549
 
550
+ if ( rb_obj_is_kind_of(a0, rb_cRange) ) { /* [Range, step] */
551
+ ca_classifier_block_stepped(ctx, axis,
552
+ CA_CLASSIFIER_RANGE_BEG(a0),
553
+ CA_CLASSIFIER_RANGE_END(a0),
554
+ RTEST(CA_CLASSIFIER_RANGE_EXCL(a0)),
555
+ ca_classifier_key_step(a1, axis));
556
+ return;
557
+ }
558
+
416
559
  /* [start, count] */
417
560
  {
418
561
  ca_size_t start, count, bound;
419
- start = NUM2SIZE(a0);
420
- count = NUM2SIZE(a1);
421
- if ( __builtin_add_overflow(start, count - 1, &bound) ) {
562
+ ca_size_t given;
563
+ given = ca_classifier_key_int(a0, axis);
564
+ count = ca_classifier_key_int(a1, axis);
565
+ /* The start counts from the end when negative; the last cell is
566
+ counted from that start and has to be inside as it is. A count
567
+ of 0 may start at the end of the axis. */
568
+ start = ca_classifier_run_start(given, dim, axis, count == 0);
569
+ if ( count > 0 &&
570
+ ( __builtin_add_overflow(start, count - 1, &bound) || bound >= dim ) ) {
422
571
  rb_raise(rb_eIndexError,
423
572
  "%s: index [%" PRId64 ", %" PRId64 "] is out of range for "
424
573
  "length %" PRId64 " on axis %i",
425
- ca_calling_method_name(), (ca_size_t) start, (ca_size_t) count,
574
+ ca_calling_method_name(), (ca_size_t) given, (ca_size_t) count,
426
575
  (ca_size_t) dim, axis);
427
576
  }
428
- start = ca_index_in_range_at(start, dim, axis);
429
- bound = ca_index_in_range_at(bound, dim, axis);
430
577
  info->index[axis].block.start = start;
431
578
  info->index[axis].block.count = count;
432
579
  info->index[axis].block.step = 1;
@@ -435,26 +582,29 @@ ca_classifier_axis_from_array (ca_classifier_ctx_t *ctx, int axis, VALUE arg)
435
582
  }
436
583
 
437
584
  if ( len == 3 ) { /* [start, count, step] */
438
- ca_size_t start, count, step, bound;
439
- start = NUM2SIZE(rb_ary_entry(arg, 0));
440
- count = NUM2SIZE(rb_ary_entry(arg, 1));
441
- step = NUM2SIZE(rb_ary_entry(arg, 2));
585
+ ca_size_t given, start, count, step, bound;
586
+ given = ca_classifier_key_int(rb_ary_entry(arg, 0), axis);
587
+ count = ca_classifier_key_int(rb_ary_entry(arg, 1), axis);
588
+ step = ca_classifier_key_step(rb_ary_entry(arg, 2), axis);
442
589
  if ( step == 0 ) {
443
590
  rb_raise(rb_eArgError,
444
591
  "step in index equals to 0 in block reference");
445
592
  }
446
- /* A count and a step whose last cell lies past the range of an index
447
- are out of range, not a wrapped bound that lands back inside. */
448
- if ( __builtin_mul_overflow(count - 1, step, &bound) ||
449
- __builtin_add_overflow(start, bound, &bound) ) {
593
+ /* The start counts from the end when negative; the last cell is
594
+ counted from that start and has to be inside as it is, not a bound
595
+ that wraps back in from either end. A count of 0 may start at the
596
+ end of the axis. */
597
+ start = ca_classifier_run_start(given, dim, axis, count == 0);
598
+ if ( count > 0 &&
599
+ ( __builtin_mul_overflow(count - 1, step, &bound) ||
600
+ __builtin_add_overflow(start, bound, &bound) ||
601
+ bound < 0 || bound >= dim ) ) {
450
602
  rb_raise(rb_eIndexError,
451
603
  "%s: index [%" PRId64 ", %" PRId64 ", %" PRId64 "] is out of "
452
604
  "range for length %" PRId64 " on axis %i",
453
- ca_calling_method_name(), (ca_size_t) start, (ca_size_t) count,
605
+ ca_calling_method_name(), (ca_size_t) given, (ca_size_t) count,
454
606
  (ca_size_t) step, (ca_size_t) dim, axis);
455
607
  }
456
- start = ca_index_in_range_at(start, dim, axis);
457
- bound = ca_index_in_range_at(bound, dim, axis);
458
608
  info->index[axis].block.start = start;
459
609
  info->index[axis].block.count = count;
460
610
  info->index[axis].block.step = step;
@@ -482,8 +632,8 @@ ca_classifier_dispatch_axis_arg (ca_classifier_ctx_t *ctx, int axis, VALUE arg,
482
632
  CAIndexInfo *info = ctx->info;
483
633
  ca_size_t dim = ctx->ca_dim[axis];
484
634
 
485
- if ( rb_obj_is_kind_of(arg, rb_cInteger) ) {
486
- ca_size_t k = NUM2SIZE(arg);
635
+ if ( ca_index_key_is_scalar(arg) ) {
636
+ ca_size_t k = ca_classifier_key_int(arg, axis);
487
637
  info->index_type[axis] = CA_IDX_SCALAR;
488
638
  if ( info->range_check ) {
489
639
  k = ca_classifier_normalize_axis_index(k, dim, axis, info->range_check);
@@ -526,7 +676,7 @@ ca_classifier_dispatch_axis_arg (ca_classifier_ctx_t *ctx, int axis, VALUE arg,
526
676
  "symbol :_ (newaxis) is handled at the [] / []= level, "
527
677
  "not valid in scan_index");
528
678
  }
529
- ca_classifier_check_reserved_contraction_symbol(arg); /* raises if reserved */
679
+ ca_classifier_check_reserved_symbol(arg); /* raises if reserved */
530
680
  rb_raise(rb_eIndexError,
531
681
  "symbol :%"PRIsVALUE" is invalid as the index for slab iterator "
532
682
  "(use :> instead)",
@@ -547,12 +697,7 @@ ca_classifier_dispatch_axis_arg (ca_classifier_ctx_t *ctx, int axis, VALUE arg,
547
697
  "(should be boolean or integer)",
548
698
  ca_type_name[ci->data_type], axis);
549
699
  }
550
- {
551
- VALUE inspect = rb_inspect(arg);
552
- rb_raise(rb_eIndexError,
553
- "object '%s' is invalid for the index for reference at %i-dim",
554
- StringValuePtr(inspect), axis);
555
- }
700
+ ca_classifier_invalid_key(arg, axis);
556
701
  }
557
702
 
558
703
  /* -------------------------------------------------------------------- */
@@ -562,66 +707,117 @@ ca_classifier_dispatch_axis_arg (ca_classifier_ctx_t *ctx, int axis, VALUE arg,
562
707
  rubber marker is honoured; a second one falls through to the
563
708
  per-axis dispatcher and raises. */
564
709
 
565
- static void
566
- ca_classifier_expand_rubber_dim (ca_classifier_ctx_t *ctx)
710
+ /* Expand the rubber marker (`false` or `:~`) of a key list for an
711
+ array of ndim axes into as many nils as it stands for, writing the
712
+ list into out (CA_RANK_MAX slots). Returns the new count, or -1 when
713
+ the list has no rubber marker (out is then untouched). Only the first
714
+ marker is honoured; a later one is left for the axis reader to refuse. */
715
+ long
716
+ ca_index_expand_rubber (int ndim, long argc, const VALUE *argv, VALUE *out)
567
717
  {
568
718
  long i, j;
569
- long argc = ctx->argc;
570
- VALUE *argv = ctx->argv;
571
719
  int rubber_pos = -1;
572
- long expanded_argc;
573
- int rndim;
720
+ int rndim;
574
721
 
722
+ ca_classifier_sym_cache_init();
575
723
  for (i = 0; i < argc; i++) {
576
724
  if ( argv[i] == Qfalse || argv[i] == ca_classifier_sym_tilde ) {
577
- rubber_pos = (int) i; /* :~ is an alias for the `false` rubber marker */
578
- /* Only the first rubber marker is honoured; later occurrences fall
579
- through to the per-axis dispatcher and raise. */
725
+ rubber_pos = (int) i;
580
726
  break;
581
727
  }
582
728
  }
583
-
584
729
  if ( rubber_pos < 0 ) {
585
- /* No rubber dim — validate argc == ndim and pass through. */
586
- if ( argc != ctx->ca_ndim ) {
587
- rb_raise(rb_eIndexError,
588
- "%i indices given for a carray of ndim %i",
589
- (int) argc, ctx->ca_ndim);
590
- }
591
- return;
730
+ return -1;
592
731
  }
593
-
594
- if ( argc > ctx->ca_ndim + 1 ) {
732
+ if ( argc > ndim + 1 ) {
595
733
  rb_raise(rb_eIndexError,
596
- "index specification exceeds the ndim of carray (%i)",
597
- ctx->ca_ndim);
734
+ "index specification exceeds the ndim of carray (%i)", ndim);
598
735
  }
599
-
600
- /* rndim = number of nils the rubber expands to.
601
- *
602
- * CAREFUL: rndim == 0 is a valid outcome — the rubber consumes
603
- * nothing when argc == ndim + 1 (e.g. `[false, 0, 1]` on
604
- * ndim = 2 collapses to `[0, 1]`). Clamping only at the
605
- * negative side preserves that path. */
606
- rndim = ctx->ca_ndim - (int)argc + 1;
736
+ /* CAREFUL: rndim == 0 is a valid outcome -- the rubber consumes nothing
737
+ when argc == ndim + 1 (e.g. `[false, 0, 1]` on ndim = 2 collapses to
738
+ `[0, 1]`). Clamping only at the negative side preserves that path. */
739
+ rndim = ndim - (int) argc + 1;
607
740
  if ( rndim < 0 ) rndim = 0;
608
- expanded_argc = argc - 1 + rndim;
609
-
610
741
  j = 0;
611
742
  for (i = 0; i < argc; i++) {
612
743
  if ( i == rubber_pos ) {
613
744
  int k;
614
745
  for (k = 0; k < rndim; k++) {
615
- ctx->argv_expanded[j++] = Qnil;
746
+ out[j++] = Qnil;
616
747
  }
617
748
  } else {
618
- ctx->argv_expanded[j++] = argv[i];
749
+ out[j++] = argv[i];
619
750
  }
620
751
  }
621
- ctx->argc = expanded_argc;
752
+ return j;
753
+ }
754
+
755
+ static void
756
+ ca_classifier_expand_rubber_dim (ca_classifier_ctx_t *ctx)
757
+ {
758
+ long n = ca_index_expand_rubber(ctx->ca_ndim, ctx->argc, ctx->argv,
759
+ ctx->argv_expanded);
760
+ if ( n < 0 ) {
761
+ /* No rubber dim -- validate argc == ndim and pass through. */
762
+ if ( ctx->argc != ctx->ca_ndim ) {
763
+ rb_raise(rb_eIndexError,
764
+ "%i indices given for a carray of ndim %i",
765
+ (int) ctx->argc, ctx->ca_ndim);
766
+ }
767
+ return;
768
+ }
769
+ ctx->argc = n;
622
770
  ctx->argv = ctx->argv_expanded;
623
771
  }
624
772
 
773
+ /* Read one key along an axis of length dim, as the indexer reads it when
774
+ the key is not a CArray: an Integer, nil, a Range, an
775
+ ArithmeticSequence or an Array form. Fills *start / *count / *step and
776
+ returns CA_IDX_SCALAR (count 1), CA_IDX_ALL (the whole axis) or
777
+ CA_IDX_BLOCK. Anything else raises IndexError. grid and CASelectAxis
778
+ read their non-CArray keys through this, so a key means the same cells
779
+ whichever view the indexer builds. */
780
+ int
781
+ ca_index_axis_key (VALUE arg, ca_size_t dim, int axis,
782
+ ca_size_t *start, ca_size_t *count, ca_size_t *step)
783
+ {
784
+ ca_classifier_ctx_t ctx;
785
+ CAIndexInfo info;
786
+ ca_size_t dims[CA_RANK_MAX];
787
+ int is_grid = 0;
788
+
789
+ ca_classifier_sym_cache_init();
790
+ if ( axis < 0 || axis >= CA_RANK_MAX ) { /* rank-check: an axis number, not a rank */
791
+ rb_raise(rb_eArgError, "axis %d is out of range", axis);
792
+ }
793
+ dims[axis] = dim;
794
+ ctx.ca_ndim = axis + 1;
795
+ ctx.ca_dim = dims;
796
+ ctx.info = &info;
797
+ info.range_check = 1;
798
+ if ( rb_obj_is_carray(arg) || arg == ca_classifier_sym_gt ) {
799
+ ca_classifier_invalid_key(arg, axis);
800
+ }
801
+ ca_classifier_dispatch_axis_arg(&ctx, axis, arg, &is_grid);
802
+ switch ( info.index_type[axis] ) {
803
+ case CA_IDX_SCALAR:
804
+ *start = info.index[axis].scalar;
805
+ *count = 1;
806
+ *step = 1;
807
+ return CA_IDX_SCALAR;
808
+ case CA_IDX_ALL:
809
+ *start = 0;
810
+ *count = dim;
811
+ *step = 1;
812
+ return CA_IDX_ALL;
813
+ default:
814
+ *start = info.index[axis].block.start;
815
+ *count = info.index[axis].block.count;
816
+ *step = info.index[axis].block.step;
817
+ return CA_IDX_BLOCK;
818
+ }
819
+ }
820
+
625
821
  /* -------------------------------------------------------------------- */
626
822
  /* Final REG decision — walks info->index_type[] and decides
627
823
  POINT / BLOCK / ITERATOR / GRID. */
@@ -681,16 +877,13 @@ ca_classifier_try_argc1_special (ca_classifier_ctx_t *ctx)
681
877
  return 1;
682
878
  }
683
879
 
684
- /* T_STRING: "@name" → ATTRIBUTE, "field" → MEMBER. */
880
+ /* T_STRING: "field" → MEMBER. */
685
881
  if ( TYPE(arg) == T_STRING ) {
686
- const char *s = StringValuePtr(arg);
687
- if ( s[0] == '@' ) {
688
- info->type = CA_REG_ATTRIBUTE;
689
- info->symbol = rb_str_new2(s + 1);
690
- } else {
691
- info->type = CA_REG_MEMBER;
692
- info->symbol = ID2SYM(rb_intern(s));
693
- }
882
+ info->type = CA_REG_MEMBER;
883
+ /* A dynamic Symbol: the collector reclaims it, so indexing with
884
+ strings that name no member does not grow the symbol table. */
885
+ StringValueCStr(arg);
886
+ info->symbol = rb_to_symbol(arg);
694
887
  return 1;
695
888
  }
696
889
 
@@ -707,6 +900,24 @@ ca_classifier_try_argc1_special (ca_classifier_ctx_t *ctx)
707
900
  return 1;
708
901
  }
709
902
  if ( ca_is_boolean_type(cs) ) {
903
+ /* A boolean selector of more than one axis states a shape, and has
904
+ to have the array's; a 1-D one picks by position among all the
905
+ cells. */
906
+ if ( cs->ndim > 1 ) {
907
+ int k, same = ( cs->ndim == ctx->ca_ndim );
908
+ for (k = 0; same && k < cs->ndim; k++) {
909
+ same = ( cs->dim[k] == ctx->ca_dim[k] );
910
+ }
911
+ if ( ! same ) {
912
+ volatile VALUE sshape = rb_ary_new(), ashape = rb_ary_new();
913
+ for (k = 0; k < cs->ndim; k++) rb_ary_push(sshape, SIZE2NUM(cs->dim[k]));
914
+ for (k = 0; k < ctx->ca_ndim; k++) rb_ary_push(ashape, SIZE2NUM(ctx->ca_dim[k]));
915
+ rb_raise(rb_eArgError,
916
+ "boolean selector of shape %"PRIsVALUE" for an array of shape "
917
+ "%"PRIsVALUE" (a selector states its shape; use .flatten to "
918
+ "select by position)", sshape, ashape);
919
+ }
920
+ }
710
921
  if ( ctx->ca_elements != cs->elements ) {
711
922
  rb_raise(rb_eArgError,
712
923
  "mismatch of # of elements ( %" PRId64 " <=> %" PRId64 " ) "
@@ -724,10 +935,11 @@ ca_classifier_try_argc1_special (ca_classifier_ctx_t *ctx)
724
935
  ca_type_name[cs->data_type]);
725
936
  }
726
937
 
727
- /* ndim > 1 with Integer → ADDRESS. Fixnum was caught by the
728
- fast path; this branch handles Bignum. */
729
- if ( ctx->ca_ndim > 1 && rb_obj_is_kind_of(arg, rb_cInteger) ) {
730
- ca_size_t addr = NUM2SIZE(arg);
938
+ /* ndim > 1 with an integer key → ADDRESS. Fixnum was caught by the
939
+ fast path; this branch handles Bignum and Float. */
940
+ if ( ctx->ca_ndim > 1
941
+ && ca_index_key_is_scalar(arg) ) {
942
+ ca_size_t addr = ca_classifier_key_int_where(arg, 0, "");
731
943
  info->type = CA_REG_ADDRESS;
732
944
  info->ndim = 1;
733
945
  if ( info->range_check ) {
@@ -789,7 +1001,7 @@ rb_ca_scan_index_v2 (int ca_ndim, ca_size_t *ca_dim, ca_size_t ca_elements,
789
1001
  /* argv[0] = multi-char Symbol → METHOD_CALL. Single-char
790
1002
  specials (:_ / :%) fall through the strlen check and are
791
1003
  handled downstream (argc == 1 special / fast paths / main
792
- loop). Single-char alphabetic reserved-contraction Symbols
1004
+ loop). Single-char alphabetic reserved Symbols
793
1005
  also fall through here and are raised later by the per-axis
794
1006
  dispatcher's SYMBOL_P branch. */
795
1007
  if ( SYMBOL_P(argv[0]) ) {
@@ -875,8 +1087,8 @@ rb_ca_s_scan_index_v2 (VALUE self, VALUE rdim, VALUE ridx)
875
1087
  elements *= dim[i];
876
1088
  }
877
1089
 
878
- CA_CHECK_RANK(ndim);
879
- CA_CHECK_DIM(ndim, dim);
1090
+ CA_CHECK_RANK_GIVEN(ndim);
1091
+ CA_CHECK_DIM_GIVEN(ndim, dim);
880
1092
 
881
1093
  info.range_check = 1;
882
1094
  rb_ca_scan_index_v2(ndim, dim, elements,