carray 3.0.1 → 3.0.3

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 (229) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +824 -0
  3. data/README.md +5 -6
  4. data/carray.gemspec +1 -1
  5. data/ext/ca_axis_dispatch.c +258 -4
  6. data/ext/ca_axis_group.c +368 -184
  7. data/ext/ca_binop_dispatch.c +54 -32
  8. data/ext/ca_binop_dispatch.h +1 -0
  9. data/ext/ca_categorical_iterator.c +90 -41
  10. data/ext/ca_compare.c +8 -2
  11. data/ext/ca_for_buffer.h +21 -68
  12. data/ext/ca_group_iter.c +27 -11
  13. data/ext/ca_kernel_iterator.c +960 -245
  14. data/ext/ca_kernel_iterator.h +352 -181
  15. data/ext/ca_monop_dispatch.c +4 -4
  16. data/ext/ca_monop_dispatch.h +4 -0
  17. data/ext/ca_obj_array.c +194 -42
  18. data/ext/ca_obj_bincmp.c +152 -121
  19. data/ext/ca_obj_binop.c +107 -210
  20. data/ext/ca_obj_bitarray.c +37 -24
  21. data/ext/ca_obj_bitfield.c +95 -62
  22. data/ext/ca_obj_block.c +5 -4
  23. data/ext/ca_obj_byte_swap.c +40 -40
  24. data/ext/ca_obj_const_string.c +180 -50
  25. data/ext/ca_obj_face.c +428 -62
  26. data/ext/ca_obj_face.h +107 -47
  27. data/ext/ca_obj_fake.c +21 -50
  28. data/ext/ca_obj_farray.c +1 -0
  29. data/ext/ca_obj_field.c +16 -3
  30. data/ext/ca_obj_fixlen_string.c +39 -26
  31. data/ext/ca_obj_grid.c +181 -124
  32. data/ext/ca_obj_meld.c +235 -55
  33. data/ext/ca_obj_moncmp.c +61 -61
  34. data/ext/ca_obj_monop.c +125 -107
  35. data/ext/ca_obj_object.c +551 -233
  36. data/ext/ca_obj_record.c +51 -16
  37. data/ext/ca_obj_reduce.c +5 -8
  38. data/ext/ca_obj_refer.c +117 -33
  39. data/ext/ca_obj_remap.c +57 -17
  40. data/ext/ca_obj_repeat.c +21 -5
  41. data/ext/ca_obj_roll.c +115 -11
  42. data/ext/ca_obj_select.c +88 -75
  43. data/ext/ca_obj_select_axis.c +58 -60
  44. data/ext/ca_obj_shift.c +22 -102
  45. data/ext/ca_obj_stack.c +223 -123
  46. data/ext/ca_obj_stride.c +267 -35
  47. data/ext/ca_obj_string.c +25 -22
  48. data/ext/ca_obj_tile.c +32 -16
  49. data/ext/ca_obj_time.c +23 -25
  50. data/ext/ca_obj_timedelta.c +21 -23
  51. data/ext/ca_obj_transpose.c +12 -3
  52. data/ext/ca_obj_triop.c +110 -117
  53. data/ext/ca_obj_window.c +157 -29
  54. data/ext/ca_op_ipower.c +25 -13
  55. data/ext/ca_op_powi.h +57 -30
  56. data/ext/ca_pow5_table.h +656 -0
  57. data/ext/ca_rng_normal.h +42 -0
  58. data/ext/ca_rng_xoshiro256pp.h +105 -0
  59. data/ext/ca_sort_kernels.h +70 -0
  60. data/ext/ca_sweep_engine.c +385 -198
  61. data/ext/ca_sweep_engine.h +31 -23
  62. data/ext/ca_triop_dispatch.c +4 -1
  63. data/ext/ca_triop_dispatch.h +5 -2
  64. data/ext/caframe_csv_reader.c +463 -0
  65. data/ext/carray.h +90 -28
  66. data/ext/carray_access.c +487 -231
  67. data/ext/carray_address_basis.c +731 -0
  68. data/ext/carray_attribute.c +45 -3
  69. data/ext/carray_bincount.c +32 -2
  70. data/ext/carray_broadcast.c +49 -17
  71. data/ext/carray_call_cfunc.c +667 -511
  72. data/ext/carray_cast.c +1556 -247
  73. data/ext/carray_cast_func.rb +34 -21
  74. data/ext/carray_conversion.c +197 -73
  75. data/ext/carray_copy.c +84 -38
  76. data/ext/carray_core.c +591 -119
  77. data/ext/carray_count.c +148 -96
  78. data/ext/carray_element.c +18 -27
  79. data/ext/carray_factorize.c +604 -291
  80. data/ext/carray_generate.c +76 -40
  81. data/ext/carray_histogram.c +152 -94
  82. data/ext/carray_hold.c +2 -1
  83. data/ext/carray_index_classifier.c +63 -96
  84. data/ext/carray_internal.h +400 -1
  85. data/ext/carray_lazy.c +834 -79
  86. data/ext/carray_loop.c +88 -66
  87. data/ext/carray_mask.c +705 -324
  88. data/ext/carray_math_kernel.h +1 -0
  89. data/ext/carray_median_percentile.c +263 -63
  90. data/ext/carray_memory_view.c +254 -248
  91. data/ext/carray_operator.c +1093 -363
  92. data/ext/carray_order.c +133 -69
  93. data/ext/carray_partition.c +102 -56
  94. data/ext/carray_random.c +582 -204
  95. data/ext/carray_scatter.c +308 -65
  96. data/ext/carray_segment.c +257 -0
  97. data/ext/carray_slab.c +183 -70
  98. data/ext/carray_sort.c +235 -240
  99. data/ext/carray_sort_kernel.c +8 -1
  100. data/ext/carray_struct.c +99 -21
  101. data/ext/carray_test.c +167 -64
  102. data/ext/carray_time_text.c +1182 -0
  103. data/ext/carray_utils.c +363 -98
  104. data/ext/extconf.rb +27 -2
  105. data/ext/mk_call_cfunc.rb +103 -118
  106. data/ext/mk_pow5_table.rb +35 -0
  107. data/ext/mkkernel.rb +1349 -851
  108. data/ext/ruby_carray.c +17 -2
  109. data/ext/version.h +4 -4
  110. data/ext/version.rb +4 -2
  111. data/lib/carray/attribute.rb +39 -0
  112. data/lib/carray/autoload_carray.rb +24 -3
  113. data/lib/carray/autoload_method_extension.rb +18 -0
  114. data/lib/carray/axis_group.rb +245 -20
  115. data/lib/carray/basics.rb +95 -25
  116. data/lib/carray/bincount_nd.rb +6 -0
  117. data/lib/carray/block_iterator.rb +200 -54
  118. data/lib/carray/boolean_reduce.rb +21 -29
  119. data/lib/carray/categorical.rb +244 -59
  120. data/lib/carray/categorical_iterator.rb +391 -757
  121. data/lib/carray/complex.rb +36 -29
  122. data/lib/carray/conditional.rb +94 -7
  123. data/lib/carray/const_string.rb +177 -27
  124. data/lib/carray/construct.rb +40 -0
  125. data/lib/carray/data_type_extension.rb +3 -0
  126. data/lib/carray/data_type_limits.rb +91 -0
  127. data/lib/carray/fixlen_string.rb +1 -1
  128. data/lib/carray/frame/concat.rb +157 -6
  129. data/lib/carray/frame/convert.rb +13 -7
  130. data/lib/carray/frame/csv_parser.rb +590 -76
  131. data/lib/carray/frame/describe.rb +79 -0
  132. data/lib/carray/frame/frame.rb +205 -35
  133. data/lib/carray/frame/group.rb +199 -40
  134. data/lib/carray/frame/io.rb +452 -60
  135. data/lib/carray/frame/join.rb +134 -46
  136. data/lib/carray/frame/records.rb +54 -19
  137. data/lib/carray/frame/reshape.rb +448 -0
  138. data/lib/carray/frame/sort.rb +26 -2
  139. data/lib/carray/frame/verbs.rb +726 -56
  140. data/lib/carray/frame.rb +2 -0
  141. data/lib/carray/fuse_source.rb +111 -2
  142. data/lib/carray/fusion.rb +229 -38
  143. data/lib/carray/inspect.rb +71 -20
  144. data/lib/carray/iterator.rb +157 -2
  145. data/lib/carray/lazy.rb +54 -67
  146. data/lib/carray/mask_gap_fill.rb +66 -50
  147. data/lib/carray/math.rb +32 -19
  148. data/lib/carray/meld_reduce.rb +40 -7
  149. data/lib/carray/methods/align_addr.rb +8 -4
  150. data/lib/carray/methods/bincount.rb +2 -2
  151. data/lib/carray/methods/bit_string.rb +14 -3
  152. data/lib/carray/methods/composition.rb +16 -10
  153. data/lib/carray/methods/discovery_along.rb +74 -0
  154. data/lib/carray/methods/factorize.rb +50 -0
  155. data/lib/carray/methods/gather_nd.rb +13 -2
  156. data/lib/carray/methods/insert_block.rb +9 -14
  157. data/lib/carray/methods/is_in.rb +130 -6
  158. data/lib/carray/methods/join.rb +1 -1
  159. data/lib/carray/methods/locate_addr.rb +235 -21
  160. data/lib/carray/methods/mask_duplicates.rb +35 -1
  161. data/lib/carray/methods/mode.rb +18 -1
  162. data/lib/carray/methods/nunique.rb +22 -1
  163. data/lib/carray/methods/repeat.rb +91 -0
  164. data/lib/carray/methods/resize.rb +21 -23
  165. data/lib/carray/methods/snap.rb +34 -12
  166. data/lib/carray/methods/unique.rb +52 -3
  167. data/lib/carray/record_order.rb +208 -0
  168. data/lib/carray/rng.rb +86 -0
  169. data/lib/carray/runtime.rb +14 -11
  170. data/lib/carray/segment_iterator.rb +990 -0
  171. data/lib/carray/serialize.rb +197 -27
  172. data/lib/carray/slab_iterator.rb +109 -40
  173. data/lib/carray/stack.rb +10 -4
  174. data/lib/carray/string_operation_extension.rb +9 -4
  175. data/lib/carray/struct.rb +142 -51
  176. data/lib/carray/struct_builder.rb +110 -14
  177. data/lib/carray/table.rb +2 -2
  178. data/lib/carray/time.rb +535 -105
  179. data/lib/carray/window_iterator.rb +259 -82
  180. data/lib/carray.rb +4 -2
  181. data/yard-stubs/ca_obj_array.rb +4 -4
  182. data/yard-stubs/ca_obj_bitarray.rb +7 -0
  183. data/yard-stubs/ca_obj_bitfield.rb +23 -12
  184. data/yard-stubs/ca_obj_block.rb +2 -7
  185. data/yard-stubs/ca_obj_fake.rb +1 -1
  186. data/yard-stubs/ca_obj_field.rb +1 -1
  187. data/yard-stubs/ca_obj_refer.rb +3 -3
  188. data/yard-stubs/ca_obj_roll.rb +0 -1
  189. data/yard-stubs/ca_obj_stride.rb +10 -7
  190. data/yard-stubs/ca_obj_transpose.rb +1 -1
  191. data/yard-stubs/ca_obj_window.rb +11 -4
  192. data/yard-stubs/carray_access.rb +6 -5
  193. data/yard-stubs/carray_broadcast.rb +2 -2
  194. data/yard-stubs/carray_cast.rb +78 -43
  195. data/yard-stubs/carray_conversion.rb +13 -1
  196. data/yard-stubs/carray_copy.rb +8 -3
  197. data/yard-stubs/carray_core.rb +3 -83
  198. data/yard-stubs/carray_count.rb +16 -2
  199. data/yard-stubs/carray_lazy.rb +210 -1
  200. data/yard-stubs/carray_loop.rb +4 -1
  201. data/yard-stubs/carray_mask.rb +34 -10
  202. data/yard-stubs/carray_math.rb +1499 -4
  203. data/yard-stubs/carray_median_percentile.rb +26 -8
  204. data/yard-stubs/carray_order.rb +43 -82
  205. data/yard-stubs/carray_scatter.rb +10 -3
  206. data/yard-stubs/carray_segment.rb +72 -0
  207. data/yard-stubs/carray_slab.rb +11 -8
  208. data/yard-stubs/carray_sort.rb +50 -8
  209. data/yard-stubs/carray_test.rb +10 -0
  210. data/yard-stubs/carray_utils.rb +0 -26
  211. metadata +19 -19
  212. data/ext/ca_for_each_element.h +0 -239
  213. data/ext/carray_build_flags.h +0 -3
  214. data/ext/carray_config.h +0 -28
  215. data/ext/carray_kernels_bincmp.c +0 -4446
  216. data/ext/carray_kernels_binop.c +0 -11001
  217. data/ext/carray_kernels_init.c +0 -1131
  218. data/ext/carray_kernels_map.c +0 -3467
  219. data/ext/carray_kernels_moncmp.c +0 -2097
  220. data/ext/carray_kernels_monop.c +0 -18313
  221. data/ext/carray_kernels_reduce_aggregate.c +0 -25837
  222. data/ext/carray_kernels_reduce_boolean.c +0 -330
  223. data/ext/carray_kernels_reduce_cumulative.c +0 -14593
  224. data/ext/carray_kernels_reduce_extreme.c +0 -16948
  225. data/ext/carray_kernels_reduce_variance.c +0 -3910
  226. data/ext/carray_kernels_scan.c +0 -3693
  227. data/ext/carray_kernels_search.c +0 -32138
  228. data/ext/carray_kernels_sort.c +0 -10626
  229. data/ext/carray_kernels_triop.c +0 -1392
data/ext/carray_cast.c CHANGED
@@ -34,11 +34,573 @@
34
34
  #include "ruby/memory_view.h"
35
35
  #include "ca_monop_dispatch.h" /* CA_MONOP_CAST_BASE for as_type routing */
36
36
  #include "ca_obj_face.h" /* ca_strip_face for write path lift */
37
+ #include "carray_internal.h" /* rb_ca_inherit_attr */
38
+ #include "ruby/util.h" /* ruby_strtod */
39
+ #include <math.h>
40
+
41
+ /* ------------------------------------------------------------------------
42
+ Reading numbers from strings
43
+
44
+ A String stored into, or converted to, an integer or float array is data,
45
+ not a Ruby literal, so it is read as a decimal number rather than through
46
+ Integer() / Float():
47
+
48
+ number [+-]? ( digit+ ( '.' digit* )? | '.' digit+ )
49
+ ( [eE] [+-]? digit+ )?
50
+ float number | nan | inf | infinity (any case, optional sign)
51
+
52
+ Surrounding ASCII whitespace is ignored. "010" is ten, never octal, and
53
+ "0x1F" / "1_000" / "0b11" are not numbers. An integer target takes a
54
+ number whose value is exactly an integer ("1.0", "1e3", "2.50e1"), decided
55
+ from the digits rather than through a double, so a long integer is not
56
+ rounded; "1.5" is not an integer.
57
+
58
+ to_type makes a string that does not read, or an integer the target type
59
+ cannot hold, UNDEF; a store raises (ArgumentError / RangeError).
60
+ ------------------------------------------------------------------------ */
61
+
62
+ static int ca_str_nonfinite (const char *str, double *out);
63
+
64
+ #include "ca_pow5_table.h"
65
+
66
+ /* Decimal to binary64, correctly rounded, without the C library.
67
+
68
+ A validated token is read as w * 10^q with w the first 19 significant
69
+ digits. Clinger's fast path answers when w and 10^q are both exact
70
+ doubles (one rounding). Otherwise the Eisel-Lemire method multiplies w
71
+ by a 128-bit approximation of 5^q (ca_pow5_table.h) and reads the
72
+ significand from the top of the product, declining in the rare cases
73
+ where the product cannot settle the rounding. A declined token, and one
74
+ with more than 19 significant digits, goes to ruby_strtod. */
75
+
76
+ static const double ca_exact_pow10[23] = {
77
+ 1e0, 1e1, 1e2, 1e3, 1e4, 1e5, 1e6, 1e7, 1e8, 1e9, 1e10, 1e11,
78
+ 1e12, 1e13, 1e14, 1e15, 1e16, 1e17, 1e18, 1e19, 1e20, 1e21, 1e22
79
+ };
80
+
81
+ #if defined(__SIZEOF_INT128__)
82
+ static int
83
+ ca_decimal_to_double_eisel_lemire (int64_t q, uint64_t w, uint64_t *bits)
84
+ {
85
+ const uint64_t *t;
86
+ unsigned __int128 p;
87
+ uint64_t hi, lo, m;
88
+ int lz, upper;
89
+ int64_t e2;
90
+
91
+ if ( q < CA_POW5_Q_MIN ) {
92
+ *bits = 0;
93
+ return 1;
94
+ }
95
+ if ( q > CA_POW5_Q_MAX ) {
96
+ *bits = 0x7FFULL << 52;
97
+ return 1;
98
+ }
99
+ lz = __builtin_clzll(w);
100
+ w <<= lz;
101
+ t = ca_pow5_table + 2 * ( q - CA_POW5_Q_MIN );
102
+ p = (unsigned __int128) w * t[0];
103
+ hi = (uint64_t) ( p >> 64 );
104
+ lo = (uint64_t) p;
105
+ if ( ( hi & 0x1FF ) == 0x1FF ) {
106
+ /* The bits below the significand are all ones: the low half of the
107
+ table entry can carry into them. */
108
+ uint64_t hi2 = (uint64_t) ( ( (unsigned __int128) w * t[1] ) >> 64 );
109
+ lo += hi2;
110
+ if ( lo < hi2 ) {
111
+ hi++;
112
+ }
113
+ if ( ( hi & 0x1FF ) == 0x1FF && lo == UINT64_MAX && ( q < -27 || q > 55 ) ) {
114
+ return 0;
115
+ }
116
+ }
117
+ upper = (int) ( hi >> 63 );
118
+ m = hi >> ( upper + 9 );
119
+ /* floor(q * log2(10)) + 63, then the bias. 217706 / 2^16 is log2(10)
120
+ closely enough for every q in the table; the floor is taken by hand,
121
+ since >> on a negative number is the compiler's choice. */
122
+ {
123
+ int64_t t = 217706 * q;
124
+ int64_t log2_10_q = ( t >= 0 ) ? t / 65536 : - ( ( - t + 65535 ) / 65536 );
125
+ e2 = log2_10_q + 63 + upper - lz + 1023;
126
+ }
127
+ if ( e2 <= 0 ) { /* subnormal */
128
+ if ( -e2 + 1 >= 64 ) {
129
+ *bits = 0;
130
+ return 1;
131
+ }
132
+ m >>= -e2 + 1;
133
+ m += m & 1;
134
+ m >>= 1;
135
+ e2 = ( m < ( 1ULL << 52 ) ) ? 0 : 1;
136
+ *bits = ( (uint64_t) e2 << 52 ) | ( m & ( ( 1ULL << 52 ) - 1 ) );
137
+ return 1;
138
+ }
139
+ if ( lo <= 1 && q >= -4 && q <= 23 && ( m & 3 ) == 1
140
+ && ( m << ( upper + 9 ) ) == hi ) {
141
+ m &= ~1ULL; /* exactly halfway: to even */
142
+ }
143
+ m += m & 1;
144
+ m >>= 1;
145
+ if ( m >= ( 2ULL << 52 ) ) {
146
+ m = 1ULL << 52;
147
+ e2++;
148
+ }
149
+ m &= ~( 1ULL << 52 );
150
+ if ( e2 >= 0x7FF ) {
151
+ *bits = 0x7FFULL << 52;
152
+ return 1;
153
+ }
154
+ *bits = ( (uint64_t) e2 << 52 ) | m;
155
+ return 1;
156
+ }
157
+ #endif
158
+
159
+ /* 1 with the value at out, 0 to hand the token to ruby_strtod. */
160
+ static int
161
+ ca_decimal_token_to_double_without_strtod (const char *s, long n, double *out)
162
+ {
163
+ long i = 0;
164
+ int negative = 0, nd = 0, truncated = 0;
165
+ uint64_t w = 0;
166
+ int64_t q = 0, e = 0;
167
+
168
+ if ( s[i] == '+' || s[i] == '-' ) {
169
+ negative = ( s[i] == '-' );
170
+ i++;
171
+ }
172
+ for (; i < n && s[i] >= '0' && s[i] <= '9'; i++) {
173
+ unsigned d = (unsigned) ( s[i] - '0' );
174
+ if ( w == 0 && d == 0 ) continue;
175
+ if ( nd < 19 ) { w = w * 10 + d; nd++; }
176
+ else { q++; truncated |= ( d != 0 ); }
177
+ }
178
+ if ( i < n && s[i] == '.' ) {
179
+ for (i++; i < n && s[i] >= '0' && s[i] <= '9'; i++) {
180
+ unsigned d = (unsigned) ( s[i] - '0' );
181
+ if ( w == 0 && d == 0 ) { q--; continue; }
182
+ if ( nd < 19 ) { w = w * 10 + d; nd++; q--; }
183
+ else { truncated |= ( d != 0 ); }
184
+ }
185
+ }
186
+ if ( i < n && ( s[i] == 'e' || s[i] == 'E' ) ) {
187
+ int eneg = 0;
188
+ i++;
189
+ if ( s[i] == '+' || s[i] == '-' ) {
190
+ eneg = ( s[i] == '-' );
191
+ i++;
192
+ }
193
+ for (; i < n; i++) {
194
+ if ( e < 100000 ) {
195
+ e = e * 10 + ( s[i] - '0' );
196
+ }
197
+ }
198
+ q += eneg ? -e : e;
199
+ }
200
+ if ( truncated ) {
201
+ return 0;
202
+ }
203
+ if ( w == 0 ) {
204
+ *out = negative ? -0.0 : 0.0;
205
+ return 1;
206
+ }
207
+ if ( w <= ( 1ULL << 53 ) && q >= -22 && q <= 22 ) {
208
+ double d = (double) w;
209
+ d = ( q >= 0 ) ? d * ca_exact_pow10[q] : d / ca_exact_pow10[-q];
210
+ *out = negative ? -d : d;
211
+ return 1;
212
+ }
213
+ #if defined(__SIZEOF_INT128__)
214
+ {
215
+ uint64_t bits;
216
+ double d;
217
+ if ( ! ca_decimal_to_double_eisel_lemire(q, w, &bits) ) {
218
+ return 0;
219
+ }
220
+ memcpy(&d, &bits, sizeof(d));
221
+ *out = negative ? -d : d;
222
+ return 1;
223
+ }
224
+ #else
225
+ return 0;
226
+ #endif
227
+ }
228
+
229
+ static double
230
+ ca_decimal_token_to_double (const char *s, long n, const char *buf)
231
+ {
232
+ double d;
233
+ if ( ca_decimal_token_to_double_without_strtod(s, n, &d) ) {
234
+ return d;
235
+ }
236
+ return ruby_strtod(buf, NULL);
237
+ }
238
+
239
+ enum {
240
+ CA_DECIMAL_OK = 0,
241
+ CA_DECIMAL_BLANK,
242
+ CA_DECIMAL_UNREADABLE,
243
+ CA_DECIMAL_OUT_OF_RANGE
244
+ };
245
+
246
+ static int
247
+ ca_decimal_is_space (char c)
248
+ {
249
+ return c == ' ' || c == '\t' || c == '\n' || c == '\r'
250
+ || c == '\v' || c == '\f';
251
+ }
252
+
253
+ static void
254
+ ca_decimal_trim (const char **str, long *len)
255
+ {
256
+ const char *s = *str;
257
+ long n = *len;
258
+ while ( n > 0 && ca_decimal_is_space(s[0]) ) {
259
+ s++;
260
+ n--;
261
+ }
262
+ while ( n > 0 && ca_decimal_is_space(s[n-1]) ) {
263
+ n--;
264
+ }
265
+ *str = s;
266
+ *len = n;
267
+ }
268
+
269
+ /* A decimal number whose value is exactly an integer, into a sign and a
270
+ magnitude. The digits are read with the decimal point moved by the
271
+ exponent: those left of it form the magnitude, those right of it must all
272
+ be zero. Returns CA_DECIMAL_OK, CA_DECIMAL_UNREADABLE, or
273
+ CA_DECIMAL_OUT_OF_RANGE for a magnitude beyond uint64. */
274
+ static int
275
+ ca_decimal_integer (const char *s, long n, int *negative, uint64_t *magnitude)
276
+ {
277
+ long i = 0, int_start, int_end, frac_start, frac_end;
278
+ long nint, total, point, exponent = 0, k;
279
+ int exponent_negative = 0;
280
+ uint64_t v = 0;
281
+ *negative = 0;
282
+ if ( i < n && ( s[i] == '+' || s[i] == '-' ) ) {
283
+ *negative = ( s[i] == '-' );
284
+ i++;
285
+ }
286
+ int_start = i;
287
+ while ( i < n && s[i] >= '0' && s[i] <= '9' ) {
288
+ i++;
289
+ }
290
+ int_end = frac_start = frac_end = i;
291
+ if ( i < n && s[i] == '.' ) {
292
+ i++;
293
+ frac_start = i;
294
+ while ( i < n && s[i] >= '0' && s[i] <= '9' ) {
295
+ i++;
296
+ }
297
+ frac_end = i;
298
+ }
299
+ nint = int_end - int_start;
300
+ total = nint + ( frac_end - frac_start );
301
+ if ( total == 0 ) {
302
+ return CA_DECIMAL_UNREADABLE;
303
+ }
304
+ if ( i < n && ( s[i] == 'e' || s[i] == 'E' ) ) {
305
+ long exponent_digits = 0;
306
+ i++;
307
+ if ( i < n && ( s[i] == '+' || s[i] == '-' ) ) {
308
+ exponent_negative = ( s[i] == '-' );
309
+ i++;
310
+ }
311
+ while ( i < n && s[i] >= '0' && s[i] <= '9' ) {
312
+ if ( exponent < 1000000 ) { /* saturate; far past any fit */
313
+ exponent = exponent * 10 + ( s[i] - '0' );
314
+ }
315
+ i++;
316
+ exponent_digits++;
317
+ }
318
+ if ( exponent_digits == 0 ) {
319
+ return CA_DECIMAL_UNREADABLE;
320
+ }
321
+ if ( exponent_negative ) {
322
+ exponent = - exponent;
323
+ }
324
+ }
325
+ if ( i != n ) {
326
+ return CA_DECIMAL_UNREADABLE;
327
+ }
328
+ point = nint + exponent;
329
+ for (k = 0; k < total; k++) {
330
+ char c = ( k < nint ) ? s[int_start + k] : s[frac_start + k - nint];
331
+ unsigned d = (unsigned) ( c - '0' );
332
+ if ( k >= point ) {
333
+ if ( d != 0 ) {
334
+ return CA_DECIMAL_UNREADABLE; /* a fractional part */
335
+ }
336
+ continue;
337
+ }
338
+ if ( v > ( UINT64_MAX - d ) / 10 ) {
339
+ return CA_DECIMAL_OUT_OF_RANGE;
340
+ }
341
+ v = v * 10 + d;
342
+ }
343
+ for (k = total; k < point && v != 0; k++) {
344
+ if ( v > UINT64_MAX / 10 ) {
345
+ return CA_DECIMAL_OUT_OF_RANGE;
346
+ }
347
+ v *= 10;
348
+ }
349
+ *magnitude = v;
350
+ return CA_DECIMAL_OK;
351
+ }
352
+
353
+ /* Whether s could be nan / inf / infinity: a letter after an optional sign. */
354
+ static int
355
+ ca_decimal_may_be_word (const char *s, long n)
356
+ {
357
+ long i = ( s[0] == '+' || s[0] == '-' ) ? 1 : 0;
358
+ return i < n && ( ( s[i] | 0x20 ) == 'n' || ( s[i] | 0x20 ) == 'i' );
359
+ }
360
+
361
+ /* A decimal float, written to out. With out NULL, only whether s is one. */
362
+ static int
363
+ ca_decimal_float (const char *s, long n, double *out)
364
+ {
365
+ char buf[64];
366
+ long i = 0, digits = 0;
367
+ double nonfinite;
368
+ if ( n == 0 ) {
369
+ return 0;
370
+ }
371
+ if ( n <= 9 && ca_decimal_may_be_word(s, n) ) { /* longest: "+infinity" */
372
+ memcpy(buf, s, n);
373
+ buf[n] = '\0';
374
+ if ( ca_str_nonfinite(buf, &nonfinite) ) {
375
+ if ( out ) {
376
+ *out = nonfinite;
377
+ }
378
+ return 1;
379
+ }
380
+ }
381
+ if ( s[i] == '+' || s[i] == '-' ) {
382
+ i++;
383
+ }
384
+ while ( i < n && s[i] >= '0' && s[i] <= '9' ) {
385
+ i++;
386
+ digits++;
387
+ }
388
+ if ( i < n && s[i] == '.' ) {
389
+ i++;
390
+ while ( i < n && s[i] >= '0' && s[i] <= '9' ) {
391
+ i++;
392
+ digits++;
393
+ }
394
+ }
395
+ if ( digits == 0 ) {
396
+ return 0;
397
+ }
398
+ if ( i < n && ( s[i] == 'e' || s[i] == 'E' ) ) {
399
+ long exponent_digits = 0;
400
+ i++;
401
+ if ( i < n && ( s[i] == '+' || s[i] == '-' ) ) {
402
+ i++;
403
+ }
404
+ while ( i < n && s[i] >= '0' && s[i] <= '9' ) {
405
+ i++;
406
+ exponent_digits++;
407
+ }
408
+ if ( exponent_digits == 0 ) {
409
+ return 0;
410
+ }
411
+ }
412
+ if ( i != n ) {
413
+ return 0;
414
+ }
415
+ if ( out == NULL ) {
416
+ return 1;
417
+ }
418
+ /* The token is validated, so the reader sees nothing but a decimal
419
+ number. */
420
+ if ( n < (long) sizeof(buf) ) {
421
+ memcpy(buf, s, n);
422
+ buf[n] = '\0';
423
+ *out = ca_decimal_token_to_double(s, n, buf);
424
+ }
425
+ else {
426
+ volatile VALUE token = rb_str_new(s, n);
427
+ *out = ruby_strtod(RSTRING_PTR(token), NULL);
428
+ }
429
+ return 1;
430
+ }
431
+
432
+ static int
433
+ ca_decimal_fits (int8_t data_type, int negative, uint64_t magnitude)
434
+ {
435
+ uint64_t positive_max, negative_max;
436
+ switch ( data_type ) {
437
+ case CA_INT8: positive_max = INT8_MAX; negative_max = (uint64_t) INT8_MAX + 1; break;
438
+ case CA_INT16: positive_max = INT16_MAX; negative_max = (uint64_t) INT16_MAX + 1; break;
439
+ case CA_INT32: positive_max = INT32_MAX; negative_max = (uint64_t) INT32_MAX + 1; break;
440
+ case CA_INT64: positive_max = INT64_MAX; negative_max = (uint64_t) INT64_MAX + 1; break;
441
+ case CA_UINT8: positive_max = UINT8_MAX; negative_max = 0; break;
442
+ case CA_UINT16: positive_max = UINT16_MAX; negative_max = 0; break;
443
+ case CA_UINT32: positive_max = UINT32_MAX; negative_max = 0; break;
444
+ case CA_UINT64: positive_max = UINT64_MAX; negative_max = 0; break;
445
+ default:
446
+ return 0;
447
+ }
448
+ return negative ? ( magnitude <= negative_max ) : ( magnitude <= positive_max );
449
+ }
450
+
451
+ /* Store a value that ca_decimal_fits accepted. */
452
+ static void
453
+ ca_decimal_store_integer (int8_t data_type, char *ptr, ca_size_t i,
454
+ int negative, uint64_t magnitude)
455
+ {
456
+ int64_t v;
457
+ if ( negative && magnitude > 0 ) {
458
+ v = - (int64_t) ( magnitude - 1 ) - 1;
459
+ }
460
+ else {
461
+ v = (int64_t) magnitude;
462
+ }
463
+ switch ( data_type ) {
464
+ case CA_INT8: ((int8_t *) ptr)[i] = (int8_t) v; break;
465
+ case CA_INT16: ((int16_t *) ptr)[i] = (int16_t) v; break;
466
+ case CA_INT32: ((int32_t *) ptr)[i] = (int32_t) v; break;
467
+ case CA_INT64: ((int64_t *) ptr)[i] = v; break;
468
+ case CA_UINT8: ((uint8_t *) ptr)[i] = (uint8_t) magnitude; break;
469
+ case CA_UINT16: ((uint16_t *) ptr)[i] = (uint16_t) magnitude; break;
470
+ case CA_UINT32: ((uint32_t *) ptr)[i] = (uint32_t) magnitude; break;
471
+ case CA_UINT64: ((uint64_t *) ptr)[i] = magnitude; break;
472
+ }
473
+ }
474
+
475
+ /* Whether v is a real number other than an Integer or a Float -- a Rational,
476
+ a BigDecimal -- which reads as the value it stands for. */
477
+ static int
478
+ ca_decimal_other_real (VALUE v)
479
+ {
480
+ return RTEST(rb_obj_is_kind_of(v, rb_cNumeric))
481
+ && RTEST(rb_funcall(v, rb_intern("real?"), 0));
482
+ }
483
+
484
+ static VALUE
485
+ ca_decimal_truncate (VALUE v)
486
+ {
487
+ return rb_funcall(v, rb_intern("truncate"), 0);
488
+ }
489
+
490
+ /* A non-String cell as an integer: an Integer, or a Float or another real
491
+ number with no fractional part, that the target can hold. */
492
+ static int
493
+ ca_decimal_object_integer (VALUE v, int *negative, uint64_t *magnitude)
494
+ {
495
+ if ( ! RB_INTEGER_TYPE_P(v) && ! RB_FLOAT_TYPE_P(v) && ca_decimal_other_real(v) ) {
496
+ /* truncate raises for a NaN or infinite BigDecimal, which reads as
497
+ nothing an integer can hold. */
498
+ int state = 0;
499
+ VALUE whole = rb_protect(ca_decimal_truncate, v, &state);
500
+ if ( state ) {
501
+ rb_set_errinfo(Qnil);
502
+ return 0;
503
+ }
504
+ if ( ! RB_INTEGER_TYPE_P(whole) || ! RTEST(rb_equal(whole, v)) ) {
505
+ return 0;
506
+ }
507
+ v = whole;
508
+ }
509
+ if ( RB_INTEGER_TYPE_P(v) ) {
510
+ VALUE zero = INT2FIX(0);
511
+ *negative = RTEST(rb_funcall(v, '<', 1, zero));
512
+ if ( *negative ) {
513
+ v = rb_funcall(v, rb_intern("-@"), 0);
514
+ }
515
+ if ( RTEST(rb_funcall(v, '>', 1, ULL2NUM(UINT64_MAX))) ) {
516
+ return 0;
517
+ }
518
+ *magnitude = NUM2ULL(v);
519
+ return 1;
520
+ }
521
+ if ( RB_FLOAT_TYPE_P(v) ) {
522
+ double d = RFLOAT_VALUE(v);
523
+ if ( d != d || d != floor(d) || fabs(d) >= 18446744073709551616.0 ) {
524
+ return 0;
525
+ }
526
+ *negative = ( d < 0 );
527
+ *magnitude = (uint64_t) fabs(d);
528
+ return 1;
529
+ }
530
+ return 0;
531
+ }
532
+
533
+ /* A String as one value of data_type (an integer or float type), written
534
+ to out. */
535
+ static int
536
+ ca_decimal_read_bytes_as (const char *s, long n, int8_t data_type, void *out)
537
+ {
538
+ ca_decimal_trim(&s, &n);
539
+ if ( n == 0 ) {
540
+ return CA_DECIMAL_BLANK;
541
+ }
542
+ if ( data_type == CA_FLOAT32 || data_type == CA_FLOAT64 ) {
543
+ double d;
544
+ if ( ! ca_decimal_float(s, n, &d) ) {
545
+ return CA_DECIMAL_UNREADABLE;
546
+ }
547
+ if ( data_type == CA_FLOAT32 ) {
548
+ *(float32_t *) out = (float32_t) d;
549
+ }
550
+ else {
551
+ *(float64_t *) out = d;
552
+ }
553
+ return CA_DECIMAL_OK;
554
+ }
555
+ else {
556
+ int negative = 0;
557
+ uint64_t magnitude = 0;
558
+ int rc = ca_decimal_integer(s, n, &negative, &magnitude);
559
+ if ( rc != CA_DECIMAL_OK ) {
560
+ return rc;
561
+ }
562
+ if ( ! ca_decimal_fits(data_type, negative, magnitude) ) {
563
+ return CA_DECIMAL_OUT_OF_RANGE;
564
+ }
565
+ ca_decimal_store_integer(data_type, (char *) out, 0, negative, magnitude);
566
+ return CA_DECIMAL_OK;
567
+ }
568
+ }
569
+
570
+ static int
571
+ ca_decimal_string_read (VALUE str, int8_t data_type, void *out)
572
+ {
573
+ return ca_decimal_read_bytes_as(RSTRING_PTR(str), RSTRING_LEN(str), data_type, out);
574
+ }
575
+
576
+ /* The to_type reader: 1 with the value at out, or 0 for a cell that
577
+ becomes UNDEF. */
578
+ int
579
+ ca_decimal_string_to (VALUE str, int8_t data_type, void *out)
580
+ {
581
+ return ca_decimal_string_read(str, data_type, out) == CA_DECIMAL_OK;
582
+ }
583
+
584
+ /* The store reader: the value at out, or a raise. */
585
+ void
586
+ ca_decimal_string_store (VALUE str, int8_t data_type, void *out)
587
+ {
588
+ switch ( ca_decimal_string_read(str, data_type, out) ) {
589
+ case CA_DECIMAL_OK:
590
+ return;
591
+ case CA_DECIMAL_OUT_OF_RANGE:
592
+ rb_raise(rb_eRangeError, "%+"PRIsVALUE" is out of range for %s",
593
+ str, ca_type_name[data_type]);
594
+ default:
595
+ rb_raise(rb_eArgError, "%+"PRIsVALUE" cannot be read as %s",
596
+ str, ca_type_name[data_type]);
597
+ }
598
+ }
37
599
 
38
600
  boolean8_t
39
601
  OBJ2BOOL (VALUE v)
40
602
  {
41
- if ( v == Qfalse || v == Qnil ) {
603
+ if ( v == Qfalse ) {
42
604
  return 0;
43
605
  }
44
606
  else if ( v == Qtrue ) {
@@ -99,25 +661,20 @@ OBJ2DBL (VALUE val)
99
661
  switch ( TYPE(val) ) {
100
662
  case T_FLOAT:
101
663
  return NUM2DBL(val);
102
- case T_NIL:
103
- return 0.0/0.0;
104
664
  case T_STRING: {
105
665
  double d;
106
- volatile VALUE rstr = rb_funcall(val, rb_intern("strip"), 0);
107
- char *str = StringValuePtr(rstr);
108
- if ( ca_str_nonfinite(str, &d) ) {
109
- return d;
110
- }
111
- return rb_cstr_to_dbl(str, 0);
666
+ ca_decimal_string_store(val, CA_FLOAT64, &d);
667
+ return d;
112
668
  }
113
669
  default:
114
670
  return NUM2DBL(rb_Float(val));
115
671
  }
116
672
  }
117
673
 
118
- /* rb_protect bodies: Kernel#Float / Kernel#Integer applied to one value.
119
- Used by the mask-aware object->numeric parsers below so a conversion
120
- error is caught (and turned into a masked cell) rather than raised. */
674
+ /* rb_protect bodies: Kernel#Float / Kernel#Integer applied to one value
675
+ that is not a String. Used by the mask-aware object->numeric parsers
676
+ below so a conversion error is caught (and turned into a masked cell)
677
+ rather than raised. */
121
678
  static VALUE
122
679
  ca_kernel_float (VALUE v)
123
680
  {
@@ -130,12 +687,71 @@ ca_kernel_integer (VALUE v)
130
687
  return rb_Integer(v);
131
688
  }
132
689
 
690
+ /* Complex#to_s writes a part that is not finite as "NaN" or "Infinity" and
691
+ then marks the imaginary part with "*i" ("1.0+Infinity*i", "NaN+0i"),
692
+ which Complex() does not read back. Read that form as two decimal
693
+ numbers: the imaginary part starts at the last sign that is not the
694
+ first character and not an exponent's. Returns 0 when the text is not
695
+ of that form. */
696
+ static int
697
+ ca_str_to_cmplx_nonfinite (VALUE str, double complex *out)
698
+ {
699
+ const char *s = RSTRING_PTR(str);
700
+ long n = RSTRING_LEN(str), k;
701
+ double re, im;
702
+ volatile VALUE vre, vim;
703
+ while ( n > 0 && ( s[0] == ' ' || s[0] == '\t' ) ) { s++; n--; }
704
+ while ( n > 0 && ( s[n-1] == ' ' || s[n-1] == '\t' ) ) { n--; }
705
+ if ( n < 2 || s[n-1] != 'i' ) {
706
+ return 0;
707
+ }
708
+ n -= ( n >= 2 && s[n-2] == '*' ) ? 2 : 1;
709
+ for ( k = n - 1; k > 0; k-- ) {
710
+ if ( ( s[k] == '+' || s[k] == '-' ) && s[k-1] != 'e' && s[k-1] != 'E' ) {
711
+ break;
712
+ }
713
+ }
714
+ if ( k <= 0 ) {
715
+ return 0;
716
+ }
717
+ vre = rb_str_new(s, k);
718
+ vim = rb_str_new(s + k, n - k);
719
+ if ( ! ca_decimal_string_to(vre, CA_FLOAT64, &re)
720
+ || ! ca_decimal_string_to(vim, CA_FLOAT64, &im) ) {
721
+ return 0;
722
+ }
723
+ *out = CMPLX(re, im);
724
+ return 1;
725
+ }
726
+
727
+ /* A real is read as a decimal number; anything else goes to Complex(). */
728
+ double complex
729
+ ca_str_to_cmplx (VALUE str)
730
+ {
731
+ volatile VALUE rstr;
732
+ volatile VALUE c;
733
+ double d;
734
+ double complex z;
735
+ if ( ca_decimal_string_to(str, CA_FLOAT64, &d) ) {
736
+ return (double complex) d;
737
+ }
738
+ if ( ( memchr(RSTRING_PTR(str), 'N', RSTRING_LEN(str))
739
+ || memchr(RSTRING_PTR(str), 'I', RSTRING_LEN(str)) )
740
+ && ca_str_to_cmplx_nonfinite(str, &z) ) {
741
+ return z;
742
+ }
743
+ rstr = rb_funcall(str, rb_intern("strip"), 0);
744
+ c = rb_funcall(rb_mKernel, rb_intern("Complex"), 1, rstr);
745
+ return CMPLX(NUM2DBL(rb_complex_real(c)), NUM2DBL(rb_complex_imag(c)));
746
+ }
747
+
133
748
  /* Object -> double with parse-failure signalling. Returns 1 and sets *out
134
749
  on success; returns 0 when the cell cannot be parsed, in which case the
135
- caller writes UNDEF (mask) for that cell. nil and unparseable
136
- strings/objects fail; already-numeric values and explicit nan/inf
137
- literals succeed. Strictness mirrors Ruby Float() (whitespace strip,
138
- "1e3" ok, ...) but a bad token masks instead of raising. */
750
+ caller writes UNDEF (mask) for that cell. nil and unreadable
751
+ strings/objects fail; already-numeric values succeed, and a String is
752
+ read as a decimal number. This is the reading path of to_type from an
753
+ object array, where a cell with no number in it is a missing value; a
754
+ store (OBJ2DBL) raises instead. */
139
755
  int
140
756
  ca_obj2dbl_ok (VALUE val, double *out)
141
757
  {
@@ -147,10 +763,7 @@ ca_obj2dbl_ok (VALUE val, double *out)
147
763
  return 1;
148
764
  }
149
765
  if ( RB_TYPE_P(val, T_STRING) ) {
150
- volatile VALUE rstr = rb_funcall(val, rb_intern("strip"), 0);
151
- if ( ca_str_nonfinite(StringValuePtr(rstr), out) ) {
152
- return 1;
153
- }
766
+ return ca_decimal_string_to(val, CA_FLOAT64, out);
154
767
  }
155
768
  {
156
769
  int state = 0;
@@ -164,98 +777,118 @@ ca_obj2dbl_ok (VALUE val, double *out)
164
777
  }
165
778
  }
166
779
 
167
- /* Object -> integer with parse-failure signalling (int/long lane). Same
168
- contract as ca_obj2dbl_ok: 1 + *out on success, 0 (mask) on failure.
169
- Strictness mirrors Ruby Integer() -- a non-integer string ("1.5") fails
170
- (no truncation), nil fails, "0xff"/"1_000" parse. A valid Integer that
171
- overflows the widest C integer still raises (a domain error, not a parse
172
- failure). */
780
+ /* Object -> complex with parse-failure signalling: the complex sibling of
781
+ ca_obj2dbl_ok. A cell is read as a store reads it (a real as a decimal
782
+ number, the rest as Complex()); nil and a cell that does not read fail. */
783
+ static VALUE
784
+ ca_cmplx_cell_body (VALUE v)
785
+ {
786
+ double complex z = rb_carray_num2cmplx(v);
787
+ return rb_dbl_complex_new(creal(z), cimag(z));
788
+ }
789
+
173
790
  int
174
- rb_obj2long_ok (VALUE val, long *out)
791
+ ca_obj2cmplx_ok (VALUE val, double complex *out)
175
792
  {
793
+ int state = 0;
794
+ volatile VALUE r;
176
795
  if ( NIL_P(val) ) {
177
796
  return 0;
178
797
  }
798
+ r = rb_protect(ca_cmplx_cell_body, val, &state);
799
+ if ( state ) {
800
+ rb_set_errinfo(Qnil);
801
+ return 0;
802
+ }
803
+ *out = CMPLX(NUM2DBL(rb_complex_real(r)), NUM2DBL(rb_complex_imag(r)));
804
+ return 1;
805
+ }
806
+
807
+ /* Object -> integer with parse-failure signalling. Same contract as
808
+ ca_obj2dbl_ok: 1 + *out on success, 0 (mask) on failure. A String is
809
+ read as a decimal number ("1.5" fails, no truncation); any other object
810
+ goes through Integer(). nil fails. A valid Integer that overflows the
811
+ widest C integer still raises (a domain error, not a parse failure). */
812
+ static int
813
+ ca_obj2integer_ok (VALUE val, int8_t wide_type, void *wide, VALUE *integer)
814
+ {
815
+ int state = 0;
816
+ if ( NIL_P(val) ) {
817
+ return 0;
818
+ }
819
+ if ( RB_TYPE_P(val, T_STRING) ) {
820
+ return ca_decimal_string_to(val, wide_type, wide);
821
+ }
822
+ *integer = rb_protect(ca_kernel_integer, val, &state);
823
+ if ( state ) {
824
+ rb_set_errinfo(Qnil);
825
+ return 0;
826
+ }
827
+ return 1;
828
+ }
829
+
830
+ int
831
+ rb_obj2long_ok (VALUE val, long *out)
832
+ {
833
+ int64_t wide;
834
+ VALUE integer = Qnil;
179
835
  if ( RB_TYPE_P(val, T_FIXNUM) ) {
180
836
  *out = NUM2LONG(val);
181
837
  return 1;
182
838
  }
183
- {
184
- int state = 0;
185
- volatile VALUE r = rb_protect(ca_kernel_integer, val, &state);
186
- if ( state ) {
187
- rb_set_errinfo(Qnil);
188
- return 0;
189
- }
190
- *out = NUM2LONG(r);
191
- return 1;
839
+ if ( ! ca_obj2integer_ok(val, CA_INT64, &wide, &integer) ) {
840
+ return 0;
192
841
  }
842
+ *out = NIL_P(integer) ? (long) wide : NUM2LONG(integer);
843
+ return 1;
193
844
  }
194
845
 
195
846
  int
196
847
  rb_obj2ulong_ok (VALUE val, unsigned long *out)
197
848
  {
198
- if ( NIL_P(val) ) {
199
- return 0;
200
- }
849
+ uint64_t wide;
850
+ VALUE integer = Qnil;
201
851
  if ( RB_TYPE_P(val, T_FIXNUM) ) {
202
852
  *out = NUM2ULONG(val);
203
853
  return 1;
204
854
  }
205
- {
206
- int state = 0;
207
- volatile VALUE r = rb_protect(ca_kernel_integer, val, &state);
208
- if ( state ) {
209
- rb_set_errinfo(Qnil);
210
- return 0;
211
- }
212
- *out = NUM2ULONG(r);
213
- return 1;
855
+ if ( ! ca_obj2integer_ok(val, CA_UINT64, &wide, &integer) ) {
856
+ return 0;
214
857
  }
858
+ *out = NIL_P(integer) ? (unsigned long) wide : NUM2ULONG(integer);
859
+ return 1;
215
860
  }
216
861
 
217
862
  int
218
863
  rb_obj2ll_ok (VALUE val, long long *out)
219
864
  {
220
- if ( NIL_P(val) ) {
221
- return 0;
222
- }
865
+ int64_t wide;
866
+ VALUE integer = Qnil;
223
867
  if ( RB_TYPE_P(val, T_FIXNUM) ) {
224
868
  *out = NUM2LL(val);
225
869
  return 1;
226
870
  }
227
- {
228
- int state = 0;
229
- volatile VALUE r = rb_protect(ca_kernel_integer, val, &state);
230
- if ( state ) {
231
- rb_set_errinfo(Qnil);
232
- return 0;
233
- }
234
- *out = NUM2LL(r);
235
- return 1;
871
+ if ( ! ca_obj2integer_ok(val, CA_INT64, &wide, &integer) ) {
872
+ return 0;
236
873
  }
874
+ *out = NIL_P(integer) ? (long long) wide : NUM2LL(integer);
875
+ return 1;
237
876
  }
238
877
 
239
878
  int
240
879
  rb_obj2ull_ok (VALUE val, unsigned long long *out)
241
880
  {
242
- if ( NIL_P(val) ) {
243
- return 0;
244
- }
881
+ uint64_t wide;
882
+ VALUE integer = Qnil;
245
883
  if ( RB_TYPE_P(val, T_FIXNUM) ) {
246
884
  *out = NUM2ULL(val);
247
885
  return 1;
248
886
  }
249
- {
250
- int state = 0;
251
- volatile VALUE r = rb_protect(ca_kernel_integer, val, &state);
252
- if ( state ) {
253
- rb_set_errinfo(Qnil);
254
- return 0;
255
- }
256
- *out = rb_num2ull(r);
257
- return 1;
887
+ if ( ! ca_obj2integer_ok(val, CA_UINT64, &wide, &integer) ) {
888
+ return 0;
258
889
  }
890
+ *out = NIL_P(integer) ? (unsigned long long) wide : rb_num2ull(integer);
891
+ return 1;
259
892
  }
260
893
 
261
894
  long
@@ -267,8 +900,13 @@ rb_obj2long (VALUE val)
267
900
  case T_BIGNUM:
268
901
  return (long) NUM2LL(val);
269
902
  case T_NIL:
270
- rb_raise(rb_eTypeError, "no implicit conversion from nil to integer");
903
+ rb_raise(rb_eTypeError, "can't convert nil into Integer");
271
904
  break;
905
+ case T_STRING: {
906
+ int64_t v;
907
+ ca_decimal_string_store(val, CA_INT64, &v);
908
+ return (long) v;
909
+ }
272
910
  default:
273
911
  return NUM2LONG(rb_Integer(val));
274
912
  }
@@ -283,8 +921,13 @@ rb_obj2ulong (VALUE val)
283
921
  case T_BIGNUM:
284
922
  return (unsigned long) rb_num2ull(val);
285
923
  case T_NIL:
286
- rb_raise(rb_eTypeError, "no implicit conversion from nil to integer");
924
+ rb_raise(rb_eTypeError, "can't convert nil into Integer");
287
925
  break;
926
+ case T_STRING: {
927
+ uint64_t v;
928
+ ca_decimal_string_store(val, CA_UINT64, &v);
929
+ return (unsigned long) v;
930
+ }
288
931
  default:
289
932
  return NUM2ULONG(rb_Integer(val));
290
933
  }
@@ -297,8 +940,13 @@ rb_obj2ll (VALUE val)
297
940
  case T_FIXNUM:
298
941
  return NUM2LONG(val);
299
942
  case T_NIL:
300
- rb_raise(rb_eTypeError, "no implicit conversion from nil to integer");
943
+ rb_raise(rb_eTypeError, "can't convert nil into Integer");
301
944
  break;
945
+ case T_STRING: {
946
+ int64_t v;
947
+ ca_decimal_string_store(val, CA_INT64, &v);
948
+ return (long long) v;
949
+ }
302
950
  default:
303
951
  return NUM2LL(rb_Integer(val));
304
952
  }
@@ -311,8 +959,13 @@ rb_obj2ull (VALUE val)
311
959
  case T_FIXNUM:
312
960
  return NUM2ULONG(val);
313
961
  case T_NIL:
314
- rb_raise(rb_eTypeError, "no implicit conversion from nil to integer");
962
+ rb_raise(rb_eTypeError, "can't convert nil into Integer");
315
963
  break;
964
+ case T_STRING: {
965
+ uint64_t v;
966
+ ca_decimal_string_store(val, CA_UINT64, &v);
967
+ return (unsigned long long) v;
968
+ }
316
969
  default:
317
970
  return rb_num2ull(rb_Integer(val));
318
971
  }
@@ -334,7 +987,7 @@ ca_face_safe_check (CArray *ca)
334
987
  static inline int8_t
335
988
  ca_storage_type_of (CArray *ca)
336
989
  {
337
- while (ca_face_safe_check(ca)) ca = ((CAView *) ca)->parent;
990
+ if ( ca_face_safe_check(ca) ) ca = ca_strip_face(ca);
338
991
  return ca ? ca->data_type : CA_FIXLEN;
339
992
  }
340
993
 
@@ -433,7 +1086,7 @@ ca_val2val (int8_t data_type1, void *ptr1, int8_t data_type2, void *ptr2)
433
1086
  /* Cast to CA_OBJECT for an array whose cells mean something other than their
434
1087
  storage bytes: a data_class array (CARecord / CAStruct) or a Face (CATime,
435
1088
  CAConstString, CACategorical, ...). Both decode per cell -- rb_ca_fetch_addr
436
- goes through the data_class decode and CA_FACE_STORAGE_TO_SCALAR_IF_FACE --
1089
+ goes through the data_class decode and CA_FACE_STORAGE_TO_ELEMENT_IF_FACE --
437
1090
  so the object array holds the *surface* values (labels, not codes;
438
1091
  CATime::Element, not serials; the string, not the descriptor bytes). Reading
439
1092
  the storage instead is `.parent`, which stays available and explicit. Mask
@@ -548,19 +1201,142 @@ rb_ca_object_to_data_class (VALUE self, VALUE rtype, ca_size_t bytes)
548
1201
  rb_ca_store_addr(obj, i, rb_funcall(rval, id_encode, 0));
549
1202
  }
550
1203
  }
551
-
552
- return obj;
1204
+
1205
+ return obj;
1206
+ }
1207
+
1208
+ typedef struct {
1209
+ CArray *ca;
1210
+ CArray *cb;
1211
+ boolean8_t *scratch;
1212
+ } ca_to_type_ctx_t;
1213
+
1214
+ /* Marks in `m` the cells of a float or complex array that hold NaN (in
1215
+ either part). A NaN has no integer, and C leaves its conversion
1216
+ undefined. */
1217
+ static void
1218
+ ca_mark_nan (CArray *ca, boolean8_t *m)
1219
+ {
1220
+ ca_size_t i, n = ca->elements;
1221
+ switch ( ca->data_type ) {
1222
+ case CA_FLOAT32: { float32_t *p = (float32_t *) ca->ptr;
1223
+ for (i=0; i<n; i++) if ( isnan(p[i]) ) m[i] = 1;
1224
+ break; }
1225
+ case CA_FLOAT64: { float64_t *p = (float64_t *) ca->ptr;
1226
+ for (i=0; i<n; i++) if ( isnan(p[i]) ) m[i] = 1;
1227
+ break; }
1228
+ #ifdef HAVE_COMPLEX_H
1229
+ case CA_CMPLX64: { cmplx64_t *p = (cmplx64_t *) ca->ptr;
1230
+ for (i=0; i<n; i++) if ( isnan(crealf(p[i])) || isnan(cimagf(p[i])) ) m[i] = 1;
1231
+ break; }
1232
+ case CA_CMPLX128: { cmplx128_t *p = (cmplx128_t *) ca->ptr;
1233
+ for (i=0; i<n; i++) if ( isnan(creal(p[i])) || isnan(cimag(p[i])) ) m[i] = 1;
1234
+ break; }
1235
+ #endif
1236
+ default:
1237
+ break;
1238
+ }
1239
+ }
1240
+
1241
+ /* The cast proper, with `ca` attached. It can raise (a value the target
1242
+ type cannot hold), so it runs under rb_ensure. */
1243
+ static VALUE
1244
+ ca_to_type_cast (VALUE arg)
1245
+ {
1246
+ ca_to_type_ctx_t *c = (ca_to_type_ctx_t *) arg;
1247
+ CArray *ca = c->ca, *cb = c->cb;
1248
+ int to_integer = ( cb->data_type >= CA_INT8 && cb->data_type <= CA_UINT64 );
1249
+ int from_float = ( ca->data_type == CA_FLOAT32 || ca->data_type == CA_FLOAT64
1250
+ || ca->data_type == CA_CMPLX64 || ca->data_type == CA_CMPLX128 );
1251
+
1252
+ if ( ( ca->data_type == CA_OBJECT
1253
+ && ( to_integer
1254
+ || cb->data_type == CA_FLOAT32 || cb->data_type == CA_FLOAT64
1255
+ || cb->data_type == CA_CMPLX64 || cb->data_type == CA_CMPLX128 ) )
1256
+ || ( from_float && to_integer ) ) {
1257
+ /* object -> int/float/complex: give the cast a mask buffer so an unparseable
1258
+ cell becomes UNDEF (see ext/carray_cast_func.rb). Cast into a
1259
+ scratch mask seeded from the source mask, then attach it to the
1260
+ output only if some cell ended up masked -- an all-valid cast keeps
1261
+ the mask-less result the legacy path produced. */
1262
+ ca_size_t ne = cb->elements;
1263
+ ca_size_t i;
1264
+ boolean8_t any = 0;
1265
+ boolean8_t *scratch;
1266
+ c->scratch = scratch = ALLOC_N(boolean8_t, ne > 0 ? ne : 1);
1267
+ if ( cb->mask ) {
1268
+ memcpy(scratch, cb->mask->ptr, ne); /* copy of source mask */
1269
+ }
1270
+ else {
1271
+ memset(scratch, 0, ne);
1272
+ }
1273
+ /* float -> integer: a NaN cell becomes UNDEF, as an unparseable object
1274
+ cell does. A finite value outside the integer type is converted the
1275
+ way the machine's C converts it, which differs between machines. */
1276
+ if ( from_float ) {
1277
+ ca_mark_nan(ca, scratch);
1278
+ memset(cb->ptr, 0, ne * cb->bytes);
1279
+ }
1280
+ ca_cast_block_with_mask(ne, ca, ca->ptr, cb, cb->ptr, scratch);
1281
+ for (i=0; i<ne; i++) {
1282
+ if ( scratch[i] ) {
1283
+ any = 1;
1284
+ break;
1285
+ }
1286
+ }
1287
+ if ( any ) {
1288
+ if ( ! cb->mask ) {
1289
+ ca_create_mask(cb);
1290
+ }
1291
+ memcpy(cb->mask->ptr, scratch, ne);
1292
+ }
1293
+ xfree(scratch);
1294
+ c->scratch = NULL;
1295
+ }
1296
+ else if ( ca_has_mask(ca) ) {
1297
+ ca_cast_block_with_mask(cb->elements, ca, ca->ptr, cb, cb->ptr,
1298
+ (boolean8_t*)ca->mask->ptr);
1299
+ }
1300
+ else {
1301
+ ca_cast_block(cb->elements, ca, ca->ptr, cb, cb->ptr);
1302
+ }
1303
+ return Qnil;
1304
+ }
1305
+
1306
+ static VALUE
1307
+ ca_to_type_release (VALUE arg)
1308
+ {
1309
+ ca_to_type_ctx_t *c = (ca_to_type_ctx_t *) arg;
1310
+ if ( c->scratch ) {
1311
+ xfree(c->scratch);
1312
+ c->scratch = NULL;
1313
+ }
1314
+ ca_detach(c->ca);
1315
+ return Qnil;
553
1316
  }
554
1317
 
555
1318
  /* CArray#to_type(data_type, bytes:) -- eager copy of self converted to
556
1319
  data_type (a new entity owning its storage). User doc lives in
557
1320
  yard-stubs/carray_cast.rb. */
558
1321
 
1322
+ static VALUE rb_ca_to_type_convert (int argc, VALUE *argv, VALUE self);
1323
+
1324
+ /* The converted array keeps self's attributes: the values are the same
1325
+ values in another representation. */
559
1326
  static VALUE
560
1327
  rb_ca_to_type_internal (int argc, VALUE *argv, VALUE self)
1328
+ {
1329
+ VALUE obj = rb_ca_to_type_convert(argc, argv, self);
1330
+ rb_ca_inherit_attr(obj, self);
1331
+ return obj;
1332
+ }
1333
+
1334
+ static VALUE
1335
+ rb_ca_to_type_convert (int argc, VALUE *argv, VALUE self)
561
1336
  {
562
1337
  volatile VALUE obj, rtype = Qnil, ropt, rbytes = Qnil;
563
1338
  CArray *ca, *cb;
1339
+ ca_to_type_ctx_t ctx;
564
1340
  int8_t data_type;
565
1341
  ca_size_t bytes;
566
1342
 
@@ -585,8 +1361,8 @@ rb_ca_to_type_internal (int argc, VALUE *argv, VALUE self)
585
1361
  Numeric Face is not routed here -- its surface *is* its storage, so the
586
1362
  ordinary cast below is already right. */
587
1363
  if ( ca_is_face(ca) && ca->data_type == CA_FIXLEN ) {
588
- return rb_ca_to_type_internal(argc, argv,
589
- rb_ca_face_numeric_projection(self, ca));
1364
+ return rb_ca_to_type_convert(argc, argv,
1365
+ rb_ca_face_numeric_projection(self, ca));
590
1366
  }
591
1367
 
592
1368
  ca_update_mask(ca);
@@ -601,48 +1377,11 @@ rb_ca_to_type_internal (int argc, VALUE *argv, VALUE self)
601
1377
 
602
1378
  TypedData_Get_Struct(obj, CArray, &carray_data_type, cb);
603
1379
 
604
- ca_attach(ca);
605
- if ( ca->data_type == CA_OBJECT
606
- && ( (data_type >= CA_INT8 && data_type <= CA_UINT64)
607
- || data_type == CA_FLOAT32 || data_type == CA_FLOAT64 ) ) {
608
- /* object -> int/float: give the cast a mask buffer so an unparseable
609
- cell becomes UNDEF (see ext/carray_cast_func.rb). Cast into a
610
- scratch mask seeded from the source mask, then attach it to the
611
- output only if some cell ended up masked -- an all-valid cast keeps
612
- the mask-less result the legacy path produced. */
613
- ca_size_t ne = cb->elements;
614
- ca_size_t i;
615
- boolean8_t any = 0;
616
- boolean8_t *scratch = ALLOC_N(boolean8_t, ne > 0 ? ne : 1);
617
- if ( cb->mask ) {
618
- memcpy(scratch, cb->mask->ptr, ne); /* copy of source mask */
619
- }
620
- else {
621
- memset(scratch, 0, ne);
622
- }
623
- ca_cast_block_with_mask(ne, ca, ca->ptr, cb, cb->ptr, scratch);
624
- for (i=0; i<ne; i++) {
625
- if ( scratch[i] ) {
626
- any = 1;
627
- break;
628
- }
629
- }
630
- if ( any ) {
631
- if ( ! cb->mask ) {
632
- ca_create_mask(cb);
633
- }
634
- memcpy(cb->mask->ptr, scratch, ne);
635
- }
636
- xfree(scratch);
637
- }
638
- else if ( ca_has_mask(ca) ) {
639
- ca_cast_block_with_mask(cb->elements, ca, ca->ptr, cb, cb->ptr,
640
- (boolean8_t*)ca->mask->ptr);
641
- }
642
- else {
643
- ca_cast_block(cb->elements, ca, ca->ptr, cb, cb->ptr);
644
- }
645
- ca_detach(ca);
1380
+ ctx.ca = ca;
1381
+ ctx.cb = cb;
1382
+ ctx.scratch = NULL;
1383
+ ca_attach(ca); /* window: released by ca_to_type_release under rb_ensure */
1384
+ rb_ensure(ca_to_type_cast, (VALUE) &ctx, ca_to_type_release, (VALUE) &ctx);
646
1385
 
647
1386
  /* When rtype is a data_class (e.g. CAStruct subclass), wrap the
648
1387
  result in CARecord so the cast output carries data_class. */
@@ -1046,30 +1785,30 @@ rb_ca_ptr2obj (VALUE self, void *ptr)
1046
1785
  }
1047
1786
 
1048
1787
  /* Surface -> storage-domain conversion for a Face store (mirror of the
1049
- storage_to_scalar read hook). Non-Face `ca` returns `obj` unchanged, so
1788
+ storage_to_element read hook). Non-Face `ca` returns `obj` unchanged, so
1050
1789
  the non-Face store path is untouched. For a Face, the per-obj_type C
1051
1790
  table is consulted first (fast path); if none is registered, the Ruby
1052
- `scalar_to_storage` method is called when defined. A Face that recognizes
1791
+ `element_to_storage` method is called when defined. A Face that recognizes
1053
1792
  `obj` returns a storage-domain value (e.g. an Integer in its unit); a bare
1054
1793
  Integer / String falls through unchanged to the storage cast. */
1055
1794
  VALUE
1056
- ca_face_scalar_to_storage (VALUE self, CArray *ca, VALUE obj)
1795
+ ca_face_element_to_storage (VALUE self, CArray *ca, VALUE obj)
1057
1796
  {
1058
- ca_face_scalar_to_storage_fn fn;
1797
+ ca_face_element_to_storage_fn fn;
1059
1798
  if ( ! ca_face_safe_check(ca) || obj == CA_UNDEF ) {
1060
1799
  return obj;
1061
1800
  }
1062
- fn = ca_face_scalar_to_storage_table[ca->obj_type];
1801
+ fn = ca_face_element_to_storage_table[ca->obj_type];
1063
1802
  if ( fn != NULL ) {
1064
1803
  return fn(self, obj);
1065
1804
  }
1066
1805
  else {
1067
- static ID id_scalar_to_storage = 0;
1068
- if ( id_scalar_to_storage == 0 ) {
1069
- id_scalar_to_storage = rb_intern("scalar_to_storage");
1806
+ static ID id_element_to_storage = 0;
1807
+ if ( id_element_to_storage == 0 ) {
1808
+ id_element_to_storage = rb_intern("element_to_storage");
1070
1809
  }
1071
- if ( rb_respond_to(self, id_scalar_to_storage) ) {
1072
- return rb_funcall(self, id_scalar_to_storage, 1, obj);
1810
+ if ( rb_respond_to(self, id_element_to_storage) ) {
1811
+ return rb_funcall(self, id_element_to_storage, 1, obj);
1073
1812
  }
1074
1813
  }
1075
1814
  return obj;
@@ -1092,7 +1831,7 @@ rb_ca_obj2ptr (VALUE self, VALUE obj, void *ptr)
1092
1831
  if ( ca_face_safe_check(ca) ) {
1093
1832
  /* Face write hook: turn a surface value object (Scalar / Time /
1094
1833
  DateTime) into a storage-domain value the cast below consumes. */
1095
- obj = ca_face_scalar_to_storage(self, ca, obj);
1834
+ obj = ca_face_element_to_storage(self, ca, obj);
1096
1835
  convert_type = ca_storage_type_of(ca);
1097
1836
  }
1098
1837
  if ( convert_type == CA_FIXLEN ) {
@@ -1114,7 +1853,7 @@ rb_ca_wrap_writable (VALUE arg, VALUE rtype)
1114
1853
  if ( rb_obj_is_carray(obj) ) { /* obj == carray */
1115
1854
  TypedData_Get_Struct(obj, CArray, &carray_data_type, ca);
1116
1855
  if ( ca_is_readonly(ca) ) {
1117
- rb_raise(rb_eRuntimeError, "can't modify read-only carray");
1856
+ rb_raise(rb_eRuntimeError, "can not modify read-only array");
1118
1857
  }
1119
1858
  if ( NIL_P(rtype) ) {
1120
1859
  data_type = ca->data_type;
@@ -1152,7 +1891,7 @@ rb_ca_wrap_writable (VALUE arg, VALUE rtype)
1152
1891
  }
1153
1892
  TypedData_Get_Struct(obj, CArray, &carray_data_type, ca);
1154
1893
  if ( ca_is_readonly(ca) ) {
1155
- rb_raise(rb_eRuntimeError, "can't modify read-only carray");
1894
+ rb_raise(rb_eRuntimeError, "can not modify read-only array");
1156
1895
  }
1157
1896
  if ( NIL_P(rtype) ) {
1158
1897
  data_type = ca->data_type;
@@ -1185,7 +1924,7 @@ rb_ca_wrap_writable (VALUE arg, VALUE rtype)
1185
1924
  }
1186
1925
  else {
1187
1926
  volatile VALUE inspect = rb_inspect(CLASS_OF(obj));
1188
- rb_raise(rb_eRuntimeError,
1927
+ rb_raise(rb_eTypeError,
1189
1928
  "given object '%s' can't be wrapped as carray",
1190
1929
  StringValuePtr(inspect));
1191
1930
  }
@@ -1366,6 +2105,92 @@ rb_ca_cast (volatile VALUE self)
1366
2105
  return obj;
1367
2106
  }
1368
2107
 
2108
+ /* A String operand against a fixlen array is a value of that array's cell
2109
+ width. ca_value_to_data_type answers CA_OBJECT for a String, so without
2110
+ this the operand became an object scalar and the comparison ran down the
2111
+ object lane -- an rb_funcall of String#== per cell, against the cell's
2112
+ NUL-padded text. A fixlen array pads a short String on write
2113
+ (`a[i] = "be"` stores "be\0\0\0"), so that answered that an array does
2114
+ not equal the string it was written from, and did it 30x slower than the
2115
+ memcmp lane the fixlen comparison bodies are there to provide.
2116
+
2117
+ Two fixlen *arrays* of different widths keep the variable-width byte
2118
+ string semantics those bodies implement (min-width memcmp with a length
2119
+ tiebreak); this is only about a scalar standing in for a cell. */
2120
+ static int
2121
+ ca_fixlen_scalar_operand (volatile VALUE *scalar, VALUE reference)
2122
+ {
2123
+ CArray *cr;
2124
+ /* A String is the only operand that means a cell value here. A Regexp
2125
+ (match) or any other object keeps its own coercion: those are asking
2126
+ something about the bytes, not standing in for them. */
2127
+ if ( ! RB_TYPE_P(*scalar, T_STRING) || ! rb_obj_is_carray(reference) ) {
2128
+ return 0;
2129
+ }
2130
+ TypedData_Get_Struct(reference, CArray, &carray_data_type, cr);
2131
+ if ( cr->data_type != CA_FIXLEN ) {
2132
+ return 0;
2133
+ }
2134
+ *scalar = rb_cscalar_new_with_value(CA_FIXLEN, cr->bytes, *scalar);
2135
+ return 1;
2136
+ }
2137
+
2138
+ /* Where a Ruby Integer falls against the range of integer data type `dt`:
2139
+ -1 below it, 1 above it, 0 inside. Anything but an Integer, and a
2140
+ non-integer `dt`, answer 0. */
2141
+ int
2142
+ ca_integer_range_side (VALUE v, int8_t dt)
2143
+ {
2144
+ VALUE lo, hi;
2145
+ if ( ! RB_INTEGER_TYPE_P(v) ) return 0;
2146
+ switch ( dt ) {
2147
+ case CA_INT8: lo = INT2FIX(INT8_MIN); hi = INT2FIX(INT8_MAX); break;
2148
+ case CA_UINT8: lo = INT2FIX(0); hi = INT2FIX(UINT8_MAX); break;
2149
+ case CA_INT16: lo = INT2FIX(INT16_MIN); hi = INT2FIX(INT16_MAX); break;
2150
+ case CA_UINT16: lo = INT2FIX(0); hi = INT2FIX(UINT16_MAX); break;
2151
+ case CA_INT32: lo = LL2NUM(INT32_MIN); hi = LL2NUM(INT32_MAX); break;
2152
+ case CA_UINT32: lo = INT2FIX(0); hi = ULL2NUM(UINT32_MAX); break;
2153
+ case CA_INT64: lo = LL2NUM(INT64_MIN); hi = LL2NUM(INT64_MAX); break;
2154
+ case CA_UINT64: lo = INT2FIX(0); hi = ULL2NUM(UINT64_MAX); break;
2155
+ default: return 0;
2156
+ }
2157
+ if ( RTEST(rb_funcall(v, rb_intern("<"), 1, lo)) ) return -1;
2158
+ if ( RTEST(rb_funcall(v, rb_intern(">"), 1, hi)) ) return 1;
2159
+ return 0;
2160
+ }
2161
+
2162
+ /* Refuses a Ruby Integer that does not fit integer data type `dt`. Taken
2163
+ modulo the type, 256 against a uint8 array would be 0 and compare equal
2164
+ to it. */
2165
+ void
2166
+ ca_check_integer_fits (VALUE v, int8_t dt)
2167
+ {
2168
+ if ( ca_integer_range_side(v, dt) != 0 ) {
2169
+ rb_raise(rb_eRangeError, "%"PRIsVALUE" is out of range for %s",
2170
+ rb_inspect(v), ca_type_name[dt]);
2171
+ }
2172
+ }
2173
+
2174
+ /* A Ruby Integer operand against an integer array is taken in the array's
2175
+ own data type, refused when it does not fit (ca_check_integer_fits).
2176
+ Widening instead would make the result's type depend on a scalar's
2177
+ value. A Float, a Face and an object array keep the coercion below. */
2178
+ static int
2179
+ ca_integer_scalar_operand (volatile VALUE *scalar, VALUE reference)
2180
+ {
2181
+ CArray *cr;
2182
+ if ( ! RB_INTEGER_TYPE_P(*scalar) || ! rb_obj_is_carray(reference) ) {
2183
+ return 0;
2184
+ }
2185
+ TypedData_Get_Struct(reference, CArray, &carray_data_type, cr);
2186
+ if ( ! ca_is_integer_type(cr) || ca_is_face(cr) ) {
2187
+ return 0;
2188
+ }
2189
+ ca_check_integer_fits(*scalar, cr->data_type);
2190
+ *scalar = rb_cscalar_new_with_value(cr->data_type, 0, *scalar);
2191
+ return 1;
2192
+ }
2193
+
1369
2194
  /* CArray.cast(value) -- singleton entry delegating to rb_ca_cast, which
1370
2195
  returns a CArray unchanged and coerces a Ruby value to a CScalar / CArray.
1371
2196
  User doc lives in yard-stubs/carray_cast.rb. */
@@ -1376,6 +2201,41 @@ rb_ca_s_cast (VALUE klass, VALUE val)
1376
2201
  return rb_ca_cast(val);
1377
2202
  }
1378
2203
 
2204
+ /* An operator operand that exports MemoryView, as a CArray. When the object
2205
+ also defines to_ca, that is asked first, in the order wrap_readonly uses:
2206
+ the MemoryView carries only the values buffer, while to_ca can hand over
2207
+ what the object knows besides it (the nulls of an Arrow array as a mask,
2208
+ a time type as a Face). Only MemoryView producers come here, so an object
2209
+ with to_ca alone (a Range) is still not an operand. */
2210
+ static VALUE
2211
+ ca_operand_from_memory_view (VALUE obj)
2212
+ {
2213
+ volatile VALUE ca;
2214
+ if ( rb_respond_to(obj, rb_intern("to_ca")) ) {
2215
+ ca = rb_funcall(obj, rb_intern("to_ca"), 0);
2216
+ if ( ! rb_obj_is_carray(ca) ) {
2217
+ volatile VALUE inspect = rb_inspect(CLASS_OF(obj));
2218
+ rb_raise(rb_eTypeError,
2219
+ "%s#to_ca did not return a CArray", StringValuePtr(inspect));
2220
+ }
2221
+ return ca;
2222
+ }
2223
+ return rb_funcall(rb_cCArray, rb_intern("wrap_memory_view"), 1, obj);
2224
+ }
2225
+
2226
+ /* The name a coercion error gives an operand: its data type, or for a Face
2227
+ its class. A Face reports the data type of its storage ("fixlen" for a
2228
+ time, a categorical, a record or a string column), which says nothing
2229
+ about what the operand is. */
2230
+ static const char *
2231
+ ca_coerce_operand_name (VALUE obj, CArray *ca)
2232
+ {
2233
+ if ( ca_is_face(ca) ) {
2234
+ return rb_obj_classname(obj);
2235
+ }
2236
+ return ca_type_name[ca->data_type];
2237
+ }
2238
+
1379
2239
  void
1380
2240
  rb_ca_cast_self_or_other (volatile VALUE *self, volatile VALUE *other)
1381
2241
  {
@@ -1388,11 +2248,11 @@ rb_ca_cast_self_or_other (volatile VALUE *self, volatile VALUE *other)
1388
2248
  Excludes T_STRING which has its own legacy interpretation. */
1389
2249
  if ( ! rb_obj_is_carray(*self) && TYPE(*self) != T_STRING &&
1390
2250
  rb_memory_view_available_p(*self) ) {
1391
- *self = rb_funcall(rb_cCArray, rb_intern("wrap_memory_view"), 1, *self);
2251
+ *self = ca_operand_from_memory_view(*self);
1392
2252
  }
1393
2253
  if ( ! rb_obj_is_carray(*other) && TYPE(*other) != T_STRING &&
1394
2254
  rb_memory_view_available_p(*other) ) {
1395
- *other = rb_funcall(rb_cCArray, rb_intern("wrap_memory_view"), 1, *other);
2255
+ *other = ca_operand_from_memory_view(*other);
1396
2256
  }
1397
2257
 
1398
2258
  if ( ! rb_obj_is_carray(*self) ) {
@@ -1409,6 +2269,12 @@ rb_ca_cast_self_or_other (volatile VALUE *self, volatile VALUE *other)
1409
2269
  *self = rb_cscalar_new_with_value(CA_CMPLX128, 0, *self);
1410
2270
  }
1411
2271
  #endif
2272
+ else if ( ca_fixlen_scalar_operand(self, *other) ) {
2273
+ /* width taken from the fixlen reference; see the helper above */
2274
+ }
2275
+ else if ( ca_integer_scalar_operand(self, *other) ) {
2276
+ /* the array's own integer type; see the helper above */
2277
+ }
1412
2278
  else if ( rb_ca_is_float_type(*other) ) {
1413
2279
  *self = rb_cscalar_new_with_value(CA_FLOAT64, 0, *self);
1414
2280
  }
@@ -1437,6 +2303,12 @@ rb_ca_cast_self_or_other (volatile VALUE *self, volatile VALUE *other)
1437
2303
  *other = rb_cscalar_new_with_value(CA_CMPLX128, 0, *other);
1438
2304
  }
1439
2305
  #endif
2306
+ else if ( ca_fixlen_scalar_operand(other, *self) ) {
2307
+ /* width taken from the fixlen reference; see the helper above */
2308
+ }
2309
+ else if ( ca_integer_scalar_operand(other, *self) ) {
2310
+ /* the array's own integer type; see the helper above */
2311
+ }
1440
2312
  else if ( rb_ca_is_float_type(*self) ) {
1441
2313
  *other = rb_cscalar_new_with_value(CA_FLOAT64, 0, *other);
1442
2314
  }
@@ -1506,10 +2378,10 @@ rb_ca_cast_self_or_other (volatile VALUE *self, volatile VALUE *other)
1506
2378
  return;
1507
2379
  }
1508
2380
 
1509
- rb_raise(rb_eRuntimeError,
2381
+ rb_raise(rb_eTypeError,
1510
2382
  "can't coerce carray with data_types of '%s' and '%s'",
1511
- ca_type_name[ca->data_type],
1512
- ca_type_name[cb->data_type]);
2383
+ ca_coerce_operand_name(*self, ca),
2384
+ ca_coerce_operand_name(*other, cb));
1513
2385
  }
1514
2386
 
1515
2387
  /* CArray.cast_self_or_other(self, other) — promote self and other
@@ -1610,33 +2482,129 @@ ca_promote_type (int8_t a, int8_t b)
1610
2482
  /* b should be cast to a */
1611
2483
  return a;
1612
2484
  }
1613
- rb_raise(rb_eRuntimeError,
2485
+ rb_raise(rb_eCADataTypeError,
1614
2486
  "can't promote data_types '%s' and '%s' to a common type",
1615
2487
  ca_type_name[a], ca_type_name[b]);
1616
2488
  }
1617
2489
 
1618
- /* CArray.result_type(*args) -- common data_type each operand promotes to
1619
- under ca_cast_table. Each arg is classified by ca_arg_to_data_type: a
2490
+ /* The type in which values of types `a` and `b`, whose common type is `t`,
2491
+ are matched for equality. It is `t`, except where `t` cannot hold every
2492
+ value of both: an integer pair of mixed sign whose common type is
2493
+ unsigned (uint8 with int8 is uint8, where -1 would read as 255) moves to
2494
+ a signed type that holds both, or, for 64 bits, where no numeric type
2495
+ does, to object, whose Integers compare by value; and an integer of 32
2496
+ bits or more beside a float32 (or cmplx64) moves to double precision. `eq` answers the
2497
+ mixed-sign pair by value. */
2498
+ int8_t
2499
+ ca_value_match_type (int8_t t, int8_t a, int8_t b)
2500
+ {
2501
+ int signed_a = ( a == CA_INT8 || a == CA_INT16 || a == CA_INT32 || a == CA_INT64 );
2502
+ int signed_b = ( b == CA_INT8 || b == CA_INT16 || b == CA_INT32 || b == CA_INT64 );
2503
+ int wide_a = ( a == CA_INT32 || a == CA_UINT32 || a == CA_INT64 || a == CA_UINT64 );
2504
+ int wide_b = ( b == CA_INT32 || b == CA_UINT32 || b == CA_INT64 || b == CA_UINT64 );
2505
+ /* An integer of 32 bits or more beside a single-precision float: float32
2506
+ holds integers only up to 2**24 (every value of a 16-bit integer, not of
2507
+ a 32-bit one), so the match moves to double precision, which holds every
2508
+ value of a 32-bit integer and of a float32 (a 64-bit integer only up to
2509
+ 2**53). */
2510
+ if ( wide_a || wide_b ) {
2511
+ if ( t == CA_FLOAT32 ) return CA_FLOAT64;
2512
+ if ( t == CA_CMPLX64 ) return CA_CMPLX128;
2513
+ }
2514
+ if ( ! signed_a && ! signed_b ) return t;
2515
+ switch ( t ) {
2516
+ case CA_UINT8: return CA_INT16;
2517
+ case CA_UINT16: return CA_INT32;
2518
+ case CA_UINT32: return CA_INT64;
2519
+ case CA_UINT64: return CA_OBJECT;
2520
+ default: return t;
2521
+ }
2522
+ }
2523
+
2524
+ /* ca.value_match_type(t, other_type) -- ca_value_match_type with self's
2525
+ data type as the first operand, for the set family and locate_addr. */
2526
+ static VALUE
2527
+ rb_ca_value_match_type (VALUE self, VALUE vt, VALUE vother)
2528
+ {
2529
+ CArray *ca;
2530
+ GetCArray(self, ca);
2531
+ return rb_ca_data_type_to_sym(ca_value_match_type(rb_ca_guess_type(vt), ca->data_type,
2532
+ rb_ca_guess_type(vother)));
2533
+ }
2534
+
2535
+ /* A result_type argument that is a scalar: a Ruby value (Integer, Float,
2536
+ true, ...) or a CScalar. The operators let a scalar take the type of the
2537
+ array beside it, so result_type does too. */
2538
+ static int
2539
+ ca_arg_is_scalar (VALUE obj)
2540
+ {
2541
+ if ( rb_obj_is_carray(obj) ) {
2542
+ CArray *ca;
2543
+ TypedData_Get_Struct(obj, CArray, &carray_data_type, ca);
2544
+ return ca_is_scalar(ca);
2545
+ }
2546
+ if ( TYPE(obj) == T_SYMBOL || TYPE(obj) == T_STRING || TYPE(obj) == T_CLASS ) {
2547
+ return 0;
2548
+ }
2549
+ return ca_mv_probe_data_type(obj) < 0;
2550
+ }
2551
+
2552
+ /* The type a scalar `obj` (of type `st`) and an array of type `at` share,
2553
+ by the rule the operators apply: a scalar of the same kind takes the
2554
+ array's type (0.1 beside float32 is float32, 1 beside int8 is int8); one
2555
+ of another kind promotes (1.5 beside int32 is float64). An Integer that
2556
+ does not fit the array's integer type takes the type of its value, where
2557
+ an operator would raise RangeError. */
2558
+ int8_t
2559
+ ca_promote_scalar_type (int8_t at, VALUE obj, int8_t st)
2560
+ {
2561
+ if ( RB_INTEGER_TYPE_P(obj) && at >= CA_INT8 && at <= CA_UINT64 ) {
2562
+ return ( ca_integer_range_side(obj, at) == 0 ) ? at : ca_promote_type(at, st);
2563
+ }
2564
+ if ( ca_cast_table2[st][at] >= 0 ) {
2565
+ return at;
2566
+ }
2567
+ return ca_promote_type(at, st);
2568
+ }
2569
+
2570
+ /* CArray.result_type(*args) -- common data_type of the operands, by the rule
2571
+ the operators use. Each arg is classified by ca_arg_to_data_type: a
1620
2572
  CArray / Symbol / String / Class gives a data_type representation, a
1621
2573
  Numeric / bool / nil / Object gives a value whose data_type is inferred.
1622
- Folds pairwise via ca_promote_type; returns a Symbol. User doc (with
1623
- the value-vs-code distinction and examples) lives in
1624
- yard-stubs/carray_cast.rb. */
2574
+ The non-scalar arguments fold pairwise via ca_promote_type; a scalar then
2575
+ joins that type through ca_promote_scalar_type. With no non-scalar
2576
+ argument every argument folds by its own type. Returns a Symbol. User
2577
+ doc lives in yard-stubs/carray_cast.rb. */
1625
2578
 
1626
2579
  static VALUE
1627
2580
  rb_ca_s_result_type (int argc, VALUE *argv, VALUE klass)
1628
2581
  {
1629
- int8_t cur;
2582
+ int8_t cur = -1;
1630
2583
  int i;
2584
+ VALUE rest, ropt;
2585
+
2586
+ /* Keywords are refused; without this a keyword Hash would be classified
2587
+ as one more operand and the answer would turn into object. */
2588
+ rb_scan_args(argc, argv, "*:", &rest, &ropt);
2589
+ rb_reject_options(ropt);
2590
+ argc = (int) RARRAY_LEN(rest);
2591
+ argv = RARRAY_PTR(rest);
1631
2592
 
1632
2593
  if ( argc < 1 ) {
1633
2594
  rb_raise(rb_eArgError, "result_type requires at least one argument");
1634
2595
  }
1635
2596
 
1636
- cur = ca_arg_to_data_type(argv[0]);
1637
- for ( i = 1; i < argc; i++ ) {
1638
- int8_t next = ca_arg_to_data_type(argv[i]);
1639
- cur = ca_promote_type(cur, next);
2597
+ for ( i = 0; i < argc; i++ ) {
2598
+ if ( ! ca_arg_is_scalar(argv[i]) ) {
2599
+ int8_t t = ca_arg_to_data_type(argv[i]);
2600
+ cur = ( cur < 0 ) ? t : ca_promote_type(cur, t);
2601
+ }
2602
+ }
2603
+ for ( i = 0; i < argc; i++ ) {
2604
+ if ( ca_arg_is_scalar(argv[i]) ) {
2605
+ int8_t t = ca_arg_to_data_type(argv[i]);
2606
+ cur = ( cur < 0 ) ? t : ca_promote_scalar_type(cur, argv[i], t);
2607
+ }
1640
2608
  }
1641
2609
  /* Return Symbol for family consistency with
1642
2610
  CArray.value_to_data_type and ca.data_type. */
@@ -1676,9 +2644,6 @@ rb_ca_s_promote_list (int argc, VALUE *argv, VALUE klass)
1676
2644
  {
1677
2645
  VALUE list, kwargs, data_type = Qnil;
1678
2646
  long n, i;
1679
- int all_face = 1, any_face = 0, all_primitive = 1;
1680
- VALUE face_class = Qnil;
1681
- CArray *ref_face = NULL;
1682
2647
  int8_t common = -1;
1683
2648
 
1684
2649
  rb_scan_args(argc, argv, "1:", &list, &kwargs);
@@ -1697,49 +2662,26 @@ rb_ca_s_promote_list (int argc, VALUE *argv, VALUE klass)
1697
2662
  if ( RB_TYPE_P(data_type, T_CLASS) || RB_TYPE_P(data_type, T_MODULE) ) {
1698
2663
  rb_raise(rb_eArgError,
1699
2664
  "promote_list: data_type must be a primitive Symbol "
1700
- "(Class targets are not supported; use auto-detect or "
1701
- "strip Face manually)");
1702
- }
1703
- }
1704
-
1705
- /* Classify each element: Face / primitive CArray. Detect Face-class
1706
- homogeneity as we go. */
1707
- for ( i = 0; i < n; i++ ) {
1708
- VALUE elem = rb_ary_entry(list, i);
1709
- CArray *ca;
1710
- rb_check_carray_object(elem);
1711
- TypedData_Get_Struct(elem, CArray, &carray_data_type, ca);
1712
- if ( ca_is_face(ca) ) {
1713
- any_face = 1;
1714
- all_primitive = 0;
1715
- if ( NIL_P(face_class) ) {
1716
- face_class = rb_obj_class(elem);
1717
- ref_face = ca;
1718
- } else if ( rb_obj_class(elem) != face_class ) {
1719
- all_face = 0; /* will be caught after the loop */
1720
- } else if ( all_face ) {
1721
- if ( ! ca_face_state_compatible(rb_ary_entry(list, 0), ref_face,
1722
- elem, ca) ) {
1723
- rb_raise(rb_eArgError,
1724
- "promote_list: Face state mismatch across elements "
1725
- "(= %s instance at index %ld differs from index 0)",
1726
- rb_class2name(face_class), i);
1727
- }
1728
- }
1729
- } else {
1730
- all_face = 0;
2665
+ "(Class targets are not supported; leave data_type out to "
2666
+ "keep a Face)");
1731
2667
  }
1732
2668
  }
1733
2669
 
1734
2670
  /* Explicit data_type path: primitive Symbol, all elements must be
1735
2671
  primitive. Reject if any Face in list. */
1736
2672
  if ( ! NIL_P(data_type) ) {
1737
- if ( any_face ) {
1738
- rb_raise(rb_eArgError,
1739
- "promote_list: data_type %s cannot be applied to a list "
1740
- "containing Face elements (= Face cannot be coerced to "
1741
- "a primitive without losing identity)",
1742
- RSTRING_PTR(rb_inspect(data_type)));
2673
+ for ( i = 0; i < n; i++ ) {
2674
+ VALUE elem = rb_ary_entry(list, i);
2675
+ CArray *ca;
2676
+ rb_check_carray_object(elem);
2677
+ TypedData_Get_Struct(elem, CArray, &carray_data_type, ca);
2678
+ if ( ca_is_face(ca) ) {
2679
+ rb_raise(rb_eArgError,
2680
+ "promote_list: data_type %s cannot be applied to a list "
2681
+ "containing Face elements (= Face cannot be coerced to "
2682
+ "a primitive without losing identity)",
2683
+ RSTRING_PTR(rb_inspect(data_type)));
2684
+ }
1743
2685
  }
1744
2686
  {
1745
2687
  VALUE out = rb_ary_new_capa(n);
@@ -1754,41 +2696,24 @@ rb_ca_s_promote_list (int argc, VALUE *argv, VALUE klass)
1754
2696
  }
1755
2697
  }
1756
2698
 
1757
- /* Auto-detect path. */
1758
- if ( any_face && ! all_face ) {
1759
- if ( all_primitive == 0 && any_face ) {
1760
- /* Mixed: at least one Face and at least one non-Face,
1761
- OR mixed Face classes. Both are rejected. */
1762
- rb_raise(rb_eArgError,
1763
- "promote_list: cannot mix Face and non-Face (or heterogeneous "
1764
- "Face classes); pass a homogeneous Face list, or strip Face "
1765
- "manually with .parent for the storage-level layout");
1766
- }
1767
- }
1768
-
1769
- if ( all_face ) {
1770
- /* Homogeneous Face: validate portability, then pass through. */
1771
- if ( n > 1 && ! ca_face_state_portable(ref_face->obj_type, face_class) ) {
1772
- rb_raise(rb_eArgError,
1773
- "promote_list: %s state is not portable across multiple "
1774
- "elements (= per-parent storage like CAConstString's buffer); "
1775
- "strip Face manually with .parent for a storage-level list",
1776
- rb_class2name(face_class));
1777
- }
1778
- /* Return a fresh Array so callers can mutate without aliasing. */
1779
- {
1780
- VALUE out = rb_ary_new_capa(n);
1781
- for ( i = 0; i < n; i++ ) rb_ary_push(out, rb_ary_entry(list, i));
1782
- return out;
1783
- }
2699
+ /* Auto-detect path. A list of one Face class passes through (the
2700
+ multi-parent constructor lifts it). */
2701
+ if ( ! NIL_P(ca_face_list_class(list, "promote_list")) ) {
2702
+ VALUE out = rb_ary_new_capa(n);
2703
+ for ( i = 0; i < n; i++ ) rb_ary_push(out, rb_ary_entry(list, i));
2704
+ return out;
1784
2705
  }
1785
2706
 
1786
2707
  /* All primitive: result_type to determine common data_type, then
1787
2708
  wrap_readonly each element. */
2709
+ /* The arrays are joined value for value, so a list mixing signed and
2710
+ unsigned integers takes a type that holds both (uint8 with int8 is
2711
+ int16, not uint8, where -1 would read as 255): the rule equality
2712
+ matches such a pair by. */
1788
2713
  common = ca_arg_to_data_type(rb_ary_entry(list, 0));
1789
2714
  for ( i = 1; i < n; i++ ) {
1790
2715
  int8_t next = ca_arg_to_data_type(rb_ary_entry(list, i));
1791
- common = ca_promote_type(common, next);
2716
+ common = ca_value_match_type(ca_promote_type(common, next), common, next);
1792
2717
  }
1793
2718
  {
1794
2719
  VALUE common_sym = rb_ca_data_type_to_sym(common);
@@ -1817,13 +2742,16 @@ rb_ca_cast_other (VALUE *self, volatile VALUE *other)
1817
2742
  Excludes T_STRING which has its own legacy interpretation. */
1818
2743
  if ( ! rb_obj_is_carray(*other) && TYPE(*other) != T_STRING &&
1819
2744
  rb_memory_view_available_p(*other) ) {
1820
- *other = rb_funcall(rb_cCArray, rb_intern("wrap_memory_view"), 1, *other);
2745
+ *other = ca_operand_from_memory_view(*other);
1821
2746
  }
1822
2747
 
1823
2748
  if ( ! rb_obj_is_carray(*other) ) {
1824
2749
  if ( rb_ca_is_object_type(*self) ) {
1825
2750
  *other = rb_cscalar_new_with_value(CA_OBJECT, 0, *other);
1826
2751
  }
2752
+ else if ( ca_fixlen_scalar_operand(other, *self) ) {
2753
+ return; /* already the receiver's data_type and width */
2754
+ }
1827
2755
  else if ( rb_ca_is_float_type(*self) ) {
1828
2756
  *other = rb_cscalar_new_with_value(CA_FLOAT64, 0, *other);
1829
2757
  }
@@ -1859,10 +2787,10 @@ rb_ca_cast_other (VALUE *self, volatile VALUE *other)
1859
2787
  return;
1860
2788
  }
1861
2789
 
1862
- rb_raise(rb_eRuntimeError,
2790
+ rb_raise(rb_eTypeError,
1863
2791
  "can't coerce carray with data_types of '%s' and '%s'",
1864
- ca_type_name[ca->data_type],
1865
- ca_type_name[cb->data_type]);
2792
+ ca_coerce_operand_name(*self, ca),
2793
+ ca_coerce_operand_name(*other, cb));
1866
2794
  }
1867
2795
 
1868
2796
  /* CArray#cast_with(other) -- coerce self and other to a common
@@ -1877,7 +2805,7 @@ rb_ca_cast_with (VALUE self, VALUE other)
1877
2805
  rb_ca_cast_self_or_other(&self, &other);
1878
2806
  }
1879
2807
  else {
1880
- rb_raise(rb_eRuntimeError, "first argument should be a carray");
2808
+ rb_raise(rb_eTypeError, "first argument should be a carray");
1881
2809
  }
1882
2810
  return rb_assoc_new(self, other);
1883
2811
  }
@@ -1926,7 +2854,7 @@ rb_ca_clip_uint64 (VALUE self)
1926
2854
  volatile VALUE vout = rb_carray_new(CA_UINT64, ca->ndim, ca->dim, 0, NULL);
1927
2855
  CArray *out;
1928
2856
  GetCArray(vout, out);
1929
- ca_attach(src);
2857
+ ca_attach(src); /* window: nothing raises inside */
1930
2858
  double *sp = (double *) src->ptr;
1931
2859
  uint64_t *op = (uint64_t *) out->ptr;
1932
2860
  ca_size_t n = ca->elements;
@@ -1954,16 +2882,19 @@ rb_ca_clip_uint64 (VALUE self)
1954
2882
  Polymorphic cast (NOT a fresh allocator): `data` is coerced into a CArray
1955
2883
  of the target data_type, dispatching on its Ruby class:
1956
2884
 
1957
- Array -> shape-guessed CArray, element-wise store (C fast path)
1958
- CArray -> copy if same data_type, else #to_type (C fast path)
1959
- nil -> empty CArray (C fast path)
1960
- Range / String / Numeric / #to_ca responder (Ruby fallback)
1961
-
1962
- The cold / Ruby-natural branches (arange arithmetic, the whitespace/`,`/`;`
1963
- string parser, generic coercion) stay in Ruby as `CArray.__cast_rest__`
1964
- (carray/basics.rb, eager-loaded). `type` is passed to the fallback as the
1965
- data_type Symbol so the Ruby side keeps its original `type == CA_OBJECT`
1966
- style comparisons.
2885
+ Array -> shape-guessed CArray, element-wise store
2886
+ CArray -> copy if same data_type, else #to_type
2887
+ nil -> empty CArray
2888
+ Range -> arange; a second argument, if given, is the step
2889
+ Numeric -> CScalar, or #to_ca when the object answers it
2890
+ String -> the whitespace / `,` / `;` / `_`->UNDEF parser, which reads
2891
+ far more naturally in Ruby and so is the one branch that
2892
+ calls back out to it (CArray.__cast_string__ in
2893
+ carray/basics.rb, eager-loaded). It receives the data_type
2894
+ as a Symbol.
2895
+
2896
+ A second argument is meaningful only for Range, and a third never is;
2897
+ anything else is refused.
1967
2898
  ------------------------------------------------------------------------ */
1968
2899
 
1969
2900
  static ID id_new_cast = 0, id_guess_array_shape_cast = 0, id_cast_string = 0,
@@ -1989,6 +2920,21 @@ ca_cast_impl (int8_t data_type, int argc, VALUE *argv)
1989
2920
  VALUE v = ( argc >= 1 ) ? argv[0] : Qnil;
1990
2921
  VALUE sym = ID2SYM(ca_data_type_sym[data_type]);
1991
2922
 
2923
+ /* Only the Range branch below reads a second argument (the step), and
2924
+ nothing reads a third. The arity is -1 for the step's sake, so the count
2925
+ is checked here; without it `CA_INT32(0, 2)`, which looks like the shape
2926
+ spelling `CArray.int32(3, 3)`, casts the 0 and drops the 2. */
2927
+ if ( argc > 2 ) {
2928
+ rb_raise(rb_eArgError,
2929
+ "wrong number of arguments (given %d, expected 0..2)", argc);
2930
+ }
2931
+ if ( argc == 2 && ! RTEST(rb_obj_is_kind_of(v, rb_cRange)) ) {
2932
+ rb_raise(rb_eArgError,
2933
+ "a step (the second argument) is only accepted "
2934
+ "when the first argument is a Range (got %s)",
2935
+ rb_obj_classname(v));
2936
+ }
2937
+
1992
2938
  if ( TYPE(v) == T_ARRAY ) {
1993
2939
  volatile VALUE shape, obj;
1994
2940
  shape = rb_funcall(rb_cCArray, id_guess_array_shape_cast, 1, v);
@@ -2009,8 +2955,9 @@ ca_cast_impl (int8_t data_type, int argc, VALUE *argv)
2009
2955
  return rb_carray_new(data_type, 1, &dim0, 0, NULL);
2010
2956
  }
2011
2957
  else if ( RTEST(rb_obj_is_kind_of(v, rb_cRange)) ) {
2012
- /* arange: n = ((end - begin).abs / step).floor (+1 unless exclude_end),
2013
- then seq(begin, sign * step.abs). */
2958
+ /* arange: n = ((end - begin).abs / step).floor + 1, or for an excluded
2959
+ end ((end - begin).abs.quo(step)).ceil -- the steps that start before
2960
+ the end -- then seq(begin, sign * step.abs). */
2014
2961
  volatile VALUE vbeg, vend, step, n;
2015
2962
  int sign;
2016
2963
  ca_size_t dn;
@@ -2020,7 +2967,7 @@ ca_cast_impl (int8_t data_type, int argc, VALUE *argv)
2020
2967
  vend = rb_funcall(v, id_end_cast, 0);
2021
2968
  step = ( argc >= 2 ) ? argv[1] : Qnil;
2022
2969
  if ( RTEST(step) && RTEST(rb_funcall(step, id_op_eq, 1, INT2FIX(0))) ) {
2023
- rb_raise(rb_eRuntimeError, "step should not be 0");
2970
+ rb_raise(rb_eArgError, "step should not be 0");
2024
2971
  }
2025
2972
  if ( data_type == CA_OBJECT && !RTEST(step) ) {
2026
2973
  VALUE va = rb_funcall(v, id_to_a_cast, 0); /* CA_OBJECT(v.to_a) */
@@ -2029,9 +2976,13 @@ ca_cast_impl (int8_t data_type, int argc, VALUE *argv)
2029
2976
  if ( !RTEST(step) ) step = INT2FIX(1);
2030
2977
  n = rb_funcall(vend, id_op_minus, 1, vbeg);
2031
2978
  n = rb_funcall(n, id_abs_cast, 0);
2032
- n = rb_funcall(n, id_op_div, 1, step);
2033
- n = rb_funcall(n, id_floor_cast, 0);
2034
- if ( !RTEST(rb_funcall(v, id_exclude_end_p_cast, 0)) ) {
2979
+ if ( RTEST(rb_funcall(v, id_exclude_end_p_cast, 0)) ) {
2980
+ n = rb_funcall(n, rb_intern("quo"), 1, rb_funcall(step, id_abs_cast, 0));
2981
+ n = rb_funcall(n, rb_intern("ceil"), 0);
2982
+ }
2983
+ else {
2984
+ n = rb_funcall(n, id_op_div, 1, step);
2985
+ n = rb_funcall(n, id_floor_cast, 0);
2035
2986
  n = rb_funcall(n, id_op_plus, 1, INT2FIX(1));
2036
2987
  }
2037
2988
  sign = RTEST(rb_funcall(vbeg, id_op_le, 1, vend)) ? 1 : -1;
@@ -2132,7 +3083,7 @@ rb_ca_cast_fixlen (int argc, VALUE *argv, VALUE self)
2132
3083
  ca_size_t l = NUM2SIZE(rb_funcall(rb_ary_entry(v, i), id_length_cast, 0));
2133
3084
  if ( l > maxlen ) maxlen = l;
2134
3085
  }
2135
- bytes = maxlen;
3086
+ bytes = ( maxlen > 0 ) ? maxlen : 1; /* all empty: one NUL byte */
2136
3087
  } else {
2137
3088
  bytes = NUM2SIZE(bytes_v);
2138
3089
  }
@@ -2145,7 +3096,7 @@ rb_ca_cast_fixlen (int argc, VALUE *argv, VALUE self)
2145
3096
  }
2146
3097
  else if ( NIL_P(v) ) {
2147
3098
  ca_size_t dim0 = 0;
2148
- ca_size_t bytes = NIL_P(bytes_v) ? 0 : NUM2SIZE(bytes_v);
3099
+ ca_size_t bytes = NIL_P(bytes_v) ? 1 : NUM2SIZE(bytes_v);
2149
3100
  return rb_carray_new(CA_FIXLEN, 1, &dim0, bytes, NULL);
2150
3101
  }
2151
3102
  else {
@@ -2157,17 +3108,373 @@ rb_ca_cast_fixlen (int argc, VALUE *argv, VALUE self)
2157
3108
  ? ca : ca_cast_to_fixlen(ca, fixlen_sym, bytes_v);
2158
3109
  }
2159
3110
  {
2160
- ca_size_t bytes = NIL_P(bytes_v) ? 0 : NUM2SIZE(bytes_v);
3111
+ /* No bytes: given: a String is as wide as it is (one NUL byte when
3112
+ empty). Anything else has no width to infer, and the fixlen
3113
+ check reports it. */
3114
+ ca_size_t bytes = 0;
3115
+ if ( ! NIL_P(bytes_v) ) {
3116
+ bytes = NUM2SIZE(bytes_v);
3117
+ }
3118
+ else if ( RB_TYPE_P(v, T_STRING) ) {
3119
+ bytes = ( RSTRING_LEN(v) > 0 ) ? RSTRING_LEN(v) : 1;
3120
+ }
2161
3121
  return rb_cscalar_new_with_value(CA_FIXLEN, bytes, v);
2162
3122
  }
2163
3123
  }
2164
3124
  }
2165
3125
 
3126
+ static VALUE
3127
+ rb_ca_read_text_as_number (int argc, VALUE *argv, VALUE self)
3128
+ {
3129
+ volatile VALUE src = self, out, vmask;
3130
+ VALUE rtype, report;
3131
+ CArray *ca, *co, *cmask;
3132
+ int8_t data_type;
3133
+ int is_float;
3134
+ VALUE *cells;
3135
+ boolean8_t *m_in, *m_out;
3136
+ ca_size_t i;
3137
+
3138
+ rb_scan_args(argc, argv, "11", &rtype, &report);
3139
+ if ( ! NIL_P(report) ) {
3140
+ Check_Type(report, T_ARRAY);
3141
+ }
3142
+
3143
+ data_type = rb_ca_guess_type(rtype);
3144
+ switch ( data_type ) {
3145
+ case CA_INT8: case CA_INT16: case CA_INT32: case CA_INT64:
3146
+ case CA_UINT8: case CA_UINT16: case CA_UINT32: case CA_UINT64:
3147
+ is_float = 0;
3148
+ break;
3149
+ case CA_FLOAT32: case CA_FLOAT64:
3150
+ is_float = 1;
3151
+ break;
3152
+ default:
3153
+ return Qnil; /* not a number type: the caller casts */
3154
+ }
3155
+
3156
+ TypedData_Get_Struct(src, CArray, &carray_data_type, ca);
3157
+ if ( ca->data_type != CA_OBJECT ) {
3158
+ rb_raise(rb_eArgError, "decimal reading needs an object array");
3159
+ }
3160
+ if ( ! ca_is_entity(ca) ) {
3161
+ src = rb_ca_copy(src);
3162
+ TypedData_Get_Struct(src, CArray, &carray_data_type, ca);
3163
+ }
3164
+
3165
+ out = rb_carray_new(data_type, ca->ndim, ca->dim, 0, NULL);
3166
+ TypedData_Get_Struct(out, CArray, &carray_data_type, co);
3167
+ vmask = rb_carray_new(CA_BOOLEAN, ca->ndim, ca->dim, 0, NULL);
3168
+
3169
+ cells = (VALUE *) ca->ptr;
3170
+ m_in = ca_has_mask(ca) ? (boolean8_t *) ca->mask->ptr : NULL;
3171
+ TypedData_Get_Struct(vmask, CArray, &carray_data_type, cmask);
3172
+ m_out = (boolean8_t *) cmask->ptr;
3173
+
3174
+ for (i = 0; i < ca->elements; i++) {
3175
+ VALUE v = cells[i];
3176
+ char *cell = co->ptr + i * co->bytes;
3177
+ int ok = 0;
3178
+ int missing = ( m_in && m_in[i] ) || NIL_P(v);
3179
+ if ( missing ) {
3180
+ ok = 0;
3181
+ }
3182
+ else if ( RB_TYPE_P(v, T_STRING) ) {
3183
+ int rc = ca_decimal_string_read(v, data_type, cell);
3184
+ missing = ( rc == CA_DECIMAL_BLANK );
3185
+ ok = ( rc == CA_DECIMAL_OK );
3186
+ }
3187
+ else if ( is_float ) {
3188
+ if ( RB_FLOAT_TYPE_P(v) || RB_INTEGER_TYPE_P(v) || ca_decimal_other_real(v) ) {
3189
+ double d = NUM2DBL(v);
3190
+ if ( data_type == CA_FLOAT32 ) {
3191
+ *(float32_t *) cell = (float32_t) d;
3192
+ }
3193
+ else {
3194
+ *(float64_t *) cell = d;
3195
+ }
3196
+ ok = 1;
3197
+ }
3198
+ }
3199
+ else {
3200
+ int negative = 0;
3201
+ uint64_t magnitude = 0;
3202
+ ok = ca_decimal_object_integer(v, &negative, &magnitude)
3203
+ && ca_decimal_fits(data_type, negative, magnitude);
3204
+ if ( ok ) {
3205
+ ca_decimal_store_integer(data_type, cell, 0, negative, magnitude);
3206
+ }
3207
+ }
3208
+ m_out[i] = ok ? 0 : 1;
3209
+ if ( ! ok ) {
3210
+ memset(co->ptr + i * co->bytes, 0, co->bytes);
3211
+ if ( ! missing && ! NIL_P(report) ) {
3212
+ rb_ary_push(report, SIZET2NUM(i));
3213
+ }
3214
+ }
3215
+ }
3216
+
3217
+ ca_mask_from_bytes(co, m_out);
3218
+ rb_ca_inherit_attr(out, self);
3219
+ RB_GC_GUARD(src);
3220
+ RB_GC_GUARD(vmask);
3221
+ return out;
3222
+ }
3223
+
3224
+ /* The number type a column of text reads as, for CAFrame#infer_types.
3225
+
3226
+ A cell is an integer when it is digits with an optional sign and fits
3227
+ int64, and a float when it is any other decimal number (the grammar
3228
+ above, nan / inf included). A number with a leading zero ("007",
3229
+ "007.5") is not a number here: it is a code, kept as text. Neither is
3230
+ an integer too long for int64 (an identifier), which a float would
3231
+ round. Blank, nil and masked cells are missing and say nothing.
3232
+
3233
+ Returns :int64 when every present cell is an integer, :float64 when
3234
+ every present cell is a number and one is not an integer, and nil
3235
+ otherwise -- including when no cell is present. */
3236
+
3237
+ enum {
3238
+ CA_INFER_MISSING,
3239
+ CA_INFER_INTEGER,
3240
+ CA_INFER_FLOAT,
3241
+ CA_INFER_TEXT
3242
+ };
3243
+
3244
+ static int
3245
+ ca_infer_decimal_kind_of_bytes (const char *s, long n)
3246
+ {
3247
+ long i = 0, k;
3248
+ int digits_only = 1;
3249
+ ca_decimal_trim(&s, &n);
3250
+ if ( n == 0 ) {
3251
+ return CA_INFER_MISSING;
3252
+ }
3253
+ if ( ! ca_decimal_float(s, n, NULL) ) {
3254
+ return CA_INFER_TEXT;
3255
+ }
3256
+ if ( s[i] == '+' || s[i] == '-' ) {
3257
+ i++;
3258
+ }
3259
+ if ( i + 1 < n && s[i] == '0' && s[i+1] >= '0' && s[i+1] <= '9' ) {
3260
+ return CA_INFER_TEXT; /* a leading zero */
3261
+ }
3262
+ if ( i == n ) {
3263
+ return CA_INFER_TEXT;
3264
+ }
3265
+ for (k = i; k < n; k++) {
3266
+ if ( s[k] < '0' || s[k] > '9' ) {
3267
+ digits_only = 0;
3268
+ break;
3269
+ }
3270
+ }
3271
+ if ( digits_only ) {
3272
+ int negative;
3273
+ uint64_t magnitude;
3274
+ if ( ca_decimal_integer(s, n, &negative, &magnitude) == CA_DECIMAL_OK
3275
+ && ca_decimal_fits(CA_INT64, negative, magnitude) ) {
3276
+ return CA_INFER_INTEGER;
3277
+ }
3278
+ return CA_INFER_TEXT; /* too long for int64 */
3279
+ }
3280
+ return CA_INFER_FLOAT;
3281
+ }
3282
+
3283
+ static VALUE
3284
+ rb_ca_infer_number_type_of_text (VALUE self)
3285
+ {
3286
+ volatile VALUE src = self;
3287
+ CArray *ca;
3288
+ VALUE *cells;
3289
+ boolean8_t *m;
3290
+ ca_size_t i;
3291
+ int present = 0, any_float = 0;
3292
+
3293
+ TypedData_Get_Struct(src, CArray, &carray_data_type, ca);
3294
+ if ( ca->data_type != CA_OBJECT ) {
3295
+ rb_raise(rb_eArgError, "type inference needs an object array");
3296
+ }
3297
+ if ( ! ca_is_entity(ca) ) {
3298
+ src = rb_ca_copy(src);
3299
+ TypedData_Get_Struct(src, CArray, &carray_data_type, ca);
3300
+ }
3301
+ cells = (VALUE *) ca->ptr;
3302
+ m = ca_has_mask(ca) ? (boolean8_t *) ca->mask->ptr : NULL;
3303
+
3304
+ for (i = 0; i < ca->elements; i++) {
3305
+ VALUE v = cells[i];
3306
+ int kind;
3307
+ if ( ( m && m[i] ) || NIL_P(v) ) {
3308
+ continue;
3309
+ }
3310
+ if ( RB_TYPE_P(v, T_STRING) ) {
3311
+ kind = ca_infer_decimal_kind_of_bytes(RSTRING_PTR(v), RSTRING_LEN(v));
3312
+ }
3313
+ else if ( RB_FLOAT_TYPE_P(v) ) {
3314
+ kind = CA_INFER_FLOAT;
3315
+ }
3316
+ else if ( RB_INTEGER_TYPE_P(v) ) {
3317
+ int negative;
3318
+ uint64_t magnitude;
3319
+ kind = ( ca_decimal_object_integer(v, &negative, &magnitude)
3320
+ && ca_decimal_fits(CA_INT64, negative, magnitude) )
3321
+ ? CA_INFER_INTEGER : CA_INFER_TEXT;
3322
+ }
3323
+ else {
3324
+ kind = CA_INFER_TEXT;
3325
+ }
3326
+ if ( kind == CA_INFER_TEXT ) {
3327
+ RB_GC_GUARD(src);
3328
+ return Qnil;
3329
+ }
3330
+ if ( kind != CA_INFER_MISSING ) {
3331
+ present = 1;
3332
+ any_float |= ( kind == CA_INFER_FLOAT );
3333
+ }
3334
+ }
3335
+ RB_GC_GUARD(src);
3336
+ if ( ! present ) {
3337
+ return Qnil;
3338
+ }
3339
+ return ID2SYM(rb_intern(any_float ? "float64" : "int64"));
3340
+ }
3341
+
3342
+ /* The same two readers over a CAConstString, from its buffer and its
3343
+ (start, end) byte ranges, without a String per cell. CAConstString's
3344
+ __infer_number_type_of_text__ / __read_text_as_number__ call them, so
3345
+ CAFrame#infer_types and #cast read a text column from a file this way. */
3346
+
3347
+ /* The buffer of a CAConstString and an entity of its (start, end) byte
3348
+ ranges, masked where a cell is missing. */
3349
+ static VALUE
3350
+ ca_const_string_buffer_and_ranges (VALUE cs, VALUE *buffer, CArray **ranges)
3351
+ {
3352
+ volatile VALUE vranges;
3353
+ VALUE klass = rb_const_get(rb_cObject, rb_intern("CAConstString"));
3354
+ if ( ! RTEST(rb_obj_is_kind_of(cs, klass)) ) {
3355
+ rb_raise(rb_eTypeError, "needs a CAConstString");
3356
+ }
3357
+ *buffer = rb_funcall(cs, rb_intern("buffer"), 0);
3358
+ vranges = rb_ca_parent(cs);
3359
+ TypedData_Get_Struct(vranges, CArray, &carray_data_type, *ranges);
3360
+ if ( ! ca_is_entity(*ranges) ) {
3361
+ vranges = rb_ca_copy(vranges);
3362
+ TypedData_Get_Struct(vranges, CArray, &carray_data_type, *ranges);
3363
+ }
3364
+ return vranges;
3365
+ }
3366
+
3367
+ static VALUE
3368
+ rb_ca_s_const_string_infer_number_type_of_text (VALUE klass, VALUE cs)
3369
+ {
3370
+ volatile VALUE src;
3371
+ VALUE buffer;
3372
+ CArray *pe;
3373
+ const char *buf;
3374
+ const int64_t *pair;
3375
+ boolean8_t *m;
3376
+ ca_size_t i;
3377
+ int present = 0, any_float = 0;
3378
+
3379
+ src = ca_const_string_buffer_and_ranges(cs, &buffer, &pe);
3380
+ buf = RSTRING_PTR(buffer);
3381
+ pair = (const int64_t *) pe->ptr;
3382
+ m = ca_has_mask(pe) ? (boolean8_t *) pe->mask->ptr : NULL;
3383
+ for (i = 0; i < pe->elements; i++) {
3384
+ int kind;
3385
+ if ( m && m[i] ) {
3386
+ continue;
3387
+ }
3388
+ kind = ca_infer_decimal_kind_of_bytes(buf + pair[2 * i], (long) ( pair[2 * i + 1] - pair[2 * i] ));
3389
+ if ( kind == CA_INFER_TEXT ) {
3390
+ RB_GC_GUARD(src);
3391
+ return Qnil;
3392
+ }
3393
+ if ( kind != CA_INFER_MISSING ) {
3394
+ present = 1;
3395
+ any_float |= ( kind == CA_INFER_FLOAT );
3396
+ }
3397
+ }
3398
+ RB_GC_GUARD(src);
3399
+ RB_GC_GUARD(buffer);
3400
+ if ( ! present ) {
3401
+ return Qnil;
3402
+ }
3403
+ return ID2SYM(rb_intern(any_float ? "float64" : "int64"));
3404
+ }
3405
+
3406
+ static VALUE
3407
+ rb_ca_s_const_string_read_text_as_number (VALUE klass, VALUE cs, VALUE rtype, VALUE report)
3408
+ {
3409
+ volatile VALUE src, out;
3410
+ VALUE buffer;
3411
+ CArray *pe, *co;
3412
+ int8_t data_type;
3413
+ const char *buf;
3414
+ const int64_t *pair;
3415
+ boolean8_t *m_in, *m_out = NULL;
3416
+ ca_size_t i;
3417
+
3418
+ if ( ! NIL_P(report) ) {
3419
+ Check_Type(report, T_ARRAY);
3420
+ }
3421
+ data_type = rb_ca_guess_type(rtype);
3422
+ switch ( data_type ) {
3423
+ case CA_INT8: case CA_INT16: case CA_INT32: case CA_INT64:
3424
+ case CA_UINT8: case CA_UINT16: case CA_UINT32: case CA_UINT64:
3425
+ case CA_FLOAT32: case CA_FLOAT64:
3426
+ break;
3427
+ default:
3428
+ return Qnil; /* not a number type: the caller casts */
3429
+ }
3430
+ src = ca_const_string_buffer_and_ranges(cs, &buffer, &pe);
3431
+ out = rb_carray_new(data_type, pe->ndim, pe->dim, 0, NULL);
3432
+ TypedData_Get_Struct(out, CArray, &carray_data_type, co);
3433
+ buf = RSTRING_PTR(buffer);
3434
+ pair = (const int64_t *) pe->ptr;
3435
+ m_in = ca_has_mask(pe) ? (boolean8_t *) pe->mask->ptr : NULL;
3436
+
3437
+ for (i = 0; i < pe->elements; i++) {
3438
+ char *cell = co->ptr + i * co->bytes;
3439
+ int rc = CA_DECIMAL_BLANK;
3440
+ if ( ! ( m_in && m_in[i] ) ) {
3441
+ rc = ca_decimal_read_bytes_as(buf + pair[2 * i],
3442
+ (long) ( pair[2 * i + 1] - pair[2 * i] ),
3443
+ data_type, cell);
3444
+ }
3445
+ if ( rc != CA_DECIMAL_OK ) {
3446
+ if ( m_out == NULL ) {
3447
+ ca_create_mask(co);
3448
+ m_out = (boolean8_t *) co->mask->ptr;
3449
+ }
3450
+ m_out[i] = 1;
3451
+ memset(cell, 0, co->bytes);
3452
+ if ( rc != CA_DECIMAL_BLANK && ! NIL_P(report) ) {
3453
+ rb_ary_push(report, SIZET2NUM(i));
3454
+ }
3455
+ }
3456
+ }
3457
+ RB_GC_GUARD(src);
3458
+ RB_GC_GUARD(buffer);
3459
+ return out;
3460
+ }
3461
+
2166
3462
  void
2167
3463
  Init_carray_cast (void)
2168
3464
  {
2169
3465
  /* CArray data_type conversion */
2170
3466
 
3467
+ /* Internal: CAFrame#infer_types asks the number type of a text column. */
3468
+ rb_define_method(rb_cCArray, "__infer_number_type_of_text__", rb_ca_infer_number_type_of_text, 0);
3469
+
3470
+ /* Internal: CAFrame#cast reads text cells with this grammar. With an
3471
+ Array as the second argument, the addresses of cells that hold
3472
+ something but do not read (blank, nil and masked cells are missing,
3473
+ not unreadable) are pushed onto it. */
3474
+ rb_define_method(rb_cCArray, "__read_text_as_number__", rb_ca_read_text_as_number, -1);
3475
+ rb_define_singleton_method(rb_cCArray, "__const_string_infer_number_type_of_text__", rb_ca_s_const_string_infer_number_type_of_text, 1);
3476
+ rb_define_singleton_method(rb_cCArray, "__const_string_read_text_as_number__", rb_ca_s_const_string_read_text_as_number, 3);
3477
+
2171
3478
  rb_define_method(rb_cCArray, "to_type", rb_ca_to_type_internal, -1);
2172
3479
 
2173
3480
  rb_define_method(rb_cCArray, "fixlen", rb_ca_to_fixlen, -1);
@@ -2232,6 +3539,8 @@ Init_carray_cast (void)
2232
3539
  "cast_self_or_other", rb_ca_s_cast_self_or_other, 2);
2233
3540
  rb_define_singleton_method(rb_cCArray,
2234
3541
  "result_type", rb_ca_s_result_type, -1);
3542
+ rb_define_private_method(rb_cCArray,
3543
+ "value_match_type", rb_ca_value_match_type, 2);
2235
3544
 
2236
3545
  rb_define_singleton_method(rb_cCArray,
2237
3546
  "promote_list", rb_ca_s_promote_list, -1);