carray 3.0.2 → 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 (218) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +304 -0
  3. data/README.md +3 -4
  4. data/carray.gemspec +1 -1
  5. data/ext/ca_axis_dispatch.c +225 -0
  6. data/ext/ca_axis_group.c +177 -99
  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 +66 -71
  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 +663 -214
  14. data/ext/ca_kernel_iterator.h +231 -167
  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 +132 -22
  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 +1 -0
  23. data/ext/ca_obj_byte_swap.c +40 -40
  24. data/ext/ca_obj_const_string.c +96 -25
  25. data/ext/ca_obj_face.c +406 -64
  26. data/ext/ca_obj_face.h +92 -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 +21 -21
  31. data/ext/ca_obj_grid.c +181 -124
  32. data/ext/ca_obj_meld.c +189 -107
  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 +543 -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 +47 -49
  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 -118
  46. data/ext/ca_obj_stride.c +195 -34
  47. data/ext/ca_obj_string.c +17 -18
  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 +149 -27
  54. data/ext/ca_op_ipower.c +24 -11
  55. data/ext/ca_op_powi.h +57 -30
  56. data/ext/ca_pow5_table.h +656 -0
  57. data/ext/ca_sort_kernels.h +70 -0
  58. data/ext/ca_sweep_engine.c +102 -79
  59. data/ext/ca_sweep_engine.h +7 -20
  60. data/ext/ca_triop_dispatch.c +4 -1
  61. data/ext/ca_triop_dispatch.h +5 -2
  62. data/ext/caframe_csv_reader.c +463 -0
  63. data/ext/carray.h +70 -27
  64. data/ext/carray_access.c +455 -211
  65. data/ext/carray_address_basis.c +177 -36
  66. data/ext/carray_attribute.c +45 -3
  67. data/ext/carray_bincount.c +32 -2
  68. data/ext/carray_broadcast.c +46 -14
  69. data/ext/carray_call_cfunc.c +0 -28
  70. data/ext/carray_cast.c +1444 -209
  71. data/ext/carray_cast_func.rb +34 -21
  72. data/ext/carray_conversion.c +197 -73
  73. data/ext/carray_copy.c +32 -11
  74. data/ext/carray_core.c +514 -122
  75. data/ext/carray_count.c +140 -87
  76. data/ext/carray_element.c +18 -27
  77. data/ext/carray_factorize.c +569 -277
  78. data/ext/carray_generate.c +76 -40
  79. data/ext/carray_histogram.c +152 -94
  80. data/ext/carray_hold.c +2 -1
  81. data/ext/carray_index_classifier.c +63 -96
  82. data/ext/carray_internal.h +385 -3
  83. data/ext/carray_lazy.c +834 -79
  84. data/ext/carray_loop.c +81 -65
  85. data/ext/carray_mask.c +682 -316
  86. data/ext/carray_math_kernel.h +1 -0
  87. data/ext/carray_median_percentile.c +221 -76
  88. data/ext/carray_memory_view.c +254 -248
  89. data/ext/carray_operator.c +1089 -359
  90. data/ext/carray_order.c +133 -69
  91. data/ext/carray_partition.c +102 -56
  92. data/ext/carray_random.c +273 -239
  93. data/ext/carray_scatter.c +308 -65
  94. data/ext/carray_segment.c +257 -0
  95. data/ext/carray_slab.c +170 -70
  96. data/ext/carray_sort.c +215 -218
  97. data/ext/carray_sort_kernel.c +8 -1
  98. data/ext/carray_struct.c +99 -21
  99. data/ext/carray_test.c +167 -64
  100. data/ext/carray_time_text.c +1182 -0
  101. data/ext/carray_utils.c +363 -98
  102. data/ext/extconf.rb +27 -2
  103. data/ext/mk_call_cfunc.rb +0 -2
  104. data/ext/mk_pow5_table.rb +35 -0
  105. data/ext/mkkernel.rb +1076 -846
  106. data/ext/ruby_carray.c +7 -1
  107. data/ext/version.h +4 -4
  108. data/ext/version.rb +4 -2
  109. data/lib/carray/attribute.rb +39 -0
  110. data/lib/carray/autoload_carray.rb +19 -0
  111. data/lib/carray/autoload_method_extension.rb +6 -0
  112. data/lib/carray/axis_group.rb +174 -26
  113. data/lib/carray/basics.rb +91 -25
  114. data/lib/carray/bincount_nd.rb +6 -0
  115. data/lib/carray/block_iterator.rb +118 -48
  116. data/lib/carray/boolean_reduce.rb +21 -29
  117. data/lib/carray/categorical.rb +94 -26
  118. data/lib/carray/categorical_iterator.rb +333 -826
  119. data/lib/carray/complex.rb +36 -29
  120. data/lib/carray/conditional.rb +94 -7
  121. data/lib/carray/const_string.rb +46 -0
  122. data/lib/carray/frame/concat.rb +157 -6
  123. data/lib/carray/frame/convert.rb +13 -7
  124. data/lib/carray/frame/csv_parser.rb +589 -82
  125. data/lib/carray/frame/describe.rb +79 -0
  126. data/lib/carray/frame/frame.rb +124 -25
  127. data/lib/carray/frame/group.rb +189 -63
  128. data/lib/carray/frame/io.rb +412 -72
  129. data/lib/carray/frame/join.rb +134 -46
  130. data/lib/carray/frame/records.rb +37 -16
  131. data/lib/carray/frame/reshape.rb +448 -0
  132. data/lib/carray/frame/sort.rb +26 -2
  133. data/lib/carray/frame/verbs.rb +715 -48
  134. data/lib/carray/frame.rb +2 -0
  135. data/lib/carray/fuse_source.rb +111 -2
  136. data/lib/carray/fusion.rb +229 -38
  137. data/lib/carray/inspect.rb +29 -11
  138. data/lib/carray/iterator.rb +14 -2
  139. data/lib/carray/lazy.rb +54 -30
  140. data/lib/carray/mask_gap_fill.rb +66 -52
  141. data/lib/carray/math.rb +32 -19
  142. data/lib/carray/meld_reduce.rb +40 -7
  143. data/lib/carray/methods/align_addr.rb +8 -4
  144. data/lib/carray/methods/bincount.rb +2 -2
  145. data/lib/carray/methods/bit_string.rb +14 -3
  146. data/lib/carray/methods/composition.rb +16 -10
  147. data/lib/carray/methods/factorize.rb +2 -2
  148. data/lib/carray/methods/gather_nd.rb +13 -2
  149. data/lib/carray/methods/insert_block.rb +9 -14
  150. data/lib/carray/methods/is_in.rb +117 -4
  151. data/lib/carray/methods/join.rb +1 -1
  152. data/lib/carray/methods/locate_addr.rb +177 -36
  153. data/lib/carray/methods/mode.rb +18 -1
  154. data/lib/carray/methods/repeat.rb +1 -20
  155. data/lib/carray/methods/resize.rb +21 -23
  156. data/lib/carray/methods/snap.rb +34 -12
  157. data/lib/carray/methods/unique.rb +11 -2
  158. data/lib/carray/record_order.rb +208 -0
  159. data/lib/carray/runtime.rb +14 -11
  160. data/lib/carray/segment_iterator.rb +990 -0
  161. data/lib/carray/serialize.rb +197 -27
  162. data/lib/carray/slab_iterator.rb +51 -27
  163. data/lib/carray/stack.rb +10 -4
  164. data/lib/carray/string_operation_extension.rb +4 -3
  165. data/lib/carray/struct.rb +142 -51
  166. data/lib/carray/struct_builder.rb +110 -14
  167. data/lib/carray/table.rb +2 -2
  168. data/lib/carray/time.rb +517 -103
  169. data/lib/carray/window_iterator.rb +127 -72
  170. data/lib/carray.rb +2 -2
  171. data/yard-stubs/ca_obj_array.rb +4 -4
  172. data/yard-stubs/ca_obj_bitarray.rb +7 -0
  173. data/yard-stubs/ca_obj_bitfield.rb +23 -12
  174. data/yard-stubs/ca_obj_fake.rb +1 -1
  175. data/yard-stubs/ca_obj_field.rb +1 -1
  176. data/yard-stubs/ca_obj_refer.rb +3 -3
  177. data/yard-stubs/ca_obj_roll.rb +0 -1
  178. data/yard-stubs/ca_obj_stride.rb +10 -7
  179. data/yard-stubs/ca_obj_transpose.rb +1 -1
  180. data/yard-stubs/ca_obj_window.rb +1 -2
  181. data/yard-stubs/carray_access.rb +5 -4
  182. data/yard-stubs/carray_broadcast.rb +1 -1
  183. data/yard-stubs/carray_cast.rb +78 -43
  184. data/yard-stubs/carray_conversion.rb +13 -1
  185. data/yard-stubs/carray_copy.rb +8 -3
  186. data/yard-stubs/carray_core.rb +3 -3
  187. data/yard-stubs/carray_count.rb +9 -0
  188. data/yard-stubs/carray_lazy.rb +5 -1
  189. data/yard-stubs/carray_loop.rb +4 -1
  190. data/yard-stubs/carray_mask.rb +34 -10
  191. data/yard-stubs/carray_math.rb +25 -13
  192. data/yard-stubs/carray_median_percentile.rb +10 -6
  193. data/yard-stubs/carray_order.rb +34 -13
  194. data/yard-stubs/carray_scatter.rb +10 -3
  195. data/yard-stubs/carray_segment.rb +72 -0
  196. data/yard-stubs/carray_slab.rb +2 -1
  197. data/yard-stubs/carray_sort.rb +43 -3
  198. data/yard-stubs/carray_test.rb +10 -0
  199. data/yard-stubs/carray_utils.rb +0 -26
  200. metadata +11 -19
  201. data/ext/ca_for_each_element.h +0 -239
  202. data/ext/carray_build_flags.h +0 -3
  203. data/ext/carray_config.h +0 -28
  204. data/ext/carray_kernels_bincmp.c +0 -4446
  205. data/ext/carray_kernels_binop.c +0 -11001
  206. data/ext/carray_kernels_init.c +0 -1131
  207. data/ext/carray_kernels_map.c +0 -3467
  208. data/ext/carray_kernels_moncmp.c +0 -2097
  209. data/ext/carray_kernels_monop.c +0 -18313
  210. data/ext/carray_kernels_reduce_aggregate.c +0 -26005
  211. data/ext/carray_kernels_reduce_boolean.c +0 -330
  212. data/ext/carray_kernels_reduce_cumulative.c +0 -14862
  213. data/ext/carray_kernels_reduce_extreme.c +0 -17494
  214. data/ext/carray_kernels_reduce_variance.c +0 -3910
  215. data/ext/carray_kernels_scan.c +0 -3693
  216. data/ext/carray_kernels_search.c +0 -32218
  217. data/ext/carray_kernels_sort.c +0 -10626
  218. 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
@@ -558,6 +1211,33 @@ typedef struct {
558
1211
  boolean8_t *scratch;
559
1212
  } ca_to_type_ctx_t;
560
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
+
561
1241
  /* The cast proper, with `ca` attached. It can raise (a value the target
562
1242
  type cannot hold), so it runs under rb_ensure. */
563
1243
  static VALUE
@@ -565,11 +1245,16 @@ ca_to_type_cast (VALUE arg)
565
1245
  {
566
1246
  ca_to_type_ctx_t *c = (ca_to_type_ctx_t *) arg;
567
1247
  CArray *ca = c->ca, *cb = c->cb;
568
-
569
- if ( ca->data_type == CA_OBJECT
570
- && ( (cb->data_type >= CA_INT8 && cb->data_type <= CA_UINT64)
571
- || cb->data_type == CA_FLOAT32 || cb->data_type == CA_FLOAT64 ) ) {
572
- /* object -> int/float: give the cast a mask buffer so an unparseable
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
573
1258
  cell becomes UNDEF (see ext/carray_cast_func.rb). Cast into a
574
1259
  scratch mask seeded from the source mask, then attach it to the
575
1260
  output only if some cell ended up masked -- an all-valid cast keeps
@@ -585,6 +1270,13 @@ ca_to_type_cast (VALUE arg)
585
1270
  else {
586
1271
  memset(scratch, 0, ne);
587
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
+ }
588
1280
  ca_cast_block_with_mask(ne, ca, ca->ptr, cb, cb->ptr, scratch);
589
1281
  for (i=0; i<ne; i++) {
590
1282
  if ( scratch[i] ) {
@@ -627,8 +1319,20 @@ ca_to_type_release (VALUE arg)
627
1319
  data_type (a new entity owning its storage). User doc lives in
628
1320
  yard-stubs/carray_cast.rb. */
629
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. */
630
1326
  static VALUE
631
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)
632
1336
  {
633
1337
  volatile VALUE obj, rtype = Qnil, ropt, rbytes = Qnil;
634
1338
  CArray *ca, *cb;
@@ -657,8 +1361,8 @@ rb_ca_to_type_internal (int argc, VALUE *argv, VALUE self)
657
1361
  Numeric Face is not routed here -- its surface *is* its storage, so the
658
1362
  ordinary cast below is already right. */
659
1363
  if ( ca_is_face(ca) && ca->data_type == CA_FIXLEN ) {
660
- return rb_ca_to_type_internal(argc, argv,
661
- rb_ca_face_numeric_projection(self, ca));
1364
+ return rb_ca_to_type_convert(argc, argv,
1365
+ rb_ca_face_numeric_projection(self, ca));
662
1366
  }
663
1367
 
664
1368
  ca_update_mask(ca);
@@ -676,7 +1380,7 @@ rb_ca_to_type_internal (int argc, VALUE *argv, VALUE self)
676
1380
  ctx.ca = ca;
677
1381
  ctx.cb = cb;
678
1382
  ctx.scratch = NULL;
679
- ca_attach(ca);
1383
+ ca_attach(ca); /* window: released by ca_to_type_release under rb_ensure */
680
1384
  rb_ensure(ca_to_type_cast, (VALUE) &ctx, ca_to_type_release, (VALUE) &ctx);
681
1385
 
682
1386
  /* When rtype is a data_class (e.g. CAStruct subclass), wrap the
@@ -1081,30 +1785,30 @@ rb_ca_ptr2obj (VALUE self, void *ptr)
1081
1785
  }
1082
1786
 
1083
1787
  /* Surface -> storage-domain conversion for a Face store (mirror of the
1084
- 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
1085
1789
  the non-Face store path is untouched. For a Face, the per-obj_type C
1086
1790
  table is consulted first (fast path); if none is registered, the Ruby
1087
- `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
1088
1792
  `obj` returns a storage-domain value (e.g. an Integer in its unit); a bare
1089
1793
  Integer / String falls through unchanged to the storage cast. */
1090
1794
  VALUE
1091
- ca_face_scalar_to_storage (VALUE self, CArray *ca, VALUE obj)
1795
+ ca_face_element_to_storage (VALUE self, CArray *ca, VALUE obj)
1092
1796
  {
1093
- ca_face_scalar_to_storage_fn fn;
1797
+ ca_face_element_to_storage_fn fn;
1094
1798
  if ( ! ca_face_safe_check(ca) || obj == CA_UNDEF ) {
1095
1799
  return obj;
1096
1800
  }
1097
- fn = ca_face_scalar_to_storage_table[ca->obj_type];
1801
+ fn = ca_face_element_to_storage_table[ca->obj_type];
1098
1802
  if ( fn != NULL ) {
1099
1803
  return fn(self, obj);
1100
1804
  }
1101
1805
  else {
1102
- static ID id_scalar_to_storage = 0;
1103
- if ( id_scalar_to_storage == 0 ) {
1104
- 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");
1105
1809
  }
1106
- if ( rb_respond_to(self, id_scalar_to_storage) ) {
1107
- 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);
1108
1812
  }
1109
1813
  }
1110
1814
  return obj;
@@ -1127,7 +1831,7 @@ rb_ca_obj2ptr (VALUE self, VALUE obj, void *ptr)
1127
1831
  if ( ca_face_safe_check(ca) ) {
1128
1832
  /* Face write hook: turn a surface value object (Scalar / Time /
1129
1833
  DateTime) into a storage-domain value the cast below consumes. */
1130
- obj = ca_face_scalar_to_storage(self, ca, obj);
1834
+ obj = ca_face_element_to_storage(self, ca, obj);
1131
1835
  convert_type = ca_storage_type_of(ca);
1132
1836
  }
1133
1837
  if ( convert_type == CA_FIXLEN ) {
@@ -1149,7 +1853,7 @@ rb_ca_wrap_writable (VALUE arg, VALUE rtype)
1149
1853
  if ( rb_obj_is_carray(obj) ) { /* obj == carray */
1150
1854
  TypedData_Get_Struct(obj, CArray, &carray_data_type, ca);
1151
1855
  if ( ca_is_readonly(ca) ) {
1152
- rb_raise(rb_eRuntimeError, "can't modify read-only carray");
1856
+ rb_raise(rb_eRuntimeError, "can not modify read-only array");
1153
1857
  }
1154
1858
  if ( NIL_P(rtype) ) {
1155
1859
  data_type = ca->data_type;
@@ -1187,7 +1891,7 @@ rb_ca_wrap_writable (VALUE arg, VALUE rtype)
1187
1891
  }
1188
1892
  TypedData_Get_Struct(obj, CArray, &carray_data_type, ca);
1189
1893
  if ( ca_is_readonly(ca) ) {
1190
- rb_raise(rb_eRuntimeError, "can't modify read-only carray");
1894
+ rb_raise(rb_eRuntimeError, "can not modify read-only array");
1191
1895
  }
1192
1896
  if ( NIL_P(rtype) ) {
1193
1897
  data_type = ca->data_type;
@@ -1220,7 +1924,7 @@ rb_ca_wrap_writable (VALUE arg, VALUE rtype)
1220
1924
  }
1221
1925
  else {
1222
1926
  volatile VALUE inspect = rb_inspect(CLASS_OF(obj));
1223
- rb_raise(rb_eRuntimeError,
1927
+ rb_raise(rb_eTypeError,
1224
1928
  "given object '%s' can't be wrapped as carray",
1225
1929
  StringValuePtr(inspect));
1226
1930
  }
@@ -1431,6 +2135,62 @@ ca_fixlen_scalar_operand (volatile VALUE *scalar, VALUE reference)
1431
2135
  return 1;
1432
2136
  }
1433
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
+
1434
2194
  /* CArray.cast(value) -- singleton entry delegating to rb_ca_cast, which
1435
2195
  returns a CArray unchanged and coerces a Ruby value to a CScalar / CArray.
1436
2196
  User doc lives in yard-stubs/carray_cast.rb. */
@@ -1441,6 +2201,41 @@ rb_ca_s_cast (VALUE klass, VALUE val)
1441
2201
  return rb_ca_cast(val);
1442
2202
  }
1443
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
+
1444
2239
  void
1445
2240
  rb_ca_cast_self_or_other (volatile VALUE *self, volatile VALUE *other)
1446
2241
  {
@@ -1453,11 +2248,11 @@ rb_ca_cast_self_or_other (volatile VALUE *self, volatile VALUE *other)
1453
2248
  Excludes T_STRING which has its own legacy interpretation. */
1454
2249
  if ( ! rb_obj_is_carray(*self) && TYPE(*self) != T_STRING &&
1455
2250
  rb_memory_view_available_p(*self) ) {
1456
- *self = rb_funcall(rb_cCArray, rb_intern("wrap_memory_view"), 1, *self);
2251
+ *self = ca_operand_from_memory_view(*self);
1457
2252
  }
1458
2253
  if ( ! rb_obj_is_carray(*other) && TYPE(*other) != T_STRING &&
1459
2254
  rb_memory_view_available_p(*other) ) {
1460
- *other = rb_funcall(rb_cCArray, rb_intern("wrap_memory_view"), 1, *other);
2255
+ *other = ca_operand_from_memory_view(*other);
1461
2256
  }
1462
2257
 
1463
2258
  if ( ! rb_obj_is_carray(*self) ) {
@@ -1477,6 +2272,9 @@ rb_ca_cast_self_or_other (volatile VALUE *self, volatile VALUE *other)
1477
2272
  else if ( ca_fixlen_scalar_operand(self, *other) ) {
1478
2273
  /* width taken from the fixlen reference; see the helper above */
1479
2274
  }
2275
+ else if ( ca_integer_scalar_operand(self, *other) ) {
2276
+ /* the array's own integer type; see the helper above */
2277
+ }
1480
2278
  else if ( rb_ca_is_float_type(*other) ) {
1481
2279
  *self = rb_cscalar_new_with_value(CA_FLOAT64, 0, *self);
1482
2280
  }
@@ -1508,6 +2306,9 @@ rb_ca_cast_self_or_other (volatile VALUE *self, volatile VALUE *other)
1508
2306
  else if ( ca_fixlen_scalar_operand(other, *self) ) {
1509
2307
  /* width taken from the fixlen reference; see the helper above */
1510
2308
  }
2309
+ else if ( ca_integer_scalar_operand(other, *self) ) {
2310
+ /* the array's own integer type; see the helper above */
2311
+ }
1511
2312
  else if ( rb_ca_is_float_type(*self) ) {
1512
2313
  *other = rb_cscalar_new_with_value(CA_FLOAT64, 0, *other);
1513
2314
  }
@@ -1577,10 +2378,10 @@ rb_ca_cast_self_or_other (volatile VALUE *self, volatile VALUE *other)
1577
2378
  return;
1578
2379
  }
1579
2380
 
1580
- rb_raise(rb_eRuntimeError,
2381
+ rb_raise(rb_eTypeError,
1581
2382
  "can't coerce carray with data_types of '%s' and '%s'",
1582
- ca_type_name[ca->data_type],
1583
- ca_type_name[cb->data_type]);
2383
+ ca_coerce_operand_name(*self, ca),
2384
+ ca_coerce_operand_name(*other, cb));
1584
2385
  }
1585
2386
 
1586
2387
  /* CArray.cast_self_or_other(self, other) — promote self and other
@@ -1681,33 +2482,129 @@ ca_promote_type (int8_t a, int8_t b)
1681
2482
  /* b should be cast to a */
1682
2483
  return a;
1683
2484
  }
1684
- rb_raise(rb_eRuntimeError,
2485
+ rb_raise(rb_eCADataTypeError,
1685
2486
  "can't promote data_types '%s' and '%s' to a common type",
1686
2487
  ca_type_name[a], ca_type_name[b]);
1687
2488
  }
1688
2489
 
1689
- /* CArray.result_type(*args) -- common data_type each operand promotes to
1690
- 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
1691
2572
  CArray / Symbol / String / Class gives a data_type representation, a
1692
2573
  Numeric / bool / nil / Object gives a value whose data_type is inferred.
1693
- Folds pairwise via ca_promote_type; returns a Symbol. User doc (with
1694
- the value-vs-code distinction and examples) lives in
1695
- 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. */
1696
2578
 
1697
2579
  static VALUE
1698
2580
  rb_ca_s_result_type (int argc, VALUE *argv, VALUE klass)
1699
2581
  {
1700
- int8_t cur;
2582
+ int8_t cur = -1;
1701
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);
1702
2592
 
1703
2593
  if ( argc < 1 ) {
1704
2594
  rb_raise(rb_eArgError, "result_type requires at least one argument");
1705
2595
  }
1706
2596
 
1707
- cur = ca_arg_to_data_type(argv[0]);
1708
- for ( i = 1; i < argc; i++ ) {
1709
- int8_t next = ca_arg_to_data_type(argv[i]);
1710
- 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
+ }
1711
2608
  }
1712
2609
  /* Return Symbol for family consistency with
1713
2610
  CArray.value_to_data_type and ca.data_type. */
@@ -1747,9 +2644,6 @@ rb_ca_s_promote_list (int argc, VALUE *argv, VALUE klass)
1747
2644
  {
1748
2645
  VALUE list, kwargs, data_type = Qnil;
1749
2646
  long n, i;
1750
- int all_face = 1, any_face = 0, all_primitive = 1;
1751
- VALUE face_class = Qnil;
1752
- CArray *ref_face = NULL;
1753
2647
  int8_t common = -1;
1754
2648
 
1755
2649
  rb_scan_args(argc, argv, "1:", &list, &kwargs);
@@ -1768,49 +2662,26 @@ rb_ca_s_promote_list (int argc, VALUE *argv, VALUE klass)
1768
2662
  if ( RB_TYPE_P(data_type, T_CLASS) || RB_TYPE_P(data_type, T_MODULE) ) {
1769
2663
  rb_raise(rb_eArgError,
1770
2664
  "promote_list: data_type must be a primitive Symbol "
1771
- "(Class targets are not supported; use auto-detect or "
1772
- "strip Face manually)");
1773
- }
1774
- }
1775
-
1776
- /* Classify each element: Face / primitive CArray. Detect Face-class
1777
- homogeneity as we go. */
1778
- for ( i = 0; i < n; i++ ) {
1779
- VALUE elem = rb_ary_entry(list, i);
1780
- CArray *ca;
1781
- rb_check_carray_object(elem);
1782
- TypedData_Get_Struct(elem, CArray, &carray_data_type, ca);
1783
- if ( ca_is_face(ca) ) {
1784
- any_face = 1;
1785
- all_primitive = 0;
1786
- if ( NIL_P(face_class) ) {
1787
- face_class = rb_obj_class(elem);
1788
- ref_face = ca;
1789
- } else if ( rb_obj_class(elem) != face_class ) {
1790
- all_face = 0; /* will be caught after the loop */
1791
- } else if ( all_face ) {
1792
- if ( ! ca_face_state_compatible(rb_ary_entry(list, 0), ref_face,
1793
- elem, ca) ) {
1794
- rb_raise(rb_eArgError,
1795
- "promote_list: Face state mismatch across elements "
1796
- "(= %s instance at index %ld differs from index 0)",
1797
- rb_class2name(face_class), i);
1798
- }
1799
- }
1800
- } else {
1801
- all_face = 0;
2665
+ "(Class targets are not supported; leave data_type out to "
2666
+ "keep a Face)");
1802
2667
  }
1803
2668
  }
1804
2669
 
1805
2670
  /* Explicit data_type path: primitive Symbol, all elements must be
1806
2671
  primitive. Reject if any Face in list. */
1807
2672
  if ( ! NIL_P(data_type) ) {
1808
- if ( any_face ) {
1809
- rb_raise(rb_eArgError,
1810
- "promote_list: data_type %s cannot be applied to a list "
1811
- "containing Face elements (= Face cannot be coerced to "
1812
- "a primitive without losing identity)",
1813
- 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
+ }
1814
2685
  }
1815
2686
  {
1816
2687
  VALUE out = rb_ary_new_capa(n);
@@ -1825,41 +2696,24 @@ rb_ca_s_promote_list (int argc, VALUE *argv, VALUE klass)
1825
2696
  }
1826
2697
  }
1827
2698
 
1828
- /* Auto-detect path. */
1829
- if ( any_face && ! all_face ) {
1830
- if ( all_primitive == 0 && any_face ) {
1831
- /* Mixed: at least one Face and at least one non-Face,
1832
- OR mixed Face classes. Both are rejected. */
1833
- rb_raise(rb_eArgError,
1834
- "promote_list: cannot mix Face and non-Face (or heterogeneous "
1835
- "Face classes); pass a homogeneous Face list, or strip Face "
1836
- "manually with .parent for the storage-level layout");
1837
- }
1838
- }
1839
-
1840
- if ( all_face ) {
1841
- /* Homogeneous Face: validate portability, then pass through. */
1842
- if ( n > 1 && ! ca_face_state_portable(ref_face->obj_type, face_class) ) {
1843
- rb_raise(rb_eArgError,
1844
- "promote_list: %s state is not portable across multiple "
1845
- "elements (= per-parent storage like CAConstString's buffer); "
1846
- "strip Face manually with .parent for a storage-level list",
1847
- rb_class2name(face_class));
1848
- }
1849
- /* Return a fresh Array so callers can mutate without aliasing. */
1850
- {
1851
- VALUE out = rb_ary_new_capa(n);
1852
- for ( i = 0; i < n; i++ ) rb_ary_push(out, rb_ary_entry(list, i));
1853
- return out;
1854
- }
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;
1855
2705
  }
1856
2706
 
1857
2707
  /* All primitive: result_type to determine common data_type, then
1858
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. */
1859
2713
  common = ca_arg_to_data_type(rb_ary_entry(list, 0));
1860
2714
  for ( i = 1; i < n; i++ ) {
1861
2715
  int8_t next = ca_arg_to_data_type(rb_ary_entry(list, i));
1862
- common = ca_promote_type(common, next);
2716
+ common = ca_value_match_type(ca_promote_type(common, next), common, next);
1863
2717
  }
1864
2718
  {
1865
2719
  VALUE common_sym = rb_ca_data_type_to_sym(common);
@@ -1888,7 +2742,7 @@ rb_ca_cast_other (VALUE *self, volatile VALUE *other)
1888
2742
  Excludes T_STRING which has its own legacy interpretation. */
1889
2743
  if ( ! rb_obj_is_carray(*other) && TYPE(*other) != T_STRING &&
1890
2744
  rb_memory_view_available_p(*other) ) {
1891
- *other = rb_funcall(rb_cCArray, rb_intern("wrap_memory_view"), 1, *other);
2745
+ *other = ca_operand_from_memory_view(*other);
1892
2746
  }
1893
2747
 
1894
2748
  if ( ! rb_obj_is_carray(*other) ) {
@@ -1933,10 +2787,10 @@ rb_ca_cast_other (VALUE *self, volatile VALUE *other)
1933
2787
  return;
1934
2788
  }
1935
2789
 
1936
- rb_raise(rb_eRuntimeError,
2790
+ rb_raise(rb_eTypeError,
1937
2791
  "can't coerce carray with data_types of '%s' and '%s'",
1938
- ca_type_name[ca->data_type],
1939
- ca_type_name[cb->data_type]);
2792
+ ca_coerce_operand_name(*self, ca),
2793
+ ca_coerce_operand_name(*other, cb));
1940
2794
  }
1941
2795
 
1942
2796
  /* CArray#cast_with(other) -- coerce self and other to a common
@@ -1951,7 +2805,7 @@ rb_ca_cast_with (VALUE self, VALUE other)
1951
2805
  rb_ca_cast_self_or_other(&self, &other);
1952
2806
  }
1953
2807
  else {
1954
- rb_raise(rb_eRuntimeError, "first argument should be a carray");
2808
+ rb_raise(rb_eTypeError, "first argument should be a carray");
1955
2809
  }
1956
2810
  return rb_assoc_new(self, other);
1957
2811
  }
@@ -2000,7 +2854,7 @@ rb_ca_clip_uint64 (VALUE self)
2000
2854
  volatile VALUE vout = rb_carray_new(CA_UINT64, ca->ndim, ca->dim, 0, NULL);
2001
2855
  CArray *out;
2002
2856
  GetCArray(vout, out);
2003
- ca_attach(src);
2857
+ ca_attach(src); /* window: nothing raises inside */
2004
2858
  double *sp = (double *) src->ptr;
2005
2859
  uint64_t *op = (uint64_t *) out->ptr;
2006
2860
  ca_size_t n = ca->elements;
@@ -2028,16 +2882,19 @@ rb_ca_clip_uint64 (VALUE self)
2028
2882
  Polymorphic cast (NOT a fresh allocator): `data` is coerced into a CArray
2029
2883
  of the target data_type, dispatching on its Ruby class:
2030
2884
 
2031
- Array -> shape-guessed CArray, element-wise store (C fast path)
2032
- CArray -> copy if same data_type, else #to_type (C fast path)
2033
- nil -> empty CArray (C fast path)
2034
- Range / String / Numeric / #to_ca responder (Ruby fallback)
2035
-
2036
- The cold / Ruby-natural branches (arange arithmetic, the whitespace/`,`/`;`
2037
- string parser, generic coercion) stay in Ruby as `CArray.__cast_rest__`
2038
- (carray/basics.rb, eager-loaded). `type` is passed to the fallback as the
2039
- data_type Symbol so the Ruby side keeps its original `type == CA_OBJECT`
2040
- 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.
2041
2898
  ------------------------------------------------------------------------ */
2042
2899
 
2043
2900
  static ID id_new_cast = 0, id_guess_array_shape_cast = 0, id_cast_string = 0,
@@ -2063,6 +2920,21 @@ ca_cast_impl (int8_t data_type, int argc, VALUE *argv)
2063
2920
  VALUE v = ( argc >= 1 ) ? argv[0] : Qnil;
2064
2921
  VALUE sym = ID2SYM(ca_data_type_sym[data_type]);
2065
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
+
2066
2938
  if ( TYPE(v) == T_ARRAY ) {
2067
2939
  volatile VALUE shape, obj;
2068
2940
  shape = rb_funcall(rb_cCArray, id_guess_array_shape_cast, 1, v);
@@ -2083,8 +2955,9 @@ ca_cast_impl (int8_t data_type, int argc, VALUE *argv)
2083
2955
  return rb_carray_new(data_type, 1, &dim0, 0, NULL);
2084
2956
  }
2085
2957
  else if ( RTEST(rb_obj_is_kind_of(v, rb_cRange)) ) {
2086
- /* arange: n = ((end - begin).abs / step).floor (+1 unless exclude_end),
2087
- 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). */
2088
2961
  volatile VALUE vbeg, vend, step, n;
2089
2962
  int sign;
2090
2963
  ca_size_t dn;
@@ -2094,7 +2967,7 @@ ca_cast_impl (int8_t data_type, int argc, VALUE *argv)
2094
2967
  vend = rb_funcall(v, id_end_cast, 0);
2095
2968
  step = ( argc >= 2 ) ? argv[1] : Qnil;
2096
2969
  if ( RTEST(step) && RTEST(rb_funcall(step, id_op_eq, 1, INT2FIX(0))) ) {
2097
- rb_raise(rb_eRuntimeError, "step should not be 0");
2970
+ rb_raise(rb_eArgError, "step should not be 0");
2098
2971
  }
2099
2972
  if ( data_type == CA_OBJECT && !RTEST(step) ) {
2100
2973
  VALUE va = rb_funcall(v, id_to_a_cast, 0); /* CA_OBJECT(v.to_a) */
@@ -2103,9 +2976,13 @@ ca_cast_impl (int8_t data_type, int argc, VALUE *argv)
2103
2976
  if ( !RTEST(step) ) step = INT2FIX(1);
2104
2977
  n = rb_funcall(vend, id_op_minus, 1, vbeg);
2105
2978
  n = rb_funcall(n, id_abs_cast, 0);
2106
- n = rb_funcall(n, id_op_div, 1, step);
2107
- n = rb_funcall(n, id_floor_cast, 0);
2108
- 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);
2109
2986
  n = rb_funcall(n, id_op_plus, 1, INT2FIX(1));
2110
2987
  }
2111
2988
  sign = RTEST(rb_funcall(vbeg, id_op_le, 1, vend)) ? 1 : -1;
@@ -2206,7 +3083,7 @@ rb_ca_cast_fixlen (int argc, VALUE *argv, VALUE self)
2206
3083
  ca_size_t l = NUM2SIZE(rb_funcall(rb_ary_entry(v, i), id_length_cast, 0));
2207
3084
  if ( l > maxlen ) maxlen = l;
2208
3085
  }
2209
- bytes = maxlen;
3086
+ bytes = ( maxlen > 0 ) ? maxlen : 1; /* all empty: one NUL byte */
2210
3087
  } else {
2211
3088
  bytes = NUM2SIZE(bytes_v);
2212
3089
  }
@@ -2219,7 +3096,7 @@ rb_ca_cast_fixlen (int argc, VALUE *argv, VALUE self)
2219
3096
  }
2220
3097
  else if ( NIL_P(v) ) {
2221
3098
  ca_size_t dim0 = 0;
2222
- 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);
2223
3100
  return rb_carray_new(CA_FIXLEN, 1, &dim0, bytes, NULL);
2224
3101
  }
2225
3102
  else {
@@ -2231,17 +3108,373 @@ rb_ca_cast_fixlen (int argc, VALUE *argv, VALUE self)
2231
3108
  ? ca : ca_cast_to_fixlen(ca, fixlen_sym, bytes_v);
2232
3109
  }
2233
3110
  {
2234
- 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
+ }
2235
3121
  return rb_cscalar_new_with_value(CA_FIXLEN, bytes, v);
2236
3122
  }
2237
3123
  }
2238
3124
  }
2239
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
+
2240
3462
  void
2241
3463
  Init_carray_cast (void)
2242
3464
  {
2243
3465
  /* CArray data_type conversion */
2244
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
+
2245
3478
  rb_define_method(rb_cCArray, "to_type", rb_ca_to_type_internal, -1);
2246
3479
 
2247
3480
  rb_define_method(rb_cCArray, "fixlen", rb_ca_to_fixlen, -1);
@@ -2306,6 +3539,8 @@ Init_carray_cast (void)
2306
3539
  "cast_self_or_other", rb_ca_s_cast_self_or_other, 2);
2307
3540
  rb_define_singleton_method(rb_cCArray,
2308
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);
2309
3544
 
2310
3545
  rb_define_singleton_method(rb_cCArray,
2311
3546
  "promote_list", rb_ca_s_promote_list, -1);