carray 3.0.2 → 3.0.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (247) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +568 -0
  3. data/README.md +6 -6
  4. data/carray.gemspec +1 -1
  5. data/ext/ca_axis_descriptor.h +27 -24
  6. data/ext/ca_axis_dispatch.c +220 -0
  7. data/ext/ca_axis_group.c +201 -123
  8. data/ext/ca_binop_dispatch.c +46 -52
  9. data/ext/ca_binop_dispatch.h +1 -1
  10. data/ext/ca_categorical_iterator.c +249 -183
  11. data/ext/ca_compare.c +7 -12
  12. data/ext/ca_for_buffer.h +21 -68
  13. data/ext/ca_group_iter.c +38 -23
  14. data/ext/ca_kernel_iterator.c +1765 -2076
  15. data/ext/ca_kernel_iterator.h +584 -566
  16. data/ext/ca_monop_dispatch.c +86 -63
  17. data/ext/ca_monop_dispatch.h +5 -0
  18. data/ext/ca_obj_array.c +223 -56
  19. data/ext/ca_obj_bincmp.c +172 -138
  20. data/ext/ca_obj_binop.c +116 -230
  21. data/ext/ca_obj_bitarray.c +40 -26
  22. data/ext/ca_obj_bitfield.c +100 -68
  23. data/ext/ca_obj_block.c +19 -9
  24. data/ext/ca_obj_byte_swap.c +45 -46
  25. data/ext/ca_obj_const_string.c +269 -44
  26. data/ext/ca_obj_face.c +612 -89
  27. data/ext/ca_obj_face.h +111 -47
  28. data/ext/ca_obj_fake.c +36 -79
  29. data/ext/ca_obj_farray.c +7 -3
  30. data/ext/ca_obj_field.c +33 -8
  31. data/ext/ca_obj_fixlen_string.c +23 -24
  32. data/ext/ca_obj_grid.c +541 -210
  33. data/ext/ca_obj_meld.c +331 -111
  34. data/ext/ca_obj_moncmp.c +80 -65
  35. data/ext/ca_obj_monop.c +177 -149
  36. data/ext/ca_obj_object.c +664 -248
  37. data/ext/ca_obj_record.c +63 -23
  38. data/ext/ca_obj_reduce.c +18 -16
  39. data/ext/ca_obj_refer.c +140 -58
  40. data/ext/ca_obj_remap.c +85 -24
  41. data/ext/ca_obj_repeat.c +24 -9
  42. data/ext/ca_obj_roll.c +121 -16
  43. data/ext/ca_obj_select.c +61 -58
  44. data/ext/ca_obj_select_axis.c +73 -107
  45. data/ext/ca_obj_shift.c +32 -102
  46. data/ext/ca_obj_stack.c +372 -122
  47. data/ext/ca_obj_stride.c +348 -140
  48. data/ext/ca_obj_string.c +88 -22
  49. data/ext/ca_obj_tile.c +38 -21
  50. data/ext/ca_obj_time.c +45 -31
  51. data/ext/ca_obj_timedelta.c +43 -29
  52. data/ext/ca_obj_transpose.c +17 -6
  53. data/ext/ca_obj_triop.c +125 -141
  54. data/ext/ca_obj_window.c +330 -44
  55. data/ext/ca_op_ipower.c +24 -11
  56. data/ext/ca_op_powi.h +57 -30
  57. data/ext/ca_pow5_table.h +656 -0
  58. data/ext/ca_sort_kernels.h +70 -0
  59. data/ext/ca_sweep_engine.c +104 -81
  60. data/ext/ca_sweep_engine.h +7 -20
  61. data/ext/ca_triop_dispatch.c +4 -1
  62. data/ext/ca_triop_dispatch.h +5 -2
  63. data/ext/caframe_csv_reader.c +542 -0
  64. data/ext/carray.h +241 -92
  65. data/ext/carray_access.c +608 -276
  66. data/ext/carray_address_basis.c +494 -97
  67. data/ext/carray_attribute.c +129 -4
  68. data/ext/carray_bincount.c +39 -7
  69. data/ext/carray_broadcast.c +56 -19
  70. data/ext/carray_build_flags.h +1 -1
  71. data/ext/carray_call_cfunc.c +108 -28
  72. data/ext/carray_cast.c +1576 -223
  73. data/ext/carray_cast_func.rb +77 -24
  74. data/ext/carray_class.c +4 -3
  75. data/ext/carray_conversion.c +208 -75
  76. data/ext/carray_copy.c +82 -24
  77. data/ext/carray_core.c +646 -162
  78. data/ext/carray_count.c +171 -88
  79. data/ext/carray_element.c +94 -60
  80. data/ext/carray_factorize.c +670 -324
  81. data/ext/carray_generate.c +260 -83
  82. data/ext/carray_histogram.c +209 -106
  83. data/ext/carray_hold.c +14 -16
  84. data/ext/carray_index_classifier.c +422 -243
  85. data/ext/carray_internal.h +692 -3
  86. data/ext/carray_kernels_bincmp.c +7 -0
  87. data/ext/carray_kernels_binop.c +487 -120
  88. data/ext/carray_kernels_init.c +653 -50
  89. data/ext/carray_kernels_moncmp.c +15 -8
  90. data/ext/carray_kernels_monop.c +99 -43
  91. data/ext/carray_kernels_reduce_aggregate.c +3925 -6112
  92. data/ext/carray_kernels_reduce_boolean.c +112 -36
  93. data/ext/carray_kernels_reduce_cumulative.c +2127 -6903
  94. data/ext/carray_kernels_reduce_extreme.c +3032 -2892
  95. data/ext/carray_kernels_reduce_variance.c +439 -500
  96. data/ext/carray_kernels_scan.c +1097 -488
  97. data/ext/carray_kernels_search.c +3787 -2176
  98. data/ext/carray_kernels_sort.c +1200 -920
  99. data/ext/carray_kernels_triop.c +479 -7
  100. data/ext/carray_key_windows.c +216 -0
  101. data/ext/carray_lazy.c +967 -153
  102. data/ext/carray_loop.c +81 -65
  103. data/ext/carray_mask.c +910 -324
  104. data/ext/carray_math_kernel.h +1 -0
  105. data/ext/carray_median_percentile.c +299 -100
  106. data/ext/carray_memory_view.c +872 -758
  107. data/ext/carray_operator.c +1171 -364
  108. data/ext/carray_order.c +505 -133
  109. data/ext/carray_partition.c +112 -65
  110. data/ext/carray_random.c +327 -239
  111. data/ext/carray_round.c +273 -0
  112. data/ext/carray_scatter.c +346 -78
  113. data/ext/carray_segment.c +257 -0
  114. data/ext/carray_slab.c +249 -86
  115. data/ext/carray_sort.c +224 -227
  116. data/ext/carray_sort_kernel.c +8 -1
  117. data/ext/carray_struct.c +118 -39
  118. data/ext/carray_test.c +193 -69
  119. data/ext/carray_time_text.c +1182 -0
  120. data/ext/carray_undef.c +4 -0
  121. data/ext/carray_utils.c +579 -113
  122. data/ext/extconf.rb +27 -2
  123. data/ext/mk_call_cfunc.rb +19 -4
  124. data/ext/mk_pow5_table.rb +35 -0
  125. data/ext/mkkernel.rb +1202 -1014
  126. data/ext/ruby_carray.c +17 -5
  127. data/ext/version.h +4 -4
  128. data/ext/version.rb +4 -2
  129. data/lib/carray/attribute.rb +160 -17
  130. data/lib/carray/autoload_carray.rb +23 -27
  131. data/lib/carray/autoload_method_extension.rb +20 -5
  132. data/lib/carray/axis_group.rb +360 -63
  133. data/lib/carray/basics.rb +162 -28
  134. data/lib/carray/bincount_nd.rb +18 -2
  135. data/lib/carray/block_iterator.rb +231 -58
  136. data/lib/carray/boolean_reduce.rb +34 -42
  137. data/lib/carray/categorical.rb +242 -52
  138. data/lib/carray/categorical_iterator.rb +437 -827
  139. data/lib/carray/complex.rb +41 -34
  140. data/lib/carray/conditional.rb +150 -14
  141. data/lib/carray/const_string.rb +59 -4
  142. data/lib/carray/construct.rb +15 -11
  143. data/lib/carray/data_type_extension.rb +1 -0
  144. data/lib/carray/distance_limit.rb +77 -0
  145. data/lib/carray/fixlen_string.rb +7 -1
  146. data/lib/carray/frame/column_verbs.rb +70 -0
  147. data/lib/carray/frame/concat.rb +157 -6
  148. data/lib/carray/frame/convert.rb +82 -7
  149. data/lib/carray/frame/csv_parser.rb +688 -87
  150. data/lib/carray/frame/describe.rb +80 -0
  151. data/lib/carray/frame/frame.rb +369 -45
  152. data/lib/carray/frame/group.rb +631 -90
  153. data/lib/carray/frame/io.rb +506 -79
  154. data/lib/carray/frame/join.rb +411 -62
  155. data/lib/carray/frame/records.rb +116 -19
  156. data/lib/carray/frame/reshape.rb +455 -0
  157. data/lib/carray/frame/sort.rb +30 -18
  158. data/lib/carray/frame/verbs.rb +868 -61
  159. data/lib/carray/frame/windows.rb +117 -0
  160. data/lib/carray/frame.rb +4 -0
  161. data/lib/carray/fuse_source.rb +118 -2
  162. data/lib/carray/fusion.rb +257 -42
  163. data/lib/carray/histogram.rb +6 -1
  164. data/lib/carray/inspect.rb +54 -13
  165. data/lib/carray/iterator.rb +99 -9
  166. data/lib/carray/key_windows.rb +620 -0
  167. data/lib/carray/lazy.rb +216 -193
  168. data/lib/carray/mask_gap_fill.rb +112 -92
  169. data/lib/carray/math.rb +32 -19
  170. data/lib/carray/meld_reduce.rb +52 -21
  171. data/lib/carray/methods/align_addr.rb +8 -4
  172. data/lib/carray/methods/bin.rb +26 -18
  173. data/lib/carray/methods/bincount.rb +18 -2
  174. data/lib/carray/methods/bit_string.rb +15 -4
  175. data/lib/carray/methods/choose.rb +16 -3
  176. data/lib/carray/methods/composition.rb +18 -10
  177. data/lib/carray/methods/factorize.rb +2 -2
  178. data/lib/carray/methods/gather_nd.rb +25 -5
  179. data/lib/carray/methods/insert_block.rb +9 -14
  180. data/lib/carray/methods/is_between.rb +59 -0
  181. data/lib/carray/methods/is_in.rb +140 -10
  182. data/lib/carray/methods/join.rb +2 -7
  183. data/lib/carray/methods/lag.rb +49 -0
  184. data/lib/carray/methods/locate_addr.rb +182 -45
  185. data/lib/carray/methods/mode.rb +18 -1
  186. data/lib/carray/methods/nunique.rb +11 -6
  187. data/lib/carray/methods/repeat.rb +1 -20
  188. data/lib/carray/methods/resize.rb +21 -23
  189. data/lib/carray/methods/snap.rb +34 -12
  190. data/lib/carray/methods/unique.rb +11 -2
  191. data/lib/carray/methods/value_counts.rb +4 -11
  192. data/lib/carray/record_order.rb +208 -0
  193. data/lib/carray/rng.rb +3 -0
  194. data/lib/carray/runtime.rb +65 -17
  195. data/lib/carray/segment_iterator.rb +1221 -0
  196. data/lib/carray/serialize.rb +214 -30
  197. data/lib/carray/slab_iterator.rb +101 -27
  198. data/lib/carray/stack.rb +10 -4
  199. data/lib/carray/string.rb +105 -5
  200. data/lib/carray/string_operation_extension.rb +12 -4
  201. data/lib/carray/struct.rb +137 -58
  202. data/lib/carray/struct_builder.rb +124 -17
  203. data/lib/carray/time.rb +1222 -186
  204. data/lib/carray/window_iterator.rb +242 -81
  205. data/lib/carray.rb +9 -2
  206. data/yard-stubs/ca_obj_array.rb +4 -4
  207. data/yard-stubs/ca_obj_bitarray.rb +7 -0
  208. data/yard-stubs/ca_obj_bitfield.rb +23 -12
  209. data/yard-stubs/ca_obj_fake.rb +1 -1
  210. data/yard-stubs/ca_obj_field.rb +5 -1
  211. data/yard-stubs/ca_obj_grid.rb +21 -10
  212. data/yard-stubs/ca_obj_refer.rb +5 -4
  213. data/yard-stubs/ca_obj_roll.rb +2 -3
  214. data/yard-stubs/ca_obj_shift.rb +5 -1
  215. data/yard-stubs/ca_obj_stride.rb +10 -7
  216. data/yard-stubs/ca_obj_tile.rb +1 -1
  217. data/yard-stubs/ca_obj_transpose.rb +1 -1
  218. data/yard-stubs/ca_obj_window.rb +9 -5
  219. data/yard-stubs/carray_access.rb +11 -5
  220. data/yard-stubs/carray_attribute.rb +0 -5
  221. data/yard-stubs/carray_broadcast.rb +1 -1
  222. data/yard-stubs/carray_cast.rb +88 -43
  223. data/yard-stubs/carray_conversion.rb +15 -2
  224. data/yard-stubs/carray_copy.rb +12 -5
  225. data/yard-stubs/carray_core.rb +3 -3
  226. data/yard-stubs/carray_count.rb +9 -0
  227. data/yard-stubs/carray_element.rb +10 -4
  228. data/yard-stubs/carray_generate.rb +21 -1
  229. data/yard-stubs/carray_lazy.rb +5 -1
  230. data/yard-stubs/carray_loop.rb +4 -1
  231. data/yard-stubs/carray_mask.rb +60 -12
  232. data/yard-stubs/carray_math.rb +52 -14
  233. data/yard-stubs/carray_median_percentile.rb +15 -8
  234. data/yard-stubs/carray_memory_view.rb +40 -23
  235. data/yard-stubs/carray_order.rb +61 -29
  236. data/yard-stubs/carray_random.rb +13 -4
  237. data/yard-stubs/carray_scatter.rb +21 -9
  238. data/yard-stubs/carray_segment.rb +72 -0
  239. data/yard-stubs/carray_slab.rb +10 -2
  240. data/yard-stubs/carray_sort.rb +49 -8
  241. data/yard-stubs/carray_test.rb +15 -3
  242. data/yard-stubs/carray_utils.rb +0 -26
  243. metadata +19 -5
  244. data/ext/ca_for_each_element.h +0 -239
  245. data/ext/carray_kernels_map.c +0 -3467
  246. data/lib/carray/arrow_tensor.rb +0 -401
  247. data/lib/carray/table.rb +0 -108
data/ext/carray_cast.c CHANGED
@@ -34,11 +34,574 @@
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
+ /* protect: a BigDecimal that is not finite reads as no integer; nothing is held */
500
+ VALUE whole = rb_protect(ca_decimal_truncate, v, &state);
501
+ if ( state ) {
502
+ rb_set_errinfo(Qnil);
503
+ return 0;
504
+ }
505
+ if ( ! RB_INTEGER_TYPE_P(whole) || ! RTEST(rb_equal(whole, v)) ) {
506
+ return 0;
507
+ }
508
+ v = whole;
509
+ }
510
+ if ( RB_INTEGER_TYPE_P(v) ) {
511
+ VALUE zero = INT2FIX(0);
512
+ *negative = RTEST(rb_funcall(v, '<', 1, zero));
513
+ if ( *negative ) {
514
+ v = rb_funcall(v, rb_intern("-@"), 0);
515
+ }
516
+ if ( RTEST(rb_funcall(v, '>', 1, ULL2NUM(UINT64_MAX))) ) {
517
+ return 0;
518
+ }
519
+ *magnitude = NUM2ULL(v);
520
+ return 1;
521
+ }
522
+ if ( RB_FLOAT_TYPE_P(v) ) {
523
+ double d = RFLOAT_VALUE(v);
524
+ if ( d != d || d != floor(d) || fabs(d) >= 18446744073709551616.0 ) {
525
+ return 0;
526
+ }
527
+ *negative = ( d < 0 );
528
+ *magnitude = (uint64_t) fabs(d);
529
+ return 1;
530
+ }
531
+ return 0;
532
+ }
533
+
534
+ /* A String as one value of data_type (an integer or float type), written
535
+ to out. */
536
+ static int
537
+ ca_decimal_read_bytes_as (const char *s, long n, int8_t data_type, void *out)
538
+ {
539
+ ca_decimal_trim(&s, &n);
540
+ if ( n == 0 ) {
541
+ return CA_DECIMAL_BLANK;
542
+ }
543
+ if ( data_type == CA_FLOAT32 || data_type == CA_FLOAT64 ) {
544
+ double d;
545
+ if ( ! ca_decimal_float(s, n, &d) ) {
546
+ return CA_DECIMAL_UNREADABLE;
547
+ }
548
+ if ( data_type == CA_FLOAT32 ) {
549
+ *(float32_t *) out = (float32_t) d;
550
+ }
551
+ else {
552
+ *(float64_t *) out = d;
553
+ }
554
+ return CA_DECIMAL_OK;
555
+ }
556
+ else {
557
+ int negative = 0;
558
+ uint64_t magnitude = 0;
559
+ int rc = ca_decimal_integer(s, n, &negative, &magnitude);
560
+ if ( rc != CA_DECIMAL_OK ) {
561
+ return rc;
562
+ }
563
+ if ( ! ca_decimal_fits(data_type, negative, magnitude) ) {
564
+ return CA_DECIMAL_OUT_OF_RANGE;
565
+ }
566
+ ca_decimal_store_integer(data_type, (char *) out, 0, negative, magnitude);
567
+ return CA_DECIMAL_OK;
568
+ }
569
+ }
570
+
571
+ static int
572
+ ca_decimal_string_read (VALUE str, int8_t data_type, void *out)
573
+ {
574
+ return ca_decimal_read_bytes_as(RSTRING_PTR(str), RSTRING_LEN(str), data_type, out);
575
+ }
576
+
577
+ /* The to_type reader: 1 with the value at out, or 0 for a cell that
578
+ becomes UNDEF. */
579
+ int
580
+ ca_decimal_string_to (VALUE str, int8_t data_type, void *out)
581
+ {
582
+ return ca_decimal_string_read(str, data_type, out) == CA_DECIMAL_OK;
583
+ }
584
+
585
+ /* The store reader: the value at out, or a raise. */
586
+ void
587
+ ca_decimal_string_store (VALUE str, int8_t data_type, void *out)
588
+ {
589
+ switch ( ca_decimal_string_read(str, data_type, out) ) {
590
+ case CA_DECIMAL_OK:
591
+ return;
592
+ case CA_DECIMAL_OUT_OF_RANGE:
593
+ rb_raise(rb_eRangeError, "%s: %+"PRIsVALUE" is out of range for %s",
594
+ ca_calling_method_name(), str, ca_type_name[data_type]);
595
+ default:
596
+ rb_raise(rb_eArgError, "%s: %+"PRIsVALUE" cannot be read as %s",
597
+ ca_calling_method_name(), str, ca_type_name[data_type]);
598
+ }
599
+ }
37
600
 
38
601
  boolean8_t
39
602
  OBJ2BOOL (VALUE v)
40
603
  {
41
- if ( v == Qfalse || v == Qnil ) {
604
+ if ( v == Qfalse ) {
42
605
  return 0;
43
606
  }
44
607
  else if ( v == Qtrue ) {
@@ -99,25 +662,20 @@ OBJ2DBL (VALUE val)
99
662
  switch ( TYPE(val) ) {
100
663
  case T_FLOAT:
101
664
  return NUM2DBL(val);
102
- case T_NIL:
103
- return 0.0/0.0;
104
665
  case T_STRING: {
105
666
  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);
667
+ ca_decimal_string_store(val, CA_FLOAT64, &d);
668
+ return d;
112
669
  }
113
670
  default:
114
671
  return NUM2DBL(rb_Float(val));
115
672
  }
116
673
  }
117
674
 
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. */
675
+ /* rb_protect bodies: Kernel#Float / Kernel#Integer applied to one value
676
+ that is not a String. Used by the mask-aware object->numeric parsers
677
+ below so a conversion error is caught (and turned into a masked cell)
678
+ rather than raised. */
121
679
  static VALUE
122
680
  ca_kernel_float (VALUE v)
123
681
  {
@@ -130,12 +688,71 @@ ca_kernel_integer (VALUE v)
130
688
  return rb_Integer(v);
131
689
  }
132
690
 
691
+ /* Complex#to_s writes a part that is not finite as "NaN" or "Infinity" and
692
+ then marks the imaginary part with "*i" ("1.0+Infinity*i", "NaN+0i"),
693
+ which Complex() does not read back. Read that form as two decimal
694
+ numbers: the imaginary part starts at the last sign that is not the
695
+ first character and not an exponent's. Returns 0 when the text is not
696
+ of that form. */
697
+ static int
698
+ ca_str_to_cmplx_nonfinite (VALUE str, double complex *out)
699
+ {
700
+ const char *s = RSTRING_PTR(str);
701
+ long n = RSTRING_LEN(str), k;
702
+ double re, im;
703
+ volatile VALUE vre, vim;
704
+ while ( n > 0 && ( s[0] == ' ' || s[0] == '\t' ) ) { s++; n--; }
705
+ while ( n > 0 && ( s[n-1] == ' ' || s[n-1] == '\t' ) ) { n--; }
706
+ if ( n < 2 || s[n-1] != 'i' ) {
707
+ return 0;
708
+ }
709
+ n -= ( n >= 2 && s[n-2] == '*' ) ? 2 : 1;
710
+ for ( k = n - 1; k > 0; k-- ) {
711
+ if ( ( s[k] == '+' || s[k] == '-' ) && s[k-1] != 'e' && s[k-1] != 'E' ) {
712
+ break;
713
+ }
714
+ }
715
+ if ( k <= 0 ) {
716
+ return 0;
717
+ }
718
+ vre = rb_str_new(s, k);
719
+ vim = rb_str_new(s + k, n - k);
720
+ if ( ! ca_decimal_string_to(vre, CA_FLOAT64, &re)
721
+ || ! ca_decimal_string_to(vim, CA_FLOAT64, &im) ) {
722
+ return 0;
723
+ }
724
+ *out = CMPLX(re, im);
725
+ return 1;
726
+ }
727
+
728
+ /* A real is read as a decimal number; anything else goes to Complex(). */
729
+ double complex
730
+ ca_str_to_cmplx (VALUE str)
731
+ {
732
+ volatile VALUE rstr;
733
+ volatile VALUE c;
734
+ double d;
735
+ double complex z;
736
+ if ( ca_decimal_string_to(str, CA_FLOAT64, &d) ) {
737
+ return (double complex) d;
738
+ }
739
+ if ( ( memchr(RSTRING_PTR(str), 'N', RSTRING_LEN(str))
740
+ || memchr(RSTRING_PTR(str), 'I', RSTRING_LEN(str)) )
741
+ && ca_str_to_cmplx_nonfinite(str, &z) ) {
742
+ return z;
743
+ }
744
+ rstr = rb_funcall(str, rb_intern("strip"), 0);
745
+ c = rb_funcall(rb_mKernel, rb_intern("Complex"), 1, rstr);
746
+ return CMPLX(NUM2DBL(rb_complex_real(c)), NUM2DBL(rb_complex_imag(c)));
747
+ }
748
+
133
749
  /* Object -> double with parse-failure signalling. Returns 1 and sets *out
134
750
  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. */
751
+ caller writes UNDEF (mask) for that cell. nil and unreadable
752
+ strings/objects fail; already-numeric values succeed, and a String is
753
+ read as a decimal number. This is the reading path of to_type from an
754
+ object array, where a cell with no number in it is a missing value; a
755
+ store (OBJ2DBL) raises instead. */
139
756
  int
140
757
  ca_obj2dbl_ok (VALUE val, double *out)
141
758
  {
@@ -147,13 +764,11 @@ ca_obj2dbl_ok (VALUE val, double *out)
147
764
  return 1;
148
765
  }
149
766
  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
- }
767
+ return ca_decimal_string_to(val, CA_FLOAT64, out);
154
768
  }
155
769
  {
156
770
  int state = 0;
771
+ /* protect: converts the caller's value and reports failure; nothing is held */
157
772
  volatile VALUE r = rb_protect(ca_kernel_float, val, &state);
158
773
  if ( state ) {
159
774
  rb_set_errinfo(Qnil);
@@ -164,98 +779,120 @@ ca_obj2dbl_ok (VALUE val, double *out)
164
779
  }
165
780
  }
166
781
 
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). */
782
+ /* Object -> complex with parse-failure signalling: the complex sibling of
783
+ ca_obj2dbl_ok. A cell is read as a store reads it (a real as a decimal
784
+ number, the rest as Complex()); nil and a cell that does not read fail. */
785
+ static VALUE
786
+ ca_cmplx_cell_body (VALUE v)
787
+ {
788
+ double complex z = rb_carray_num2cmplx(v);
789
+ return rb_dbl_complex_new(creal(z), cimag(z));
790
+ }
791
+
173
792
  int
174
- rb_obj2long_ok (VALUE val, long *out)
793
+ ca_obj2cmplx_ok (VALUE val, double complex *out)
794
+ {
795
+ int state = 0;
796
+ volatile VALUE r;
797
+ if ( NIL_P(val) ) {
798
+ return 0;
799
+ }
800
+ /* protect: converts the caller's value and reports failure; nothing is held */
801
+ r = rb_protect(ca_cmplx_cell_body, val, &state);
802
+ if ( state ) {
803
+ rb_set_errinfo(Qnil);
804
+ return 0;
805
+ }
806
+ *out = CMPLX(NUM2DBL(rb_complex_real(r)), NUM2DBL(rb_complex_imag(r)));
807
+ return 1;
808
+ }
809
+
810
+ /* Object -> integer with parse-failure signalling. Same contract as
811
+ ca_obj2dbl_ok: 1 + *out on success, 0 (mask) on failure. A String is
812
+ read as a decimal number ("1.5" fails, no truncation); any other object
813
+ goes through Integer(). nil fails. A valid Integer that overflows the
814
+ widest C integer still raises (a domain error, not a parse failure). */
815
+ static int
816
+ ca_obj2integer_ok (VALUE val, int8_t wide_type, void *wide, VALUE *integer)
175
817
  {
818
+ int state = 0;
176
819
  if ( NIL_P(val) ) {
177
820
  return 0;
178
821
  }
822
+ if ( RB_TYPE_P(val, T_STRING) ) {
823
+ return ca_decimal_string_to(val, wide_type, wide);
824
+ }
825
+ /* protect: converts the caller's value and reports failure; nothing is held */
826
+ *integer = rb_protect(ca_kernel_integer, val, &state);
827
+ if ( state ) {
828
+ rb_set_errinfo(Qnil);
829
+ return 0;
830
+ }
831
+ return 1;
832
+ }
833
+
834
+ int
835
+ rb_obj2long_ok (VALUE val, long *out)
836
+ {
837
+ int64_t wide;
838
+ VALUE integer = Qnil;
179
839
  if ( RB_TYPE_P(val, T_FIXNUM) ) {
180
840
  *out = NUM2LONG(val);
181
841
  return 1;
182
842
  }
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;
843
+ if ( ! ca_obj2integer_ok(val, CA_INT64, &wide, &integer) ) {
844
+ return 0;
192
845
  }
846
+ *out = NIL_P(integer) ? (long) wide : NUM2LONG(integer);
847
+ return 1;
193
848
  }
194
849
 
195
850
  int
196
851
  rb_obj2ulong_ok (VALUE val, unsigned long *out)
197
852
  {
198
- if ( NIL_P(val) ) {
199
- return 0;
200
- }
853
+ uint64_t wide;
854
+ VALUE integer = Qnil;
201
855
  if ( RB_TYPE_P(val, T_FIXNUM) ) {
202
856
  *out = NUM2ULONG(val);
203
857
  return 1;
204
858
  }
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;
859
+ if ( ! ca_obj2integer_ok(val, CA_UINT64, &wide, &integer) ) {
860
+ return 0;
214
861
  }
862
+ *out = NIL_P(integer) ? (unsigned long) wide : NUM2ULONG(integer);
863
+ return 1;
215
864
  }
216
865
 
217
866
  int
218
867
  rb_obj2ll_ok (VALUE val, long long *out)
219
868
  {
220
- if ( NIL_P(val) ) {
221
- return 0;
222
- }
869
+ int64_t wide;
870
+ VALUE integer = Qnil;
223
871
  if ( RB_TYPE_P(val, T_FIXNUM) ) {
224
872
  *out = NUM2LL(val);
225
873
  return 1;
226
874
  }
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;
875
+ if ( ! ca_obj2integer_ok(val, CA_INT64, &wide, &integer) ) {
876
+ return 0;
236
877
  }
878
+ *out = NIL_P(integer) ? (long long) wide : NUM2LL(integer);
879
+ return 1;
237
880
  }
238
881
 
239
882
  int
240
883
  rb_obj2ull_ok (VALUE val, unsigned long long *out)
241
884
  {
242
- if ( NIL_P(val) ) {
243
- return 0;
244
- }
885
+ uint64_t wide;
886
+ VALUE integer = Qnil;
245
887
  if ( RB_TYPE_P(val, T_FIXNUM) ) {
246
888
  *out = NUM2ULL(val);
247
889
  return 1;
248
890
  }
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;
891
+ if ( ! ca_obj2integer_ok(val, CA_UINT64, &wide, &integer) ) {
892
+ return 0;
258
893
  }
894
+ *out = NIL_P(integer) ? (unsigned long long) wide : rb_num2ull(integer);
895
+ return 1;
259
896
  }
260
897
 
261
898
  long
@@ -267,8 +904,13 @@ rb_obj2long (VALUE val)
267
904
  case T_BIGNUM:
268
905
  return (long) NUM2LL(val);
269
906
  case T_NIL:
270
- rb_raise(rb_eTypeError, "no implicit conversion from nil to integer");
907
+ rb_raise(rb_eTypeError, "can't convert nil into Integer");
271
908
  break;
909
+ case T_STRING: {
910
+ int64_t v;
911
+ ca_decimal_string_store(val, CA_INT64, &v);
912
+ return (long) v;
913
+ }
272
914
  default:
273
915
  return NUM2LONG(rb_Integer(val));
274
916
  }
@@ -283,8 +925,13 @@ rb_obj2ulong (VALUE val)
283
925
  case T_BIGNUM:
284
926
  return (unsigned long) rb_num2ull(val);
285
927
  case T_NIL:
286
- rb_raise(rb_eTypeError, "no implicit conversion from nil to integer");
928
+ rb_raise(rb_eTypeError, "can't convert nil into Integer");
287
929
  break;
930
+ case T_STRING: {
931
+ uint64_t v;
932
+ ca_decimal_string_store(val, CA_UINT64, &v);
933
+ return (unsigned long) v;
934
+ }
288
935
  default:
289
936
  return NUM2ULONG(rb_Integer(val));
290
937
  }
@@ -297,8 +944,13 @@ rb_obj2ll (VALUE val)
297
944
  case T_FIXNUM:
298
945
  return NUM2LONG(val);
299
946
  case T_NIL:
300
- rb_raise(rb_eTypeError, "no implicit conversion from nil to integer");
947
+ rb_raise(rb_eTypeError, "can't convert nil into Integer");
301
948
  break;
949
+ case T_STRING: {
950
+ int64_t v;
951
+ ca_decimal_string_store(val, CA_INT64, &v);
952
+ return (long long) v;
953
+ }
302
954
  default:
303
955
  return NUM2LL(rb_Integer(val));
304
956
  }
@@ -311,8 +963,13 @@ rb_obj2ull (VALUE val)
311
963
  case T_FIXNUM:
312
964
  return NUM2ULONG(val);
313
965
  case T_NIL:
314
- rb_raise(rb_eTypeError, "no implicit conversion from nil to integer");
966
+ rb_raise(rb_eTypeError, "can't convert nil into Integer");
315
967
  break;
968
+ case T_STRING: {
969
+ uint64_t v;
970
+ ca_decimal_string_store(val, CA_UINT64, &v);
971
+ return (unsigned long long) v;
972
+ }
316
973
  default:
317
974
  return rb_num2ull(rb_Integer(val));
318
975
  }
@@ -334,7 +991,7 @@ ca_face_safe_check (CArray *ca)
334
991
  static inline int8_t
335
992
  ca_storage_type_of (CArray *ca)
336
993
  {
337
- while (ca_face_safe_check(ca)) ca = ((CAView *) ca)->parent;
994
+ if ( ca_face_safe_check(ca) ) ca = ca_strip_face(ca);
338
995
  return ca ? ca->data_type : CA_FIXLEN;
339
996
  }
340
997
 
@@ -399,7 +1056,7 @@ ca_ptr2val (void *ap1, void *ptr1, int8_t data_type, void *ptr2)
399
1056
  {
400
1057
  CArray *ca1 = (CArray *)ap1;
401
1058
  static CArray dummy;
402
- CA_CHECK_DATA_TYPE(data_type);
1059
+ CA_CHECK_DATA_TYPE_GIVEN(data_type);
403
1060
  dummy.data_type = data_type;
404
1061
  ca_cast_func_table[ca1->data_type][data_type](1, ca1, ptr1, &dummy, ptr2, NULL);
405
1062
  return;
@@ -410,7 +1067,7 @@ ca_val2ptr (int8_t data_type, void *ptr1, void *ap2, void *ptr2)
410
1067
  {
411
1068
  CArray *ca2 = (CArray *)ap2;
412
1069
  static CArray dummy;
413
- CA_CHECK_DATA_TYPE(data_type);
1070
+ CA_CHECK_DATA_TYPE_GIVEN(data_type);
414
1071
  dummy.data_type = data_type;
415
1072
  ca_cast_func_table[data_type][ca2->data_type](1, &dummy, ptr1, ca2, ptr2, NULL);
416
1073
  return;
@@ -420,8 +1077,8 @@ void
420
1077
  ca_val2val (int8_t data_type1, void *ptr1, int8_t data_type2, void *ptr2)
421
1078
  {
422
1079
  static CArray dummy1, dummy2;
423
- CA_CHECK_DATA_TYPE(data_type1);
424
- CA_CHECK_DATA_TYPE(data_type2);
1080
+ CA_CHECK_DATA_TYPE_GIVEN(data_type1);
1081
+ CA_CHECK_DATA_TYPE_GIVEN(data_type2);
425
1082
  dummy1.data_type = data_type1;
426
1083
  dummy2.data_type = data_type2;
427
1084
  ca_cast_func_table[data_type1][data_type2](1, &dummy1, ptr1, &dummy2, ptr2, NULL);
@@ -433,7 +1090,7 @@ ca_val2val (int8_t data_type1, void *ptr1, int8_t data_type2, void *ptr2)
433
1090
  /* Cast to CA_OBJECT for an array whose cells mean something other than their
434
1091
  storage bytes: a data_class array (CARecord / CAStruct) or a Face (CATime,
435
1092
  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 --
1093
+ goes through the data_class decode and CA_FACE_STORAGE_TO_ELEMENT_IF_FACE --
437
1094
  so the object array holds the *surface* values (labels, not codes;
438
1095
  CATime::Element, not serials; the string, not the descriptor bytes). Reading
439
1096
  the storage instead is `.parent`, which stays available and explicit. Mask
@@ -558,6 +1215,33 @@ typedef struct {
558
1215
  boolean8_t *scratch;
559
1216
  } ca_to_type_ctx_t;
560
1217
 
1218
+ /* Marks in `m` the cells of a float or complex array that hold NaN (in
1219
+ either part). A NaN has no integer, and C leaves its conversion
1220
+ undefined. */
1221
+ static void
1222
+ ca_mark_nan (CArray *ca, boolean8_t *m)
1223
+ {
1224
+ ca_size_t i, n = ca->elements;
1225
+ switch ( ca->data_type ) {
1226
+ case CA_FLOAT32: { float32_t *p = (float32_t *) ca->ptr;
1227
+ for (i=0; i<n; i++) if ( isnan(p[i]) ) m[i] = 1;
1228
+ break; }
1229
+ case CA_FLOAT64: { float64_t *p = (float64_t *) ca->ptr;
1230
+ for (i=0; i<n; i++) if ( isnan(p[i]) ) m[i] = 1;
1231
+ break; }
1232
+ #ifdef HAVE_COMPLEX_H
1233
+ case CA_CMPLX64: { cmplx64_t *p = (cmplx64_t *) ca->ptr;
1234
+ for (i=0; i<n; i++) if ( isnan(crealf(p[i])) || isnan(cimagf(p[i])) ) m[i] = 1;
1235
+ break; }
1236
+ case CA_CMPLX128: { cmplx128_t *p = (cmplx128_t *) ca->ptr;
1237
+ for (i=0; i<n; i++) if ( isnan(creal(p[i])) || isnan(cimag(p[i])) ) m[i] = 1;
1238
+ break; }
1239
+ #endif
1240
+ default:
1241
+ break;
1242
+ }
1243
+ }
1244
+
561
1245
  /* The cast proper, with `ca` attached. It can raise (a value the target
562
1246
  type cannot hold), so it runs under rb_ensure. */
563
1247
  static VALUE
@@ -565,11 +1249,16 @@ ca_to_type_cast (VALUE arg)
565
1249
  {
566
1250
  ca_to_type_ctx_t *c = (ca_to_type_ctx_t *) arg;
567
1251
  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
1252
+ int to_integer = ( cb->data_type >= CA_INT8 && cb->data_type <= CA_UINT64 );
1253
+ int from_float = ( ca->data_type == CA_FLOAT32 || ca->data_type == CA_FLOAT64
1254
+ || ca->data_type == CA_CMPLX64 || ca->data_type == CA_CMPLX128 );
1255
+
1256
+ if ( ( ca->data_type == CA_OBJECT
1257
+ && ( to_integer
1258
+ || cb->data_type == CA_FLOAT32 || cb->data_type == CA_FLOAT64
1259
+ || cb->data_type == CA_CMPLX64 || cb->data_type == CA_CMPLX128 ) )
1260
+ || ( from_float && to_integer ) ) {
1261
+ /* object -> int/float/complex: give the cast a mask buffer so an unparseable
573
1262
  cell becomes UNDEF (see ext/carray_cast_func.rb). Cast into a
574
1263
  scratch mask seeded from the source mask, then attach it to the
575
1264
  output only if some cell ended up masked -- an all-valid cast keeps
@@ -585,6 +1274,13 @@ ca_to_type_cast (VALUE arg)
585
1274
  else {
586
1275
  memset(scratch, 0, ne);
587
1276
  }
1277
+ /* float -> integer: a NaN cell becomes UNDEF, as an unparseable object
1278
+ cell does. A finite value outside the integer type is converted the
1279
+ way the machine's C converts it, which differs between machines. */
1280
+ if ( from_float ) {
1281
+ ca_mark_nan(ca, scratch);
1282
+ memset(cb->ptr, 0, ne * cb->bytes);
1283
+ }
588
1284
  ca_cast_block_with_mask(ne, ca, ca->ptr, cb, cb->ptr, scratch);
589
1285
  for (i=0; i<ne; i++) {
590
1286
  if ( scratch[i] ) {
@@ -627,8 +1323,20 @@ ca_to_type_release (VALUE arg)
627
1323
  data_type (a new entity owning its storage). User doc lives in
628
1324
  yard-stubs/carray_cast.rb. */
629
1325
 
1326
+ static VALUE rb_ca_to_type_convert (int argc, VALUE *argv, VALUE self);
1327
+
1328
+ /* The converted array keeps self's attributes: the values are the same
1329
+ values in another representation. */
630
1330
  static VALUE
631
1331
  rb_ca_to_type_internal (int argc, VALUE *argv, VALUE self)
1332
+ {
1333
+ VALUE obj = rb_ca_to_type_convert(argc, argv, self);
1334
+ rb_ca_inherit_attr(obj, self);
1335
+ return obj;
1336
+ }
1337
+
1338
+ static VALUE
1339
+ rb_ca_to_type_convert (int argc, VALUE *argv, VALUE self)
632
1340
  {
633
1341
  volatile VALUE obj, rtype = Qnil, ropt, rbytes = Qnil;
634
1342
  CArray *ca, *cb;
@@ -657,8 +1365,8 @@ rb_ca_to_type_internal (int argc, VALUE *argv, VALUE self)
657
1365
  Numeric Face is not routed here -- its surface *is* its storage, so the
658
1366
  ordinary cast below is already right. */
659
1367
  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));
1368
+ return rb_ca_to_type_convert(argc, argv,
1369
+ rb_ca_face_numeric_projection(self, ca));
662
1370
  }
663
1371
 
664
1372
  ca_update_mask(ca);
@@ -676,7 +1384,7 @@ rb_ca_to_type_internal (int argc, VALUE *argv, VALUE self)
676
1384
  ctx.ca = ca;
677
1385
  ctx.cb = cb;
678
1386
  ctx.scratch = NULL;
679
- ca_attach(ca);
1387
+ ca_attach(ca); /* window: released by ca_to_type_release under rb_ensure */
680
1388
  rb_ensure(ca_to_type_cast, (VALUE) &ctx, ca_to_type_release, (VALUE) &ctx);
681
1389
 
682
1390
  /* When rtype is a data_class (e.g. CAStruct subclass), wrap the
@@ -1063,8 +1771,11 @@ rb_ca_ptr2ptr (VALUE ra1, void *ptr1, VALUE ra2, void *ptr2)
1063
1771
  }
1064
1772
 
1065
1773
 
1774
+ /* A cell's bytes as the value of self's data type before the last two
1775
+ steps of ca[i]: a boolean is 0 or 1 and a Face cell is its storage
1776
+ value. The element readers call this, check the mask, then decode. */
1066
1777
  VALUE
1067
- rb_ca_ptr2obj (VALUE self, void *ptr)
1778
+ rb_ca_ptr2obj_storage (VALUE self, void *ptr)
1068
1779
  {
1069
1780
  volatile VALUE obj;
1070
1781
  static CArray dummy;
@@ -1080,31 +1791,45 @@ rb_ca_ptr2obj (VALUE self, void *ptr)
1080
1791
  }
1081
1792
  }
1082
1793
 
1794
+ VALUE
1795
+ rb_ca_ptr2obj (VALUE self, void *ptr)
1796
+ {
1797
+ CArray *ca;
1798
+ VALUE obj = rb_ca_ptr2obj_storage(self, ptr);
1799
+ TypedData_Get_Struct(self, CArray, &carray_data_type, ca);
1800
+ CA_FACE_STORAGE_TO_ELEMENT_IF_FACE(obj, self, ca);
1801
+ if ( ca->data_type == CA_BOOLEAN &&
1802
+ ( obj == INT2FIX(0) || obj == INT2FIX(1) ) ) {
1803
+ obj = ( obj == INT2FIX(1) ) ? Qtrue : Qfalse;
1804
+ }
1805
+ return obj;
1806
+ }
1807
+
1083
1808
  /* Surface -> storage-domain conversion for a Face store (mirror of the
1084
- storage_to_scalar read hook). Non-Face `ca` returns `obj` unchanged, so
1809
+ storage_to_element read hook). Non-Face `ca` returns `obj` unchanged, so
1085
1810
  the non-Face store path is untouched. For a Face, the per-obj_type C
1086
1811
  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
1812
+ `element_to_storage` method is called when defined. A Face that recognizes
1088
1813
  `obj` returns a storage-domain value (e.g. an Integer in its unit); a bare
1089
1814
  Integer / String falls through unchanged to the storage cast. */
1090
1815
  VALUE
1091
- ca_face_scalar_to_storage (VALUE self, CArray *ca, VALUE obj)
1816
+ ca_face_element_to_storage (VALUE self, CArray *ca, VALUE obj)
1092
1817
  {
1093
- ca_face_scalar_to_storage_fn fn;
1818
+ ca_face_element_to_storage_fn fn;
1094
1819
  if ( ! ca_face_safe_check(ca) || obj == CA_UNDEF ) {
1095
1820
  return obj;
1096
1821
  }
1097
- fn = ca_face_scalar_to_storage_table[ca->obj_type];
1822
+ fn = ca_face_element_to_storage_table[ca->obj_type];
1098
1823
  if ( fn != NULL ) {
1099
1824
  return fn(self, obj);
1100
1825
  }
1101
1826
  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");
1827
+ static ID id_element_to_storage = 0;
1828
+ if ( id_element_to_storage == 0 ) {
1829
+ id_element_to_storage = rb_intern("element_to_storage");
1105
1830
  }
1106
- if ( rb_respond_to(self, id_scalar_to_storage) ) {
1107
- return rb_funcall(self, id_scalar_to_storage, 1, obj);
1831
+ if ( rb_respond_to(self, id_element_to_storage) ) {
1832
+ return rb_funcall(self, id_element_to_storage, 1, obj);
1108
1833
  }
1109
1834
  }
1110
1835
  return obj;
@@ -1127,7 +1852,7 @@ rb_ca_obj2ptr (VALUE self, VALUE obj, void *ptr)
1127
1852
  if ( ca_face_safe_check(ca) ) {
1128
1853
  /* Face write hook: turn a surface value object (Scalar / Time /
1129
1854
  DateTime) into a storage-domain value the cast below consumes. */
1130
- obj = ca_face_scalar_to_storage(self, ca, obj);
1855
+ obj = ca_face_element_to_storage(self, ca, obj);
1131
1856
  convert_type = ca_storage_type_of(ca);
1132
1857
  }
1133
1858
  if ( convert_type == CA_FIXLEN ) {
@@ -1149,7 +1874,7 @@ rb_ca_wrap_writable (VALUE arg, VALUE rtype)
1149
1874
  if ( rb_obj_is_carray(obj) ) { /* obj == carray */
1150
1875
  TypedData_Get_Struct(obj, CArray, &carray_data_type, ca);
1151
1876
  if ( ca_is_readonly(ca) ) {
1152
- rb_raise(rb_eRuntimeError, "can't modify read-only carray");
1877
+ ca_refuse_read_only();
1153
1878
  }
1154
1879
  if ( NIL_P(rtype) ) {
1155
1880
  data_type = ca->data_type;
@@ -1187,7 +1912,7 @@ rb_ca_wrap_writable (VALUE arg, VALUE rtype)
1187
1912
  }
1188
1913
  TypedData_Get_Struct(obj, CArray, &carray_data_type, ca);
1189
1914
  if ( ca_is_readonly(ca) ) {
1190
- rb_raise(rb_eRuntimeError, "can't modify read-only carray");
1915
+ ca_refuse_read_only();
1191
1916
  }
1192
1917
  if ( NIL_P(rtype) ) {
1193
1918
  data_type = ca->data_type;
@@ -1201,12 +1926,9 @@ rb_ca_wrap_writable (VALUE arg, VALUE rtype)
1201
1926
  }
1202
1927
  }
1203
1928
  else if ( rb_memory_view_available_p(obj) ) { /* MemoryView producer */
1204
- obj = rb_funcall(rb_cCArray, rb_intern("wrap_memory_view"), 1, obj);
1929
+ obj = ca_mv_wrap(obj, Qnil, rb_cCAWrap, -1,
1930
+ NIL_P(rtype) ? -1 : rb_ca_guess_type(rtype), 1);
1205
1931
  TypedData_Get_Struct(obj, CArray, &carray_data_type, ca);
1206
- if ( ca_is_readonly(ca) ) {
1207
- rb_raise(rb_eRuntimeError,
1208
- "MemoryView source is read-only; can't wrap as writable");
1209
- }
1210
1932
  if ( NIL_P(rtype) ) {
1211
1933
  data_type = ca->data_type;
1212
1934
  }
@@ -1220,7 +1942,7 @@ rb_ca_wrap_writable (VALUE arg, VALUE rtype)
1220
1942
  }
1221
1943
  else {
1222
1944
  volatile VALUE inspect = rb_inspect(CLASS_OF(obj));
1223
- rb_raise(rb_eRuntimeError,
1945
+ rb_raise(rb_eTypeError,
1224
1946
  "given object '%s' can't be wrapped as carray",
1225
1947
  StringValuePtr(inspect));
1226
1948
  }
@@ -1325,7 +2047,8 @@ rb_ca_wrap_readonly (VALUE arg, VALUE rtype)
1325
2047
  }
1326
2048
  }
1327
2049
  else if ( rb_memory_view_available_p(obj) ) { /* MemoryView producer */
1328
- obj = rb_funcall(rb_cCArray, rb_intern("wrap_memory_view"), 1, obj);
2050
+ obj = ca_mv_wrap(obj, Qnil, rb_cCAWrap, -1,
2051
+ NIL_P(rtype) ? -1 : rb_ca_guess_type(rtype), 0);
1329
2052
  TypedData_Get_Struct(obj, CArray, &carray_data_type, ca);
1330
2053
  if ( NIL_P(rtype) ) {
1331
2054
  data_type = ca->data_type;
@@ -1431,6 +2154,92 @@ ca_fixlen_scalar_operand (volatile VALUE *scalar, VALUE reference)
1431
2154
  return 1;
1432
2155
  }
1433
2156
 
2157
+ /* Where a Ruby Integer falls against the range of integer data type `dt`:
2158
+ -1 below it, 1 above it, 0 inside. Anything but an Integer, and a
2159
+ non-integer `dt`, answer 0. */
2160
+ int
2161
+ ca_integer_range_side (VALUE v, int8_t dt)
2162
+ {
2163
+ VALUE lo, hi;
2164
+ if ( ! RB_INTEGER_TYPE_P(v) ) return 0;
2165
+ switch ( dt ) {
2166
+ case CA_INT8: lo = INT2FIX(INT8_MIN); hi = INT2FIX(INT8_MAX); break;
2167
+ case CA_UINT8: lo = INT2FIX(0); hi = INT2FIX(UINT8_MAX); break;
2168
+ case CA_INT16: lo = INT2FIX(INT16_MIN); hi = INT2FIX(INT16_MAX); break;
2169
+ case CA_UINT16: lo = INT2FIX(0); hi = INT2FIX(UINT16_MAX); break;
2170
+ case CA_INT32: lo = LL2NUM(INT32_MIN); hi = LL2NUM(INT32_MAX); break;
2171
+ case CA_UINT32: lo = INT2FIX(0); hi = ULL2NUM(UINT32_MAX); break;
2172
+ case CA_INT64: lo = LL2NUM(INT64_MIN); hi = LL2NUM(INT64_MAX); break;
2173
+ case CA_UINT64: lo = INT2FIX(0); hi = ULL2NUM(UINT64_MAX); break;
2174
+ default: return 0;
2175
+ }
2176
+ if ( RTEST(rb_funcall(v, rb_intern("<"), 1, lo)) ) return -1;
2177
+ if ( RTEST(rb_funcall(v, rb_intern(">"), 1, hi)) ) return 1;
2178
+ return 0;
2179
+ }
2180
+
2181
+ /* Refuses a Ruby Integer that does not fit integer data type `dt`. Taken
2182
+ modulo the type, 256 against a uint8 array would be 0 and compare equal
2183
+ to it. */
2184
+ void
2185
+ ca_check_integer_fits (VALUE v, int8_t dt)
2186
+ {
2187
+ if ( ca_integer_range_side(v, dt) != 0 ) {
2188
+ rb_raise(rb_eRangeError, "%"PRIsVALUE" is out of range for %s",
2189
+ rb_inspect(v), ca_type_name[dt]);
2190
+ }
2191
+ }
2192
+
2193
+ /* The store reader for an integer cell: the value as an Integer, refused
2194
+ when it does not fit the cell's type, as an operator refuses it. A
2195
+ store raises where an array cast (to_type) keeps the C conversion. A
2196
+ non-Integer goes through Integer() (1.5 is 1; nil and NaN raise). */
2197
+ VALUE
2198
+ ca_obj2integer_fitting (VALUE v, int8_t data_type)
2199
+ {
2200
+ VALUE integer = RB_INTEGER_TYPE_P(v) ? v : rb_Integer(v);
2201
+ if ( FIXNUM_P(integer) ) {
2202
+ long x = FIX2LONG(integer);
2203
+ int fits;
2204
+ switch ( data_type ) {
2205
+ case CA_INT8: fits = ( x >= INT8_MIN && x <= INT8_MAX ); break;
2206
+ case CA_UINT8: fits = ( x >= 0 && x <= UINT8_MAX ); break;
2207
+ case CA_INT16: fits = ( x >= INT16_MIN && x <= INT16_MAX ); break;
2208
+ case CA_UINT16: fits = ( x >= 0 && x <= UINT16_MAX ); break;
2209
+ case CA_INT32: fits = ( x >= INT32_MIN && x <= INT32_MAX ); break;
2210
+ case CA_UINT32: fits = ( x >= 0 && (unsigned long) x <= UINT32_MAX ); break;
2211
+ case CA_INT64: fits = 1; break;
2212
+ case CA_UINT64: fits = ( x >= 0 ); break;
2213
+ default: fits = 1; break;
2214
+ }
2215
+ if ( fits ) {
2216
+ return integer;
2217
+ }
2218
+ }
2219
+ ca_check_integer_fits(integer, data_type);
2220
+ return integer;
2221
+ }
2222
+
2223
+ /* A Ruby Integer operand against an integer array is taken in the array's
2224
+ own data type, refused when it does not fit (ca_check_integer_fits).
2225
+ Widening instead would make the result's type depend on a scalar's
2226
+ value. A Float, a Face and an object array keep the coercion below. */
2227
+ static int
2228
+ ca_integer_scalar_operand (volatile VALUE *scalar, VALUE reference)
2229
+ {
2230
+ CArray *cr;
2231
+ if ( ! RB_INTEGER_TYPE_P(*scalar) || ! rb_obj_is_carray(reference) ) {
2232
+ return 0;
2233
+ }
2234
+ TypedData_Get_Struct(reference, CArray, &carray_data_type, cr);
2235
+ if ( ! ca_is_integer_type(cr) || ca_is_face(cr) ) {
2236
+ return 0;
2237
+ }
2238
+ ca_check_integer_fits(*scalar, cr->data_type);
2239
+ *scalar = rb_cscalar_new_with_value(cr->data_type, 0, *scalar);
2240
+ return 1;
2241
+ }
2242
+
1434
2243
  /* CArray.cast(value) -- singleton entry delegating to rb_ca_cast, which
1435
2244
  returns a CArray unchanged and coerces a Ruby value to a CScalar / CArray.
1436
2245
  User doc lives in yard-stubs/carray_cast.rb. */
@@ -1441,6 +2250,41 @@ rb_ca_s_cast (VALUE klass, VALUE val)
1441
2250
  return rb_ca_cast(val);
1442
2251
  }
1443
2252
 
2253
+ /* An operator operand that exports MemoryView, as a CArray. When the object
2254
+ also defines to_ca, that is asked first, in the order wrap_readonly uses:
2255
+ the MemoryView carries only the values buffer, while to_ca can hand over
2256
+ what the object knows besides it (the nulls of an Arrow array as a mask,
2257
+ a time type as a Face). Only MemoryView producers come here, so an object
2258
+ with to_ca alone (a Range) is still not an operand. */
2259
+ static VALUE
2260
+ ca_operand_from_memory_view (VALUE obj)
2261
+ {
2262
+ volatile VALUE ca;
2263
+ if ( rb_respond_to(obj, rb_intern("to_ca")) ) {
2264
+ ca = rb_funcall(obj, rb_intern("to_ca"), 0);
2265
+ if ( ! rb_obj_is_carray(ca) ) {
2266
+ volatile VALUE inspect = rb_inspect(CLASS_OF(obj));
2267
+ rb_raise(rb_eTypeError,
2268
+ "%s#to_ca did not return a CArray", StringValuePtr(inspect));
2269
+ }
2270
+ return ca;
2271
+ }
2272
+ return rb_funcall(rb_cCArray, rb_intern("wrap_memory_view"), 1, obj);
2273
+ }
2274
+
2275
+ /* The name a coercion error gives an operand: its data type, or for a Face
2276
+ its class. A Face reports the data type of its storage ("fixlen" for a
2277
+ time, a categorical, a record or a string column), which says nothing
2278
+ about what the operand is. */
2279
+ static const char *
2280
+ ca_coerce_operand_name (VALUE obj, CArray *ca)
2281
+ {
2282
+ if ( ca_is_face(ca) ) {
2283
+ return rb_obj_classname(obj);
2284
+ }
2285
+ return ca_type_name[ca->data_type];
2286
+ }
2287
+
1444
2288
  void
1445
2289
  rb_ca_cast_self_or_other (volatile VALUE *self, volatile VALUE *other)
1446
2290
  {
@@ -1453,11 +2297,11 @@ rb_ca_cast_self_or_other (volatile VALUE *self, volatile VALUE *other)
1453
2297
  Excludes T_STRING which has its own legacy interpretation. */
1454
2298
  if ( ! rb_obj_is_carray(*self) && TYPE(*self) != T_STRING &&
1455
2299
  rb_memory_view_available_p(*self) ) {
1456
- *self = rb_funcall(rb_cCArray, rb_intern("wrap_memory_view"), 1, *self);
2300
+ *self = ca_operand_from_memory_view(*self);
1457
2301
  }
1458
2302
  if ( ! rb_obj_is_carray(*other) && TYPE(*other) != T_STRING &&
1459
2303
  rb_memory_view_available_p(*other) ) {
1460
- *other = rb_funcall(rb_cCArray, rb_intern("wrap_memory_view"), 1, *other);
2304
+ *other = ca_operand_from_memory_view(*other);
1461
2305
  }
1462
2306
 
1463
2307
  if ( ! rb_obj_is_carray(*self) ) {
@@ -1477,6 +2321,9 @@ rb_ca_cast_self_or_other (volatile VALUE *self, volatile VALUE *other)
1477
2321
  else if ( ca_fixlen_scalar_operand(self, *other) ) {
1478
2322
  /* width taken from the fixlen reference; see the helper above */
1479
2323
  }
2324
+ else if ( ca_integer_scalar_operand(self, *other) ) {
2325
+ /* the array's own integer type; see the helper above */
2326
+ }
1480
2327
  else if ( rb_ca_is_float_type(*other) ) {
1481
2328
  *self = rb_cscalar_new_with_value(CA_FLOAT64, 0, *self);
1482
2329
  }
@@ -1508,6 +2355,9 @@ rb_ca_cast_self_or_other (volatile VALUE *self, volatile VALUE *other)
1508
2355
  else if ( ca_fixlen_scalar_operand(other, *self) ) {
1509
2356
  /* width taken from the fixlen reference; see the helper above */
1510
2357
  }
2358
+ else if ( ca_integer_scalar_operand(other, *self) ) {
2359
+ /* the array's own integer type; see the helper above */
2360
+ }
1511
2361
  else if ( rb_ca_is_float_type(*self) ) {
1512
2362
  *other = rb_cscalar_new_with_value(CA_FLOAT64, 0, *other);
1513
2363
  }
@@ -1577,10 +2427,10 @@ rb_ca_cast_self_or_other (volatile VALUE *self, volatile VALUE *other)
1577
2427
  return;
1578
2428
  }
1579
2429
 
1580
- rb_raise(rb_eRuntimeError,
2430
+ rb_raise(rb_eTypeError,
1581
2431
  "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]);
2432
+ ca_coerce_operand_name(*self, ca),
2433
+ ca_coerce_operand_name(*other, cb));
1584
2434
  }
1585
2435
 
1586
2436
  /* CArray.cast_self_or_other(self, other) — promote self and other
@@ -1624,7 +2474,7 @@ ca_arg_to_data_type (VALUE obj)
1624
2474
  else {
1625
2475
  data_type = ca_value_to_data_type(obj);
1626
2476
  }
1627
- CA_CHECK_DATA_TYPE(data_type);
2477
+ CA_CHECK_DATA_TYPE_GIVEN(data_type);
1628
2478
  if ( ! ca_valid[data_type] ) {
1629
2479
  rb_raise(rb_eArgError,
1630
2480
  "data type '%s' is not valid in this build",
@@ -1664,6 +2514,9 @@ int8_t
1664
2514
  ca_promote_type (int8_t a, int8_t b)
1665
2515
  {
1666
2516
  int test;
2517
+ if ( a <= CA_NONE || a >= CA_NTYPE || b <= CA_NONE || b >= CA_NTYPE ) {
2518
+ rb_raise(rb_eArgError, "invalid data_type id (%i, %i)", (int) a, (int) b);
2519
+ }
1667
2520
  if ( a == b ) {
1668
2521
  return a;
1669
2522
  }
@@ -1681,34 +2534,185 @@ ca_promote_type (int8_t a, int8_t b)
1681
2534
  /* b should be cast to a */
1682
2535
  return a;
1683
2536
  }
1684
- rb_raise(rb_eRuntimeError,
2537
+ rb_raise(rb_eCADataTypeError,
1685
2538
  "can't promote data_types '%s' and '%s' to a common type",
1686
2539
  ca_type_name[a], ca_type_name[b]);
1687
2540
  }
1688
2541
 
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
2542
+ /* The type in which values of types `a` and `b`, whose common type is `t`,
2543
+ are matched for equality. It is `t`, except where `t` cannot hold every
2544
+ value of both: an integer pair of mixed sign whose common type is
2545
+ unsigned (uint8 with int8 is uint8, where -1 would read as 255) moves to
2546
+ a signed type that holds both, or, for 64 bits, where no numeric type
2547
+ does, to object, whose Integers compare by value; and an integer of 32
2548
+ bits or more beside a float32 (or cmplx64) moves to double precision. `eq` answers the
2549
+ mixed-sign pair by value. */
2550
+ int8_t
2551
+ ca_value_match_type (int8_t t, int8_t a, int8_t b)
2552
+ {
2553
+ int signed_a = ( a == CA_INT8 || a == CA_INT16 || a == CA_INT32 || a == CA_INT64 );
2554
+ int signed_b = ( b == CA_INT8 || b == CA_INT16 || b == CA_INT32 || b == CA_INT64 );
2555
+ int wide_a = ( a == CA_INT32 || a == CA_UINT32 || a == CA_INT64 || a == CA_UINT64 );
2556
+ int wide_b = ( b == CA_INT32 || b == CA_UINT32 || b == CA_INT64 || b == CA_UINT64 );
2557
+ /* An integer of 32 bits or more beside a single-precision float: float32
2558
+ holds integers only up to 2**24 (every value of a 16-bit integer, not of
2559
+ a 32-bit one), so the match moves to double precision, which holds every
2560
+ value of a 32-bit integer and of a float32 (a 64-bit integer only up to
2561
+ 2**53). */
2562
+ if ( wide_a || wide_b ) {
2563
+ if ( t == CA_FLOAT32 ) return CA_FLOAT64;
2564
+ if ( t == CA_CMPLX64 ) return CA_CMPLX128;
2565
+ }
2566
+ if ( ! signed_a && ! signed_b ) return t;
2567
+ switch ( t ) {
2568
+ case CA_UINT8: return CA_INT16;
2569
+ case CA_UINT16: return CA_INT32;
2570
+ case CA_UINT32: return CA_INT64;
2571
+ case CA_UINT64: return CA_OBJECT;
2572
+ default: return t;
2573
+ }
2574
+ }
2575
+
2576
+ /* The type a value query and a reference of type `ref` are compared in,
2577
+ by equality or order, as `eq` compares them: the reference is read
2578
+ through a view of that type, so the query is never cut to the
2579
+ reference's type (3.9 does not match 3, -1 does not read as 255).
2580
+ A Float query takes the type the operators give it beside the
2581
+ reference; an Integer query keeps the reference's type when it fits and
2582
+ otherwise the type its value needs, where it matches nothing; a CArray
2583
+ query takes the type the two arrays match in (the common type, widened
2584
+ by ca_value_match_type). Beside a reference that is not a number, a
2585
+ scalar query keeps the reference's type. */
2586
+ int8_t
2587
+ ca_query_match_type (int8_t ref, VALUE query)
2588
+ {
2589
+ int ref_int = ( ref >= CA_INT8 && ref <= CA_UINT64 );
2590
+ int ref_float = ( ref == CA_FLOAT32 || ref == CA_FLOAT64 );
2591
+ if ( rb_obj_is_carray(query) ) {
2592
+ CArray *q;
2593
+ TypedData_Get_Struct(query, CArray, &carray_data_type, q);
2594
+ return ca_value_match_type(ca_promote_type(ref, q->data_type), ref,
2595
+ q->data_type);
2596
+ }
2597
+ if ( ! ref_int && ! ref_float && ref != CA_BOOLEAN ) {
2598
+ return ref;
2599
+ }
2600
+ if ( RB_FLOAT_TYPE_P(query) ) {
2601
+ return ca_promote_scalar_type(ref, query, CA_FLOAT64);
2602
+ }
2603
+ if ( RB_INTEGER_TYPE_P(query) ) {
2604
+ return ref_int ? ca_promote_scalar_type(ref, query, CA_INT64) : ref;
2605
+ }
2606
+ return ref;
2607
+ }
2608
+
2609
+ /* ca.query_match_type(query) -- ca_query_match_type with self as the
2610
+ reference, for the set family and locate_addr. */
2611
+ static VALUE
2612
+ rb_ca_query_match_type (VALUE self, VALUE query)
2613
+ {
2614
+ CArray *ca;
2615
+ GetCArray(self, ca);
2616
+ return rb_ca_data_type_to_sym(ca_query_match_type(ca->data_type, query));
2617
+ }
2618
+
2619
+ /* ca.value_match_type(t, other_type) -- ca_value_match_type with self's
2620
+ data type as the first operand, for the set family and locate_addr. */
2621
+ static VALUE
2622
+ rb_ca_value_match_type (VALUE self, VALUE vt, VALUE vother)
2623
+ {
2624
+ CArray *ca;
2625
+ GetCArray(self, ca);
2626
+ return rb_ca_data_type_to_sym(ca_value_match_type(rb_ca_guess_type(vt), ca->data_type,
2627
+ rb_ca_guess_type(vother)));
2628
+ }
2629
+
2630
+ /* A result_type argument that is a scalar: a Ruby value (Integer, Float,
2631
+ true, ...) or a CScalar. The operators let a scalar take the type of the
2632
+ array beside it, so result_type does too. */
2633
+ static int
2634
+ ca_arg_is_scalar (VALUE obj)
2635
+ {
2636
+ if ( rb_obj_is_carray(obj) ) {
2637
+ CArray *ca;
2638
+ TypedData_Get_Struct(obj, CArray, &carray_data_type, ca);
2639
+ return ca_is_scalar(ca);
2640
+ }
2641
+ if ( TYPE(obj) == T_SYMBOL || TYPE(obj) == T_STRING || TYPE(obj) == T_CLASS ) {
2642
+ return 0;
2643
+ }
2644
+ return ca_mv_probe_data_type(obj) < 0;
2645
+ }
2646
+
2647
+ /* The type a scalar `obj` (of type `st`) and an array of type `at` share,
2648
+ by the rule the operators apply: a scalar of the same kind takes the
2649
+ array's type (0.1 beside float32 is float32, 1 beside int8 is int8); one
2650
+ of another kind promotes (1.5 beside int32 is float64). An Integer that
2651
+ does not fit the array's integer type takes a type that holds it and the
2652
+ array's values (int64, or object where no integer type does), where an
2653
+ operator would raise RangeError. */
2654
+ int8_t
2655
+ ca_promote_scalar_type (int8_t at, VALUE obj, int8_t st)
2656
+ {
2657
+ if ( RB_INTEGER_TYPE_P(obj) && at >= CA_INT8 && at <= CA_UINT64 ) {
2658
+ int8_t vt;
2659
+ if ( ca_integer_range_side(obj, at) == 0 ) {
2660
+ return at;
2661
+ }
2662
+ /* The type the value needs, widened so that the array's values are
2663
+ kept beside it (a uint64 array and -1 meet in object). */
2664
+ vt = ( ca_integer_range_side(obj, CA_INT64) == 0 ) ? CA_INT64
2665
+ : ( ca_integer_range_side(obj, CA_UINT64) == 0 ) ? CA_UINT64
2666
+ : CA_OBJECT;
2667
+ return ca_value_match_type(ca_promote_type(at, vt), at, vt);
2668
+ }
2669
+ if ( ca_cast_table2[st][at] >= 0 ) {
2670
+ return at;
2671
+ }
2672
+ return ca_promote_type(at, st);
2673
+ }
2674
+
2675
+ /* CArray.result_type(*args) -- common data_type of the operands, by the rule
2676
+ the operators use. Each arg is classified by ca_arg_to_data_type: a
1691
2677
  CArray / Symbol / String / Class gives a data_type representation, a
1692
2678
  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. */
2679
+ The non-scalar arguments fold pairwise via ca_promote_type; a scalar then
2680
+ joins that type through ca_promote_scalar_type. With no non-scalar
2681
+ argument every argument folds by its own type. Returns a Symbol. User
2682
+ doc lives in yard-stubs/carray_cast.rb. */
1696
2683
 
1697
2684
  static VALUE
1698
2685
  rb_ca_s_result_type (int argc, VALUE *argv, VALUE klass)
1699
2686
  {
1700
- int8_t cur;
2687
+ int8_t cur = -1;
1701
2688
  int i;
2689
+ VALUE rest, ropt;
2690
+
2691
+ /* Keywords are refused; without this a keyword Hash would be classified
2692
+ as one more operand and the answer would turn into object. */
2693
+ rb_scan_args(argc, argv, "*:", &rest, &ropt);
2694
+ rb_reject_options(ropt);
2695
+ argc = (int) RARRAY_LEN(rest);
2696
+ argv = RARRAY_PTR(rest);
1702
2697
 
1703
2698
  if ( argc < 1 ) {
1704
2699
  rb_raise(rb_eArgError, "result_type requires at least one argument");
1705
2700
  }
1706
2701
 
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);
2702
+ for ( i = 0; i < argc; i++ ) {
2703
+ if ( ! ca_arg_is_scalar(argv[i]) ) {
2704
+ int8_t t = ca_arg_to_data_type(argv[i]);
2705
+ cur = ( cur < 0 ) ? t : ca_promote_type(cur, t);
2706
+ }
2707
+ }
2708
+ for ( i = 0; i < argc; i++ ) {
2709
+ if ( ca_arg_is_scalar(argv[i]) ) {
2710
+ int8_t t = ca_arg_to_data_type(argv[i]);
2711
+ cur = ( cur < 0 ) ? t : ca_promote_scalar_type(cur, argv[i], t);
2712
+ }
1711
2713
  }
2714
+ /* argv points into rest, and classifying an argument can allocate */
2715
+ RB_GC_GUARD(rest);
1712
2716
  /* Return Symbol for family consistency with
1713
2717
  CArray.value_to_data_type and ca.data_type. */
1714
2718
  return rb_ca_data_type_to_sym(cur);
@@ -1747,9 +2751,6 @@ rb_ca_s_promote_list (int argc, VALUE *argv, VALUE klass)
1747
2751
  {
1748
2752
  VALUE list, kwargs, data_type = Qnil;
1749
2753
  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
2754
  int8_t common = -1;
1754
2755
 
1755
2756
  rb_scan_args(argc, argv, "1:", &list, &kwargs);
@@ -1768,49 +2769,26 @@ rb_ca_s_promote_list (int argc, VALUE *argv, VALUE klass)
1768
2769
  if ( RB_TYPE_P(data_type, T_CLASS) || RB_TYPE_P(data_type, T_MODULE) ) {
1769
2770
  rb_raise(rb_eArgError,
1770
2771
  "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;
2772
+ "(Class targets are not supported; leave data_type out to "
2773
+ "keep a Face)");
1802
2774
  }
1803
2775
  }
1804
2776
 
1805
2777
  /* Explicit data_type path: primitive Symbol, all elements must be
1806
2778
  primitive. Reject if any Face in list. */
1807
2779
  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)));
2780
+ for ( i = 0; i < n; i++ ) {
2781
+ VALUE elem = rb_ary_entry(list, i);
2782
+ CArray *ca;
2783
+ rb_check_carray_object(elem);
2784
+ TypedData_Get_Struct(elem, CArray, &carray_data_type, ca);
2785
+ if ( ca_is_face(ca) ) {
2786
+ rb_raise(rb_eArgError,
2787
+ "promote_list: data_type %s cannot be applied to a list "
2788
+ "containing Face elements (= Face cannot be coerced to "
2789
+ "a primitive without losing identity)",
2790
+ RSTRING_PTR(rb_inspect(data_type)));
2791
+ }
1814
2792
  }
1815
2793
  {
1816
2794
  VALUE out = rb_ary_new_capa(n);
@@ -1825,41 +2803,24 @@ rb_ca_s_promote_list (int argc, VALUE *argv, VALUE klass)
1825
2803
  }
1826
2804
  }
1827
2805
 
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
- }
2806
+ /* Auto-detect path. A list of one Face class passes through (the
2807
+ multi-parent constructor lifts it). */
2808
+ if ( ! NIL_P(ca_face_list_class(list, "promote_list")) ) {
2809
+ VALUE out = rb_ary_new_capa(n);
2810
+ for ( i = 0; i < n; i++ ) rb_ary_push(out, rb_ary_entry(list, i));
2811
+ return out;
1855
2812
  }
1856
2813
 
1857
2814
  /* All primitive: result_type to determine common data_type, then
1858
2815
  wrap_readonly each element. */
2816
+ /* The arrays are joined value for value, so a list mixing signed and
2817
+ unsigned integers takes a type that holds both (uint8 with int8 is
2818
+ int16, not uint8, where -1 would read as 255): the rule equality
2819
+ matches such a pair by. */
1859
2820
  common = ca_arg_to_data_type(rb_ary_entry(list, 0));
1860
2821
  for ( i = 1; i < n; i++ ) {
1861
2822
  int8_t next = ca_arg_to_data_type(rb_ary_entry(list, i));
1862
- common = ca_promote_type(common, next);
2823
+ common = ca_value_match_type(ca_promote_type(common, next), common, next);
1863
2824
  }
1864
2825
  {
1865
2826
  VALUE common_sym = rb_ca_data_type_to_sym(common);
@@ -1888,7 +2849,7 @@ rb_ca_cast_other (VALUE *self, volatile VALUE *other)
1888
2849
  Excludes T_STRING which has its own legacy interpretation. */
1889
2850
  if ( ! rb_obj_is_carray(*other) && TYPE(*other) != T_STRING &&
1890
2851
  rb_memory_view_available_p(*other) ) {
1891
- *other = rb_funcall(rb_cCArray, rb_intern("wrap_memory_view"), 1, *other);
2852
+ *other = ca_operand_from_memory_view(*other);
1892
2853
  }
1893
2854
 
1894
2855
  if ( ! rb_obj_is_carray(*other) ) {
@@ -1933,10 +2894,10 @@ rb_ca_cast_other (VALUE *self, volatile VALUE *other)
1933
2894
  return;
1934
2895
  }
1935
2896
 
1936
- rb_raise(rb_eRuntimeError,
2897
+ rb_raise(rb_eTypeError,
1937
2898
  "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]);
2899
+ ca_coerce_operand_name(*self, ca),
2900
+ ca_coerce_operand_name(*other, cb));
1940
2901
  }
1941
2902
 
1942
2903
  /* CArray#cast_with(other) -- coerce self and other to a common
@@ -1951,7 +2912,7 @@ rb_ca_cast_with (VALUE self, VALUE other)
1951
2912
  rb_ca_cast_self_or_other(&self, &other);
1952
2913
  }
1953
2914
  else {
1954
- rb_raise(rb_eRuntimeError, "first argument should be a carray");
2915
+ rb_raise(rb_eTypeError, "first argument should be a carray");
1955
2916
  }
1956
2917
  return rb_assoc_new(self, other);
1957
2918
  }
@@ -2000,19 +2961,28 @@ rb_ca_clip_uint64 (VALUE self)
2000
2961
  volatile VALUE vout = rb_carray_new(CA_UINT64, ca->ndim, ca->dim, 0, NULL);
2001
2962
  CArray *out;
2002
2963
  GetCArray(vout, out);
2003
- ca_attach(src);
2964
+ ca_attach(src); /* window: nothing raises inside */
2004
2965
  double *sp = (double *) src->ptr;
2005
2966
  uint64_t *op = (uint64_t *) out->ptr;
2006
2967
  ca_size_t n = ca->elements;
2007
2968
  const double LIMIT = 18446744073709551616.0; /* 2^64, exact in f64 */
2969
+ boolean8_t *m = ca_has_mask(src) ? (boolean8_t *) src->mask->ptr : NULL; /* attached with src */
2970
+ int found_nan = 0;
2008
2971
  for ( ca_size_t i = 0; i < n; i++ ) {
2009
2972
  double v = sp[i];
2010
- if ( v != v ) op[i] = 0; /* NaN -> 0 */
2973
+ if ( v != v ) { /* NaN has no integer: refused below */
2974
+ op[i] = 0;
2975
+ if ( ! m || ! m[i] ) found_nan = 1;
2976
+ }
2011
2977
  else if ( v >= LIMIT ) op[i] = UINT64_MAX;
2012
2978
  else if ( v <= 0.0 ) op[i] = 0;
2013
2979
  else op[i] = (uint64_t) v;
2014
2980
  }
2015
2981
  ca_detach(src);
2982
+ if ( found_nan ) {
2983
+ rb_raise(rb_eFloatDomainError, "%s: NaN cannot be converted to uint64",
2984
+ ca_calling_method_name());
2985
+ }
2016
2986
  if ( ca_has_mask(ca) ) {
2017
2987
  ca_copy_mask(out, ca);
2018
2988
  }
@@ -2028,16 +2998,19 @@ rb_ca_clip_uint64 (VALUE self)
2028
2998
  Polymorphic cast (NOT a fresh allocator): `data` is coerced into a CArray
2029
2999
  of the target data_type, dispatching on its Ruby class:
2030
3000
 
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.
3001
+ Array -> shape-guessed CArray, element-wise store
3002
+ CArray -> copy if same data_type, else #to_type
3003
+ nil -> empty CArray
3004
+ Range -> arange; a second argument, if given, is the step
3005
+ Numeric -> CScalar, or #to_ca when the object answers it
3006
+ String -> the whitespace / `,` / `;` / `_`->UNDEF parser, which reads
3007
+ far more naturally in Ruby and so is the one branch that
3008
+ calls back out to it (CArray.cast_string in
3009
+ carray/basics.rb, eager-loaded). It receives the data_type
3010
+ as a Symbol.
3011
+
3012
+ A second argument is meaningful only for Range, and a third never is;
3013
+ anything else is refused.
2041
3014
  ------------------------------------------------------------------------ */
2042
3015
 
2043
3016
  static ID id_new_cast = 0, id_guess_array_shape_cast = 0, id_cast_string = 0,
@@ -2063,6 +3036,21 @@ ca_cast_impl (int8_t data_type, int argc, VALUE *argv)
2063
3036
  VALUE v = ( argc >= 1 ) ? argv[0] : Qnil;
2064
3037
  VALUE sym = ID2SYM(ca_data_type_sym[data_type]);
2065
3038
 
3039
+ /* Only the Range branch below reads a second argument (the step), and
3040
+ nothing reads a third. The arity is -1 for the step's sake, so the count
3041
+ is checked here; without it `CA_INT32(0, 2)`, which looks like the shape
3042
+ spelling `CArray.int32(3, 3)`, casts the 0 and drops the 2. */
3043
+ if ( argc > 2 ) {
3044
+ rb_raise(rb_eArgError,
3045
+ "wrong number of arguments (given %d, expected 0..2)", argc);
3046
+ }
3047
+ if ( argc == 2 && ! RTEST(rb_obj_is_kind_of(v, rb_cRange)) ) {
3048
+ rb_raise(rb_eArgError,
3049
+ "a step (the second argument) is only accepted "
3050
+ "when the first argument is a Range (got %s)",
3051
+ rb_obj_classname(v));
3052
+ }
3053
+
2066
3054
  if ( TYPE(v) == T_ARRAY ) {
2067
3055
  volatile VALUE shape, obj;
2068
3056
  shape = rb_funcall(rb_cCArray, id_guess_array_shape_cast, 1, v);
@@ -2083,8 +3071,9 @@ ca_cast_impl (int8_t data_type, int argc, VALUE *argv)
2083
3071
  return rb_carray_new(data_type, 1, &dim0, 0, NULL);
2084
3072
  }
2085
3073
  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). */
3074
+ /* arange: n = ((end - begin).abs / step).floor + 1, or for an excluded
3075
+ end ((end - begin).abs.quo(step)).ceil -- the steps that start before
3076
+ the end -- then seq(begin, sign * step.abs). */
2088
3077
  volatile VALUE vbeg, vend, step, n;
2089
3078
  int sign;
2090
3079
  ca_size_t dn;
@@ -2094,7 +3083,7 @@ ca_cast_impl (int8_t data_type, int argc, VALUE *argv)
2094
3083
  vend = rb_funcall(v, id_end_cast, 0);
2095
3084
  step = ( argc >= 2 ) ? argv[1] : Qnil;
2096
3085
  if ( RTEST(step) && RTEST(rb_funcall(step, id_op_eq, 1, INT2FIX(0))) ) {
2097
- rb_raise(rb_eRuntimeError, "step should not be 0");
3086
+ rb_raise(rb_eArgError, "step should not be 0");
2098
3087
  }
2099
3088
  if ( data_type == CA_OBJECT && !RTEST(step) ) {
2100
3089
  VALUE va = rb_funcall(v, id_to_a_cast, 0); /* CA_OBJECT(v.to_a) */
@@ -2103,9 +3092,13 @@ ca_cast_impl (int8_t data_type, int argc, VALUE *argv)
2103
3092
  if ( !RTEST(step) ) step = INT2FIX(1);
2104
3093
  n = rb_funcall(vend, id_op_minus, 1, vbeg);
2105
3094
  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)) ) {
3095
+ if ( RTEST(rb_funcall(v, id_exclude_end_p_cast, 0)) ) {
3096
+ n = rb_funcall(n, rb_intern("quo"), 1, rb_funcall(step, id_abs_cast, 0));
3097
+ n = rb_funcall(n, rb_intern("ceil"), 0);
3098
+ }
3099
+ else {
3100
+ n = rb_funcall(n, id_op_div, 1, step);
3101
+ n = rb_funcall(n, id_floor_cast, 0);
2109
3102
  n = rb_funcall(n, id_op_plus, 1, INT2FIX(1));
2110
3103
  }
2111
3104
  sign = RTEST(rb_funcall(vbeg, id_op_le, 1, vend)) ? 1 : -1;
@@ -2206,7 +3199,7 @@ rb_ca_cast_fixlen (int argc, VALUE *argv, VALUE self)
2206
3199
  ca_size_t l = NUM2SIZE(rb_funcall(rb_ary_entry(v, i), id_length_cast, 0));
2207
3200
  if ( l > maxlen ) maxlen = l;
2208
3201
  }
2209
- bytes = maxlen;
3202
+ bytes = ( maxlen > 0 ) ? maxlen : 1; /* all empty: one NUL byte */
2210
3203
  } else {
2211
3204
  bytes = NUM2SIZE(bytes_v);
2212
3205
  }
@@ -2219,7 +3212,7 @@ rb_ca_cast_fixlen (int argc, VALUE *argv, VALUE self)
2219
3212
  }
2220
3213
  else if ( NIL_P(v) ) {
2221
3214
  ca_size_t dim0 = 0;
2222
- ca_size_t bytes = NIL_P(bytes_v) ? 0 : NUM2SIZE(bytes_v);
3215
+ ca_size_t bytes = NIL_P(bytes_v) ? 1 : NUM2SIZE(bytes_v);
2223
3216
  return rb_carray_new(CA_FIXLEN, 1, &dim0, bytes, NULL);
2224
3217
  }
2225
3218
  else {
@@ -2231,17 +3224,373 @@ rb_ca_cast_fixlen (int argc, VALUE *argv, VALUE self)
2231
3224
  ? ca : ca_cast_to_fixlen(ca, fixlen_sym, bytes_v);
2232
3225
  }
2233
3226
  {
2234
- ca_size_t bytes = NIL_P(bytes_v) ? 0 : NUM2SIZE(bytes_v);
3227
+ /* No bytes: given: a String is as wide as it is (one NUL byte when
3228
+ empty). Anything else has no width to infer, and the fixlen
3229
+ check reports it. */
3230
+ ca_size_t bytes = 0;
3231
+ if ( ! NIL_P(bytes_v) ) {
3232
+ bytes = NUM2SIZE(bytes_v);
3233
+ }
3234
+ else if ( RB_TYPE_P(v, T_STRING) ) {
3235
+ bytes = ( RSTRING_LEN(v) > 0 ) ? RSTRING_LEN(v) : 1;
3236
+ }
2235
3237
  return rb_cscalar_new_with_value(CA_FIXLEN, bytes, v);
2236
3238
  }
2237
3239
  }
2238
3240
  }
2239
3241
 
3242
+ static VALUE
3243
+ rb_ca_read_text_as_number (int argc, VALUE *argv, VALUE self)
3244
+ {
3245
+ volatile VALUE src = self, out, vmask;
3246
+ VALUE rtype, report;
3247
+ CArray *ca, *co, *cmask;
3248
+ int8_t data_type;
3249
+ int is_float;
3250
+ VALUE *cells;
3251
+ boolean8_t *m_in, *m_out;
3252
+ ca_size_t i;
3253
+
3254
+ rb_scan_args(argc, argv, "11", &rtype, &report);
3255
+ if ( ! NIL_P(report) ) {
3256
+ Check_Type(report, T_ARRAY);
3257
+ }
3258
+
3259
+ data_type = rb_ca_guess_type(rtype);
3260
+ switch ( data_type ) {
3261
+ case CA_INT8: case CA_INT16: case CA_INT32: case CA_INT64:
3262
+ case CA_UINT8: case CA_UINT16: case CA_UINT32: case CA_UINT64:
3263
+ is_float = 0;
3264
+ break;
3265
+ case CA_FLOAT32: case CA_FLOAT64:
3266
+ is_float = 1;
3267
+ break;
3268
+ default:
3269
+ return Qnil; /* not a number type: the caller casts */
3270
+ }
3271
+
3272
+ TypedData_Get_Struct(src, CArray, &carray_data_type, ca);
3273
+ if ( ca->data_type != CA_OBJECT ) {
3274
+ rb_raise(rb_eArgError, "decimal reading needs an object array");
3275
+ }
3276
+ if ( ! ca_is_entity(ca) ) {
3277
+ src = rb_ca_copy(src);
3278
+ TypedData_Get_Struct(src, CArray, &carray_data_type, ca);
3279
+ }
3280
+
3281
+ out = rb_carray_new(data_type, ca->ndim, ca->dim, 0, NULL);
3282
+ TypedData_Get_Struct(out, CArray, &carray_data_type, co);
3283
+ vmask = rb_carray_new(CA_BOOLEAN, ca->ndim, ca->dim, 0, NULL);
3284
+
3285
+ cells = (VALUE *) ca->ptr;
3286
+ m_in = ca_has_mask(ca) ? (boolean8_t *) ca->mask->ptr : NULL;
3287
+ TypedData_Get_Struct(vmask, CArray, &carray_data_type, cmask);
3288
+ m_out = (boolean8_t *) cmask->ptr;
3289
+
3290
+ for (i = 0; i < ca->elements; i++) {
3291
+ VALUE v = cells[i];
3292
+ char *cell = co->ptr + i * co->bytes;
3293
+ int ok = 0;
3294
+ int missing = ( m_in && m_in[i] ) || NIL_P(v);
3295
+ if ( missing ) {
3296
+ ok = 0;
3297
+ }
3298
+ else if ( RB_TYPE_P(v, T_STRING) ) {
3299
+ int rc = ca_decimal_string_read(v, data_type, cell);
3300
+ missing = ( rc == CA_DECIMAL_BLANK );
3301
+ ok = ( rc == CA_DECIMAL_OK );
3302
+ }
3303
+ else if ( is_float ) {
3304
+ if ( RB_FLOAT_TYPE_P(v) || RB_INTEGER_TYPE_P(v) || ca_decimal_other_real(v) ) {
3305
+ double d = NUM2DBL(v);
3306
+ if ( data_type == CA_FLOAT32 ) {
3307
+ *(float32_t *) cell = (float32_t) d;
3308
+ }
3309
+ else {
3310
+ *(float64_t *) cell = d;
3311
+ }
3312
+ ok = 1;
3313
+ }
3314
+ }
3315
+ else {
3316
+ int negative = 0;
3317
+ uint64_t magnitude = 0;
3318
+ ok = ca_decimal_object_integer(v, &negative, &magnitude)
3319
+ && ca_decimal_fits(data_type, negative, magnitude);
3320
+ if ( ok ) {
3321
+ ca_decimal_store_integer(data_type, cell, 0, negative, magnitude);
3322
+ }
3323
+ }
3324
+ m_out[i] = ok ? 0 : 1;
3325
+ if ( ! ok ) {
3326
+ memset(co->ptr + i * co->bytes, 0, co->bytes);
3327
+ if ( ! missing && ! NIL_P(report) ) {
3328
+ rb_ary_push(report, SIZET2NUM(i));
3329
+ }
3330
+ }
3331
+ }
3332
+
3333
+ ca_mask_from_bytes(co, m_out);
3334
+ rb_ca_inherit_attr(out, self);
3335
+ RB_GC_GUARD(src);
3336
+ RB_GC_GUARD(vmask);
3337
+ return out;
3338
+ }
3339
+
3340
+ /* The number type a column of text reads as, for CAFrame#infer_types.
3341
+
3342
+ A cell is an integer when it is digits with an optional sign and fits
3343
+ int64, and a float when it is any other decimal number (the grammar
3344
+ above, nan / inf included). A number with a leading zero ("007",
3345
+ "007.5") is not a number here: it is a code, kept as text. Neither is
3346
+ an integer too long for int64 (an identifier), which a float would
3347
+ round. Blank, nil and masked cells are missing and say nothing.
3348
+
3349
+ Returns :int64 when every present cell is an integer, :float64 when
3350
+ every present cell is a number and one is not an integer, and nil
3351
+ otherwise -- including when no cell is present. */
3352
+
3353
+ enum {
3354
+ CA_INFER_MISSING,
3355
+ CA_INFER_INTEGER,
3356
+ CA_INFER_FLOAT,
3357
+ CA_INFER_TEXT
3358
+ };
3359
+
3360
+ static int
3361
+ ca_infer_decimal_kind_of_bytes (const char *s, long n)
3362
+ {
3363
+ long i = 0, k;
3364
+ int digits_only = 1;
3365
+ ca_decimal_trim(&s, &n);
3366
+ if ( n == 0 ) {
3367
+ return CA_INFER_MISSING;
3368
+ }
3369
+ if ( ! ca_decimal_float(s, n, NULL) ) {
3370
+ return CA_INFER_TEXT;
3371
+ }
3372
+ if ( s[i] == '+' || s[i] == '-' ) {
3373
+ i++;
3374
+ }
3375
+ if ( i + 1 < n && s[i] == '0' && s[i+1] >= '0' && s[i+1] <= '9' ) {
3376
+ return CA_INFER_TEXT; /* a leading zero */
3377
+ }
3378
+ if ( i == n ) {
3379
+ return CA_INFER_TEXT;
3380
+ }
3381
+ for (k = i; k < n; k++) {
3382
+ if ( s[k] < '0' || s[k] > '9' ) {
3383
+ digits_only = 0;
3384
+ break;
3385
+ }
3386
+ }
3387
+ if ( digits_only ) {
3388
+ int negative;
3389
+ uint64_t magnitude;
3390
+ if ( ca_decimal_integer(s, n, &negative, &magnitude) == CA_DECIMAL_OK
3391
+ && ca_decimal_fits(CA_INT64, negative, magnitude) ) {
3392
+ return CA_INFER_INTEGER;
3393
+ }
3394
+ return CA_INFER_TEXT; /* too long for int64 */
3395
+ }
3396
+ return CA_INFER_FLOAT;
3397
+ }
3398
+
3399
+ static VALUE
3400
+ rb_ca_infer_number_type_of_text (VALUE self)
3401
+ {
3402
+ volatile VALUE src = self;
3403
+ CArray *ca;
3404
+ VALUE *cells;
3405
+ boolean8_t *m;
3406
+ ca_size_t i;
3407
+ int present = 0, any_float = 0;
3408
+
3409
+ TypedData_Get_Struct(src, CArray, &carray_data_type, ca);
3410
+ if ( ca->data_type != CA_OBJECT ) {
3411
+ rb_raise(rb_eArgError, "type inference needs an object array");
3412
+ }
3413
+ if ( ! ca_is_entity(ca) ) {
3414
+ src = rb_ca_copy(src);
3415
+ TypedData_Get_Struct(src, CArray, &carray_data_type, ca);
3416
+ }
3417
+ cells = (VALUE *) ca->ptr;
3418
+ m = ca_has_mask(ca) ? (boolean8_t *) ca->mask->ptr : NULL;
3419
+
3420
+ for (i = 0; i < ca->elements; i++) {
3421
+ VALUE v = cells[i];
3422
+ int kind;
3423
+ if ( ( m && m[i] ) || NIL_P(v) ) {
3424
+ continue;
3425
+ }
3426
+ if ( RB_TYPE_P(v, T_STRING) ) {
3427
+ kind = ca_infer_decimal_kind_of_bytes(RSTRING_PTR(v), RSTRING_LEN(v));
3428
+ }
3429
+ else if ( RB_FLOAT_TYPE_P(v) ) {
3430
+ kind = CA_INFER_FLOAT;
3431
+ }
3432
+ else if ( RB_INTEGER_TYPE_P(v) ) {
3433
+ int negative;
3434
+ uint64_t magnitude;
3435
+ kind = ( ca_decimal_object_integer(v, &negative, &magnitude)
3436
+ && ca_decimal_fits(CA_INT64, negative, magnitude) )
3437
+ ? CA_INFER_INTEGER : CA_INFER_TEXT;
3438
+ }
3439
+ else {
3440
+ kind = CA_INFER_TEXT;
3441
+ }
3442
+ if ( kind == CA_INFER_TEXT ) {
3443
+ RB_GC_GUARD(src);
3444
+ return Qnil;
3445
+ }
3446
+ if ( kind != CA_INFER_MISSING ) {
3447
+ present = 1;
3448
+ any_float |= ( kind == CA_INFER_FLOAT );
3449
+ }
3450
+ }
3451
+ RB_GC_GUARD(src);
3452
+ if ( ! present ) {
3453
+ return Qnil;
3454
+ }
3455
+ return ID2SYM(rb_intern(any_float ? "float64" : "int64"));
3456
+ }
3457
+
3458
+ /* The same two readers over a CAConstString, from its buffer and its
3459
+ (start, end) byte ranges, without a String per cell. CAConstString's
3460
+ __infer_number_type_of_text__ / __read_text_as_number__ call them, so
3461
+ CAFrame#infer_types and #cast read a text column from a file this way. */
3462
+
3463
+ /* The buffer of a CAConstString and an entity of its (start, end) byte
3464
+ ranges, masked where a cell is missing. */
3465
+ static VALUE
3466
+ ca_const_string_buffer_and_ranges (VALUE cs, VALUE *buffer, CArray **ranges)
3467
+ {
3468
+ volatile VALUE vranges;
3469
+ VALUE klass = rb_const_get(rb_cObject, rb_intern("CAConstString"));
3470
+ if ( ! RTEST(rb_obj_is_kind_of(cs, klass)) ) {
3471
+ rb_raise(rb_eTypeError, "needs a CAConstString");
3472
+ }
3473
+ *buffer = rb_funcall(cs, rb_intern("buffer"), 0);
3474
+ vranges = rb_ca_parent(cs);
3475
+ TypedData_Get_Struct(vranges, CArray, &carray_data_type, *ranges);
3476
+ if ( ! ca_is_entity(*ranges) ) {
3477
+ vranges = rb_ca_copy(vranges);
3478
+ TypedData_Get_Struct(vranges, CArray, &carray_data_type, *ranges);
3479
+ }
3480
+ return vranges;
3481
+ }
3482
+
3483
+ static VALUE
3484
+ rb_ca_s_const_string_infer_number_type_of_text (VALUE klass, VALUE cs)
3485
+ {
3486
+ volatile VALUE src;
3487
+ VALUE buffer;
3488
+ CArray *pe;
3489
+ const char *buf;
3490
+ const int64_t *pair;
3491
+ boolean8_t *m;
3492
+ ca_size_t i;
3493
+ int present = 0, any_float = 0;
3494
+
3495
+ src = ca_const_string_buffer_and_ranges(cs, &buffer, &pe);
3496
+ buf = RSTRING_PTR(buffer);
3497
+ pair = (const int64_t *) pe->ptr;
3498
+ m = ca_has_mask(pe) ? (boolean8_t *) pe->mask->ptr : NULL;
3499
+ for (i = 0; i < pe->elements; i++) {
3500
+ int kind;
3501
+ if ( m && m[i] ) {
3502
+ continue;
3503
+ }
3504
+ kind = ca_infer_decimal_kind_of_bytes(buf + pair[2 * i], (long) ( pair[2 * i + 1] - pair[2 * i] ));
3505
+ if ( kind == CA_INFER_TEXT ) {
3506
+ RB_GC_GUARD(src);
3507
+ return Qnil;
3508
+ }
3509
+ if ( kind != CA_INFER_MISSING ) {
3510
+ present = 1;
3511
+ any_float |= ( kind == CA_INFER_FLOAT );
3512
+ }
3513
+ }
3514
+ RB_GC_GUARD(src);
3515
+ RB_GC_GUARD(buffer);
3516
+ if ( ! present ) {
3517
+ return Qnil;
3518
+ }
3519
+ return ID2SYM(rb_intern(any_float ? "float64" : "int64"));
3520
+ }
3521
+
3522
+ static VALUE
3523
+ rb_ca_s_const_string_read_text_as_number (VALUE klass, VALUE cs, VALUE rtype, VALUE report)
3524
+ {
3525
+ volatile VALUE src, out;
3526
+ VALUE buffer;
3527
+ CArray *pe, *co;
3528
+ int8_t data_type;
3529
+ const char *buf;
3530
+ const int64_t *pair;
3531
+ boolean8_t *m_in, *m_out = NULL;
3532
+ ca_size_t i;
3533
+
3534
+ if ( ! NIL_P(report) ) {
3535
+ Check_Type(report, T_ARRAY);
3536
+ }
3537
+ data_type = rb_ca_guess_type(rtype);
3538
+ switch ( data_type ) {
3539
+ case CA_INT8: case CA_INT16: case CA_INT32: case CA_INT64:
3540
+ case CA_UINT8: case CA_UINT16: case CA_UINT32: case CA_UINT64:
3541
+ case CA_FLOAT32: case CA_FLOAT64:
3542
+ break;
3543
+ default:
3544
+ return Qnil; /* not a number type: the caller casts */
3545
+ }
3546
+ src = ca_const_string_buffer_and_ranges(cs, &buffer, &pe);
3547
+ out = rb_carray_new(data_type, pe->ndim, pe->dim, 0, NULL);
3548
+ TypedData_Get_Struct(out, CArray, &carray_data_type, co);
3549
+ buf = RSTRING_PTR(buffer);
3550
+ pair = (const int64_t *) pe->ptr;
3551
+ m_in = ca_has_mask(pe) ? (boolean8_t *) pe->mask->ptr : NULL;
3552
+
3553
+ for (i = 0; i < pe->elements; i++) {
3554
+ char *cell = co->ptr + i * co->bytes;
3555
+ int rc = CA_DECIMAL_BLANK;
3556
+ if ( ! ( m_in && m_in[i] ) ) {
3557
+ rc = ca_decimal_read_bytes_as(buf + pair[2 * i],
3558
+ (long) ( pair[2 * i + 1] - pair[2 * i] ),
3559
+ data_type, cell);
3560
+ }
3561
+ if ( rc != CA_DECIMAL_OK ) {
3562
+ if ( m_out == NULL ) {
3563
+ ca_create_mask(co);
3564
+ m_out = (boolean8_t *) co->mask->ptr;
3565
+ }
3566
+ m_out[i] = 1;
3567
+ memset(cell, 0, co->bytes);
3568
+ if ( rc != CA_DECIMAL_BLANK && ! NIL_P(report) ) {
3569
+ rb_ary_push(report, SIZET2NUM(i));
3570
+ }
3571
+ }
3572
+ }
3573
+ RB_GC_GUARD(src);
3574
+ RB_GC_GUARD(buffer);
3575
+ return out;
3576
+ }
3577
+
2240
3578
  void
2241
3579
  Init_carray_cast (void)
2242
3580
  {
2243
3581
  /* CArray data_type conversion */
2244
3582
 
3583
+ /* Internal: CAFrame#infer_types asks the number type of a text column. */
3584
+ rb_define_method(rb_cCArray, "__infer_number_type_of_text__", rb_ca_infer_number_type_of_text, 0);
3585
+
3586
+ /* Internal: CAFrame#cast reads text cells with this grammar. With an
3587
+ Array as the second argument, the addresses of cells that hold
3588
+ something but do not read (blank, nil and masked cells are missing,
3589
+ not unreadable) are pushed onto it. */
3590
+ rb_define_method(rb_cCArray, "__read_text_as_number__", rb_ca_read_text_as_number, -1);
3591
+ rb_define_singleton_method(rb_cCArray, "__const_string_infer_number_type_of_text__", rb_ca_s_const_string_infer_number_type_of_text, 1);
3592
+ rb_define_singleton_method(rb_cCArray, "__const_string_read_text_as_number__", rb_ca_s_const_string_read_text_as_number, 3);
3593
+
2245
3594
  rb_define_method(rb_cCArray, "to_type", rb_ca_to_type_internal, -1);
2246
3595
 
2247
3596
  rb_define_method(rb_cCArray, "fixlen", rb_ca_to_fixlen, -1);
@@ -2306,6 +3655,10 @@ Init_carray_cast (void)
2306
3655
  "cast_self_or_other", rb_ca_s_cast_self_or_other, 2);
2307
3656
  rb_define_singleton_method(rb_cCArray,
2308
3657
  "result_type", rb_ca_s_result_type, -1);
3658
+ rb_define_private_method(rb_cCArray,
3659
+ "value_match_type", rb_ca_value_match_type, 2);
3660
+ rb_define_private_method(rb_cCArray,
3661
+ "query_match_type", rb_ca_query_match_type, 1);
2309
3662
 
2310
3663
  rb_define_singleton_method(rb_cCArray,
2311
3664
  "promote_list", rb_ca_s_promote_list, -1);
@@ -2319,7 +3672,7 @@ Init_carray_cast (void)
2319
3672
  classes registered in ext/carray_data_type.c. */
2320
3673
  id_new_cast = rb_intern("new");
2321
3674
  id_guess_array_shape_cast = rb_intern("guess_array_shape");
2322
- id_cast_string = rb_intern("__cast_string__");
3675
+ id_cast_string = rb_intern("cast_string");
2323
3676
  id_to_ca_cast = rb_intern("to_ca");
2324
3677
  id_length_cast = rb_intern("length");
2325
3678
  id_begin_cast = rb_intern("begin");