carray 3.0.4 → 3.0.6

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 (155) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +438 -0
  3. data/README.md +1 -1
  4. data/carray.gemspec +3 -2
  5. data/ext/ca_array_pool.c +25 -1
  6. data/ext/ca_axis_dispatch.c +19 -20
  7. data/ext/ca_axis_group.c +1 -1
  8. data/ext/ca_categorical_iterator.c +34 -27
  9. data/ext/ca_compare.c +4 -4
  10. data/ext/ca_composite_dispatch.c +9 -9
  11. data/ext/ca_kernel_iterator.c +122 -237
  12. data/ext/ca_kernel_iterator.h +522 -337
  13. data/ext/ca_obj_array.c +69 -19
  14. data/ext/ca_obj_bincmp.c +10 -1
  15. data/ext/ca_obj_binop.c +11 -1
  16. data/ext/ca_obj_bitarray.c +5 -1
  17. data/ext/ca_obj_bitfield.c +60 -43
  18. data/ext/ca_obj_byte_swap.c +4 -1
  19. data/ext/ca_obj_const_string.c +15 -3
  20. data/ext/ca_obj_face.c +44 -1
  21. data/ext/ca_obj_fake.c +3 -1
  22. data/ext/ca_obj_field.c +13 -3
  23. data/ext/ca_obj_grid.c +219 -17
  24. data/ext/ca_obj_meld.c +69 -113
  25. data/ext/ca_obj_moncmp.c +9 -1
  26. data/ext/ca_obj_monop.c +86 -12
  27. data/ext/ca_obj_object.c +118 -32
  28. data/ext/ca_obj_record.c +3 -0
  29. data/ext/ca_obj_reduce.c +19 -1
  30. data/ext/ca_obj_refer.c +80 -26
  31. data/ext/ca_obj_remap.c +69 -10
  32. data/ext/ca_obj_roll.c +27 -6
  33. data/ext/ca_obj_select.c +38 -6
  34. data/ext/ca_obj_select_axis.c +1 -0
  35. data/ext/ca_obj_shift.c +32 -7
  36. data/ext/ca_obj_stack.c +311 -111
  37. data/ext/ca_obj_stride.c +159 -64
  38. data/ext/ca_obj_string.c +27 -2
  39. data/ext/ca_obj_tile.c +28 -7
  40. data/ext/ca_obj_transpose.c +8 -4
  41. data/ext/ca_obj_triop.c +12 -1
  42. data/ext/ca_obj_window.c +80 -50
  43. data/ext/ca_op_ipower.c +12 -12
  44. data/ext/ca_sort_kernels.h +6 -3
  45. data/ext/caframe_csv_reader.c +1 -1
  46. data/ext/carray.h +35 -1
  47. data/ext/carray_access.c +43 -67
  48. data/ext/carray_address_basis.c +134 -0
  49. data/ext/carray_attribute.c +32 -0
  50. data/ext/carray_broadcast.c +3 -4
  51. data/ext/carray_build_flags.h +1 -1
  52. data/ext/carray_cast.c +251 -93
  53. data/ext/carray_cast_func.rb +56 -26
  54. data/ext/carray_conversion.c +11 -7
  55. data/ext/carray_core.c +814 -72
  56. data/ext/carray_count.c +6 -0
  57. data/ext/carray_element.c +22 -13
  58. data/ext/carray_factorize.c +20 -20
  59. data/ext/carray_generate.c +8 -2
  60. data/ext/carray_histogram.c +26 -4
  61. data/ext/carray_index_classifier.c +4 -4
  62. data/ext/carray_internal.h +159 -12
  63. data/ext/carray_kernels_bincmp.c +2174 -170
  64. data/ext/carray_kernels_binop.c +25653 -5487
  65. data/ext/carray_kernels_init.c +8 -8
  66. data/ext/carray_kernels_moncmp.c +96 -96
  67. data/ext/carray_kernels_monop.c +12253 -5235
  68. data/ext/carray_kernels_reduce_aggregate.c +6150 -2423
  69. data/ext/carray_kernels_reduce_boolean.c +1533 -15
  70. data/ext/carray_kernels_reduce_cumulative.c +2442 -882
  71. data/ext/carray_kernels_reduce_extreme.c +5658 -1661
  72. data/ext/carray_kernels_reduce_variance.c +2534 -402
  73. data/ext/carray_kernels_scan.c +368 -368
  74. data/ext/carray_kernels_search.c +4551 -604
  75. data/ext/carray_kernels_sort.c +659 -751
  76. data/ext/carray_kernels_triop.c +114 -114
  77. data/ext/carray_key_windows.c +4 -0
  78. data/ext/carray_lazy.c +47 -0
  79. data/ext/carray_loop.c +1 -1
  80. data/ext/carray_mask.c +175 -161
  81. data/ext/carray_median_percentile.c +4 -4
  82. data/ext/carray_memory_view.c +15 -9
  83. data/ext/carray_operator.c +152 -49
  84. data/ext/carray_order.c +230 -10
  85. data/ext/carray_random.c +22 -19
  86. data/ext/carray_scatter.c +24 -7
  87. data/ext/carray_slab.c +128 -29
  88. data/ext/carray_sort.c +224 -16
  89. data/ext/carray_sort_kernel.c +5 -7
  90. data/ext/carray_struct.c +35 -43
  91. data/ext/carray_test.c +74 -19
  92. data/ext/carray_time_text.c +6 -4
  93. data/ext/carray_utils.c +63 -21
  94. data/ext/extconf.rb +11 -0
  95. data/ext/mkkernel.rb +546 -199
  96. data/ext/ruby_carray.c +4 -1
  97. data/ext/version.h +4 -4
  98. data/lib/carray/attribute.rb +42 -5
  99. data/lib/carray/autoload_carray.rb +3 -3
  100. data/lib/carray/autoload_method_extension.rb +5 -3
  101. data/lib/carray/axis_group.rb +13 -7
  102. data/lib/carray/basics.rb +28 -8
  103. data/lib/carray/block_iterator.rb +17 -3
  104. data/lib/carray/categorical.rb +35 -23
  105. data/lib/carray/categorical_iterator.rb +8 -8
  106. data/lib/carray/complex.rb +2 -2
  107. data/lib/carray/conditional.rb +3 -2
  108. data/lib/carray/const_string.rb +10 -0
  109. data/lib/carray/data_type_extension.rb +8 -1
  110. data/lib/carray/fixlen_string.rb +41 -2
  111. data/lib/carray/frame/column_verbs.rb +48 -16
  112. data/lib/carray/frame/concat.rb +17 -3
  113. data/lib/carray/frame/convert.rb +19 -0
  114. data/lib/carray/frame/csv_parser.rb +32 -32
  115. data/lib/carray/frame/describe.rb +4 -2
  116. data/lib/carray/frame/frame.rb +405 -85
  117. data/lib/carray/frame/group.rb +777 -64
  118. data/lib/carray/frame/io.rb +212 -72
  119. data/lib/carray/frame/join.rb +146 -37
  120. data/lib/carray/frame/records.rb +3 -3
  121. data/lib/carray/frame/reshape.rb +89 -11
  122. data/lib/carray/frame/sort.rb +57 -9
  123. data/lib/carray/frame/verbs.rb +384 -49
  124. data/lib/carray/frame/windows.rb +44 -6
  125. data/lib/carray/fuse_source.rb +31 -2
  126. data/lib/carray/inspect.rb +7 -2
  127. data/lib/carray/iterator.rb +32 -0
  128. data/lib/carray/lazy.rb +52 -8
  129. data/lib/carray/mask_gap_fill.rb +37 -17
  130. data/lib/carray/meld_reduce.rb +1 -1
  131. data/lib/carray/methods/choose.rb +3 -2
  132. data/lib/carray/methods/is_in.rb +8 -1
  133. data/lib/carray/methods/meshgrid.rb +3 -1
  134. data/lib/carray/runtime.rb +47 -8
  135. data/lib/carray/segment_iterator.rb +35 -16
  136. data/lib/carray/serialize.rb +53 -19
  137. data/lib/carray/stack.rb +33 -18
  138. data/lib/carray/string.rb +8 -0
  139. data/lib/carray/string_operation_extension.rb +13 -5
  140. data/lib/carray/struct_builder.rb +5 -3
  141. data/lib/carray/time.rb +112 -68
  142. data/lib/carray/window_iterator.rb +16 -4
  143. data/yard-stubs/ca_obj_array.rb +26 -1
  144. data/yard-stubs/ca_obj_transpose.rb +5 -4
  145. data/yard-stubs/carray_attribute.rb +3 -1
  146. data/yard-stubs/carray_cast.rb +10 -5
  147. data/yard-stubs/carray_conversion.rb +13 -3
  148. data/yard-stubs/carray_element.rb +7 -6
  149. data/yard-stubs/carray_mask.rb +28 -4
  150. data/yard-stubs/carray_math.rb +45 -20
  151. data/yard-stubs/carray_memory_view.rb +7 -0
  152. data/yard-stubs/carray_order.rb +100 -4
  153. data/yard-stubs/carray_scatter.rb +7 -5
  154. data/yard-stubs/carray_test.rb +7 -4
  155. metadata +18 -4
@@ -1,3 +1,3 @@
1
1
  /* GENERATED by extconf.rb: the flags this build's arithmetic
2
2
  was compiled with. See CArray::BUILD_FLAGS. */
3
- #define CA_BUILD_FLAGS "-O3 -fno-fast-math -fwrapv -fopenmp-simd -march=native"
3
+ #define CA_BUILD_FLAGS "-O3 -fno-fast-math -fwrapv -fno-math-errno -fopenmp-simd -march=native"
data/ext/carray_cast.c CHANGED
@@ -1215,26 +1215,48 @@ typedef struct {
1215
1215
  boolean8_t *scratch;
1216
1216
  } ca_to_type_ctx_t;
1217
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. */
1218
+ /* Whether a double truncates into integer type `dt`: not NaN, not an
1219
+ infinity, and inside the open interval of values whose integer part `dt`
1220
+ holds (closed below for int64: -2**63-1 rounds to -2**63 in double).
1221
+ The same bounds as the generated casts in carray_cast_func.rb. */
1222
+ static int
1223
+ ca_double_fits_integer (double v, int8_t dt)
1224
+ {
1225
+ switch ( dt ) {
1226
+ case CA_INT8: return v > -129.0 && v < 128.0;
1227
+ case CA_UINT8: return v > -1.0 && v < 256.0;
1228
+ case CA_INT16: return v > -32769.0 && v < 32768.0;
1229
+ case CA_UINT16: return v > -1.0 && v < 65536.0;
1230
+ case CA_INT32: return v > -2147483649.0 && v < 2147483648.0;
1231
+ case CA_UINT32: return v > -1.0 && v < 4294967296.0;
1232
+ case CA_INT64: return v >= -9223372036854775808.0 && v < 9223372036854775808.0;
1233
+ case CA_UINT64: return v > -1.0 && v < 18446744073709551616.0;
1234
+ default: return 1;
1235
+ }
1236
+ }
1237
+
1238
+ /* Marks in `m` the cells of a float or complex array that have no integer
1239
+ of type `dt`: NaN (in either part), an infinity, or a value out of
1240
+ range. C leaves their conversion undefined, and machines differ. */
1221
1241
  static void
1222
- ca_mark_nan (CArray *ca, boolean8_t *m)
1242
+ ca_mark_unconvertible (CArray *ca, boolean8_t *m, int8_t dt)
1223
1243
  {
1224
1244
  ca_size_t i, n = ca->elements;
1225
1245
  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;
1246
+ case CA_FLOAT32: { ca_u_float32_t *p = (ca_u_float32_t *) ca->ptr;
1247
+ for (i=0; i<n; i++) if ( ! ca_double_fits_integer(p[i], dt) ) m[i] = 1;
1228
1248
  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;
1249
+ case CA_FLOAT64: { ca_u_float64_t *p = (ca_u_float64_t *) ca->ptr;
1250
+ for (i=0; i<n; i++) if ( ! ca_double_fits_integer(p[i], dt) ) m[i] = 1;
1231
1251
  break; }
1232
1252
  #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;
1253
+ case CA_CMPLX64: { ca_u_cmplx64_t *p = (ca_u_cmplx64_t *) ca->ptr;
1254
+ for (i=0; i<n; i++)
1255
+ if ( ! ca_double_fits_integer(crealf(p[i]), dt) || isnan(cimagf(p[i])) ) m[i] = 1;
1235
1256
  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;
1257
+ case CA_CMPLX128: { ca_u_cmplx128_t *p = (ca_u_cmplx128_t *) ca->ptr;
1258
+ for (i=0; i<n; i++)
1259
+ if ( ! ca_double_fits_integer(creal(p[i]), dt) || isnan(cimag(p[i])) ) m[i] = 1;
1238
1260
  break; }
1239
1261
  #endif
1240
1262
  default:
@@ -1274,11 +1296,11 @@ ca_to_type_cast (VALUE arg)
1274
1296
  else {
1275
1297
  memset(scratch, 0, ne);
1276
1298
  }
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. */
1299
+ /* float -> integer: a cell with no integer of the type (NaN, an
1300
+ infinity, a value out of range) becomes UNDEF, as an unparseable
1301
+ object cell does. */
1280
1302
  if ( from_float ) {
1281
- ca_mark_nan(ca, scratch);
1303
+ ca_mark_unconvertible(ca, scratch, cb->data_type);
1282
1304
  memset(cb->ptr, 0, ne * cb->bytes);
1283
1305
  }
1284
1306
  ca_cast_block_with_mask(ne, ca, ca->ptr, cb, cb->ptr, scratch);
@@ -1587,7 +1609,6 @@ static VALUE
1587
1609
  rb_ca_as_type_internal (int argc, VALUE *argv, VALUE self)
1588
1610
  {
1589
1611
  volatile VALUE obj, rtype = Qnil, ropt, rbytes = Qnil;
1590
- CArray *ca;
1591
1612
  int8_t data_type;
1592
1613
  ca_size_t bytes;
1593
1614
 
@@ -1596,14 +1617,12 @@ rb_ca_as_type_internal (int argc, VALUE *argv, VALUE self)
1596
1617
 
1597
1618
  rb_ca_guess_type_and_bytes(rtype, rbytes, &data_type, &bytes);
1598
1619
 
1599
- TypedData_Get_Struct(self, CArray, &carray_data_type, ca);
1600
- if ( ca->data_type == data_type ) {
1601
- if ( ! ca_is_fixlen_type(ca) ) {
1602
- return self;
1603
- }
1604
- }
1620
+ rb_check_carray_object(self);
1621
+
1622
+ /* A view also when the data_type is the receiver's own: what is done to
1623
+ it (an attribute, a freeze) stays with it.
1605
1624
 
1606
- /* Natural-width cast: dispatch via helper (CAMonOp for numeric,
1625
+ Natural-width cast: dispatch via helper (CAMonOp for numeric,
1607
1626
  CAFake for CA_FIXLEN sizing / CA_OBJECT / class-rtype). */
1608
1627
  if ( bytes == (ca_size_t) ca_sizeof[data_type] ) {
1609
1628
  obj = ca_type_adapt_view(self, rtype, data_type, CA_ADAPT_VIEW);
@@ -2017,7 +2036,9 @@ rb_ca_wrap_readonly (VALUE arg, VALUE rtype)
2017
2036
  else {
2018
2037
  CArray *ca_tmp;
2019
2038
  volatile VALUE ref = obj;
2020
- ca_size_t dim = RSTRING_LEN(ref)/ca_sizeof[data_type];
2039
+ ca_size_t bytes = ca_sizeof[data_type];
2040
+ CA_CHECK_BYTES_GIVEN(data_type, bytes);
2041
+ ca_size_t dim = RSTRING_LEN(ref)/bytes;
2021
2042
  obj = rb_carray_new(data_type, 1, &dim, 0, NULL);
2022
2043
  TypedData_Get_Struct(obj, CArray, &carray_data_type, ca_tmp);
2023
2044
  memcpy(ca_tmp->ptr, RSTRING_PTR(ref), dim * ca_sizeof[data_type]);
@@ -2150,7 +2171,15 @@ ca_fixlen_scalar_operand (volatile VALUE *scalar, VALUE reference)
2150
2171
  if ( cr->data_type != CA_FIXLEN ) {
2151
2172
  return 0;
2152
2173
  }
2153
- *scalar = rb_cscalar_new_with_value(CA_FIXLEN, cr->bytes, *scalar);
2174
+ /* A String longer than the cell keeps its length: cut to the cell, it
2175
+ would equal a cell it does not (a two-byte "ab" equal to "abX"). The
2176
+ wider scalar is then compared as two fixlen widths are. */
2177
+ {
2178
+ ca_size_t len = (ca_size_t) RSTRING_LEN(*scalar);
2179
+ *scalar = rb_cscalar_new_with_value(CA_FIXLEN,
2180
+ len > cr->bytes ? len : cr->bytes,
2181
+ *scalar);
2182
+ }
2154
2183
  return 1;
2155
2184
  }
2156
2185
 
@@ -2240,6 +2269,69 @@ ca_integer_scalar_operand (volatile VALUE *scalar, VALUE reference)
2240
2269
  return 1;
2241
2270
  }
2242
2271
 
2272
+ /* A Ruby Array operand against a numeric or boolean array is read as its
2273
+ cells, as the array CArray.wrap_readonly makes of it (nested Arrays give
2274
+ its axes). Its data type is the
2275
+ one the cells would take one at a time as scalar operands of the same
2276
+ operation (an Integer
2277
+ against an integer array takes the array's type and has to fit it),
2278
+ promoted together. Against an object array, a Face or a fixlen array an
2279
+ Array stays one value. */
2280
+ static int
2281
+ ca_array_operand (volatile VALUE *ary, VALUE reference, int both)
2282
+ {
2283
+ CArray *cr;
2284
+ volatile VALUE flat, probe_self, probe;
2285
+ long i, n;
2286
+ int8_t type = -1, seen_fix = -1, seen_big = -1, seen_flo = -1, seen_cpx = -1,
2287
+ seen_bool = -1;
2288
+
2289
+ if ( ! RB_TYPE_P(*ary, T_ARRAY) || ! rb_obj_is_carray(reference) ) {
2290
+ return 0;
2291
+ }
2292
+ TypedData_Get_Struct(reference, CArray, &carray_data_type, cr);
2293
+ if ( ca_is_face(cr) || ca_is_object_type(cr) || ca_is_fixlen_type(cr) ) {
2294
+ return 0;
2295
+ }
2296
+
2297
+ flat = rb_funcall(*ary, rb_intern("flatten"), 0);
2298
+ n = RARRAY_LEN(flat);
2299
+ for (i = 0; i < n; i++) {
2300
+ VALUE e = RARRAY_AREF(flat, i);
2301
+ int8_t *seen, t;
2302
+ CArray *cp;
2303
+ if ( FIXNUM_P(e) ) seen = &seen_fix;
2304
+ else if ( RB_TYPE_P(e, T_BIGNUM) ) seen = &seen_big;
2305
+ else if ( RB_FLOAT_TYPE_P(e) ) seen = &seen_flo;
2306
+ else if ( RB_TYPE_P(e, T_COMPLEX) ) seen = &seen_cpx;
2307
+ else if ( e == Qtrue || e == Qfalse ) seen = &seen_bool;
2308
+ else seen = NULL;
2309
+ if ( RB_INTEGER_TYPE_P(e) && ca_is_integer_type(cr) ) {
2310
+ ca_check_integer_fits(e, cr->data_type);
2311
+ }
2312
+ if ( seen && *seen >= 0 ) {
2313
+ continue;
2314
+ }
2315
+ probe_self = reference;
2316
+ probe = e;
2317
+ if ( both ) {
2318
+ rb_ca_cast_self_or_other(&probe_self, &probe);
2319
+ }
2320
+ else {
2321
+ rb_ca_cast_other((VALUE *) &probe_self, (volatile VALUE *) &probe);
2322
+ }
2323
+ TypedData_Get_Struct(probe, CArray, &carray_data_type, cp);
2324
+ t = cp->data_type;
2325
+ if ( seen ) *seen = t;
2326
+ type = ( type < 0 ) ? t : ca_promote_type(type, t);
2327
+ }
2328
+ if ( type < 0 ) {
2329
+ type = cr->data_type;
2330
+ }
2331
+ *ary = rb_ca_wrap_readonly(*ary, INT2NUM(type));
2332
+ return 1;
2333
+ }
2334
+
2243
2335
  /* CArray.cast(value) -- singleton entry delegating to rb_ca_cast, which
2244
2336
  returns a CArray unchanged and coerces a Ruby value to a CScalar / CArray.
2245
2337
  User doc lives in yard-stubs/carray_cast.rb. */
@@ -2304,6 +2396,10 @@ rb_ca_cast_self_or_other (volatile VALUE *self, volatile VALUE *other)
2304
2396
  *other = ca_operand_from_memory_view(*other);
2305
2397
  }
2306
2398
 
2399
+ if ( ! ca_array_operand(other, *self, 1) ) {
2400
+ ca_array_operand(self, *other, 1);
2401
+ }
2402
+
2307
2403
  if ( ! rb_obj_is_carray(*self) ) {
2308
2404
  self_is_object = 1;
2309
2405
  if ( rb_ca_is_object_type(*other) ) {
@@ -2427,12 +2523,32 @@ rb_ca_cast_self_or_other (volatile VALUE *self, volatile VALUE *other)
2427
2523
  return;
2428
2524
  }
2429
2525
 
2430
- rb_raise(rb_eTypeError,
2526
+ /* A data type the operation does not take, as `sqrt!` on an integer
2527
+ array is refused (CArray::DataTypeError is a TypeError). */
2528
+ rb_raise(rb_eCADataTypeError,
2431
2529
  "can't coerce carray with data_types of '%s' and '%s'",
2432
2530
  ca_coerce_operand_name(*self, ca),
2433
2531
  ca_coerce_operand_name(*other, cb));
2434
2532
  }
2435
2533
 
2534
+ /* Refuses a pair of arrays with no common data_type in the words of
2535
+ rb_ca_cast_self_or_other (the eager operators'), and returns otherwise
2536
+ without casting either. A lazy builder calls it before it works out
2537
+ the kernel's data_types, so that a pair the eager operator refuses is
2538
+ refused the same way. */
2539
+ void
2540
+ ca_refuse_without_common_type (VALUE self, VALUE other)
2541
+ {
2542
+ CArray *ca, *cb;
2543
+ TypedData_Get_Struct(self, CArray, &carray_data_type, ca);
2544
+ TypedData_Get_Struct(other, CArray, &carray_data_type, cb);
2545
+ if ( ca->data_type != cb->data_type
2546
+ && ca_cast_table[cb->data_type][ca->data_type] < 0
2547
+ && ca_cast_table[ca->data_type][cb->data_type] < 0 ) {
2548
+ rb_ca_cast_self_or_other(&self, &other); /* raises */
2549
+ }
2550
+ }
2551
+
2436
2552
  /* CArray.cast_self_or_other(self, other) — promote self and other
2437
2553
  * to a common data_type and return [self', other']. Wraps the
2438
2554
  * void rb_ca_cast_self_or_other above into a class method.
@@ -2471,6 +2587,15 @@ ca_arg_to_data_type (VALUE obj)
2471
2587
  /* MemoryView producer -> its format's data_type (T_STRING excluded above,
2472
2588
  so a String never reaches here). data_type already assigned. */
2473
2589
  }
2590
+ else if ( rb_memory_view_available_p(obj) ) {
2591
+ /* A MemoryView producer whose format says nothing usable (typeless, the
2592
+ other byte order) is read as the operators read it, and refused in
2593
+ the same words. */
2594
+ VALUE ca_obj = ca_operand_from_memory_view(obj);
2595
+ CArray *ca;
2596
+ TypedData_Get_Struct(ca_obj, CArray, &carray_data_type, ca);
2597
+ data_type = ca->data_type;
2598
+ }
2474
2599
  else {
2475
2600
  data_type = ca_value_to_data_type(obj);
2476
2601
  }
@@ -2606,6 +2731,24 @@ ca_query_match_type (int8_t ref, VALUE query)
2606
2731
  return ref;
2607
2732
  }
2608
2733
 
2734
+ /* A fixlen array searched or counted for a String longer than its cells
2735
+ is read as cells of the String's width (NUL-padded), so the query is not
2736
+ cut to the cell: "abX" matches no two-byte cell, as a wider scalar does
2737
+ not in `eq`. Anything else is returned as given. */
2738
+ VALUE
2739
+ ca_fixlen_widen_for_query (VALUE self, VALUE query)
2740
+ {
2741
+ CArray *ca;
2742
+ if ( ! RB_TYPE_P(query, T_STRING) || ! rb_obj_is_carray(self) ) {
2743
+ return self;
2744
+ }
2745
+ TypedData_Get_Struct(self, CArray, &carray_data_type, ca);
2746
+ if ( ca->data_type != CA_FIXLEN || RSTRING_LEN(query) <= ca->bytes ) {
2747
+ return self;
2748
+ }
2749
+ return rb_ca_to_type(self, INT2NUM(CA_FIXLEN), SIZE2NUM(RSTRING_LEN(query)));
2750
+ }
2751
+
2609
2752
  /* ca.query_match_type(query) -- ca_query_match_type with self as the
2610
2753
  reference, for the set family and locate_addr. */
2611
2754
  static VALUE
@@ -2641,7 +2784,7 @@ ca_arg_is_scalar (VALUE obj)
2641
2784
  if ( TYPE(obj) == T_SYMBOL || TYPE(obj) == T_STRING || TYPE(obj) == T_CLASS ) {
2642
2785
  return 0;
2643
2786
  }
2644
- return ca_mv_probe_data_type(obj) < 0;
2787
+ return ! rb_memory_view_available_p(obj);
2645
2788
  }
2646
2789
 
2647
2790
  /* The type a scalar `obj` (of type `st`) and an array of type `at` share,
@@ -2852,6 +2995,8 @@ rb_ca_cast_other (VALUE *self, volatile VALUE *other)
2852
2995
  *other = ca_operand_from_memory_view(*other);
2853
2996
  }
2854
2997
 
2998
+ ca_array_operand(other, *self, 0);
2999
+
2855
3000
  if ( ! rb_obj_is_carray(*other) ) {
2856
3001
  if ( rb_ca_is_object_type(*self) ) {
2857
3002
  *other = rb_cscalar_new_with_value(CA_OBJECT, 0, *other);
@@ -2894,7 +3039,9 @@ rb_ca_cast_other (VALUE *self, volatile VALUE *other)
2894
3039
  return;
2895
3040
  }
2896
3041
 
2897
- rb_raise(rb_eTypeError,
3042
+ /* A data type the operation does not take, as `sqrt!` on an integer
3043
+ array is refused (CArray::DataTypeError is a TypeError). */
3044
+ rb_raise(rb_eCADataTypeError,
2898
3045
  "can't coerce carray with data_types of '%s' and '%s'",
2899
3046
  ca_coerce_operand_name(*self, ca),
2900
3047
  ca_coerce_operand_name(*other, cb));
@@ -2920,78 +3067,89 @@ rb_ca_cast_with (VALUE self, VALUE other)
2920
3067
  /* -----------------------------------------------------------------------
2921
3068
  clip_<type> family — clip into the target data type's representable
2922
3069
  range, then cast. Sugar for `clip(lo, hi).as_<type>`.
3070
+
3071
+ A float source takes a saturating path instead. The type's maximum is
3072
+ not always exact in the source float (INT64_MAX and UINT64_MAX round up
3073
+ in float64, INT32_MAX and UINT32_MAX in float32), so clip-then-cast
3074
+ would hand the cast a value just past the range. Instead each cell is
3075
+ compared in double against the range the cast truncates into
3076
+ (ca_double_fits_integer) and saturates to the type's minimum or maximum
3077
+ outside it. A NaN that is not masked is refused.
2923
3078
  ----------------------------------------------------------------------- */
2924
3079
 
2925
- #define CLIP_CAST_FUNC(name, lo_expr, hi_expr, cast_func) \
3080
+ static VALUE
3081
+ ca_clip_float_to_integer (VALUE self, int8_t to_dt)
3082
+ {
3083
+ CArray *ca;
3084
+ GetCArray(self, ca);
3085
+ volatile VALUE vsrc = ( ca->data_type == CA_FLOAT64 ) ? self : rb_ca_as_float64(self);
3086
+ CArray *src;
3087
+ GetCArray(vsrc, src);
3088
+ volatile VALUE vout = rb_carray_new(to_dt, ca->ndim, ca->dim, 0, NULL);
3089
+ CArray *out;
3090
+ GetCArray(vout, out);
3091
+ ca_attach(src); /* window: nothing raises inside */
3092
+ double *sp = (double *) src->ptr;
3093
+ char *op = out->ptr;
3094
+ ca_size_t n = ca->elements;
3095
+ boolean8_t *m = ca_has_mask(src) ? (boolean8_t *) src->mask->ptr : NULL; /* attached with src */
3096
+ int found_nan = 0;
3097
+ for ( ca_size_t i = 0; i < n; i++ ) {
3098
+ double v = sp[i];
3099
+ int fits = ca_double_fits_integer(v, to_dt);
3100
+ if ( v != v ) { /* NaN has no integer: refused below */
3101
+ v = 0.0;
3102
+ fits = 1;
3103
+ if ( ! m || ! m[i] ) found_nan = 1;
3104
+ }
3105
+ #define CLIP_STORE(type, lo, hi) \
3106
+ ((type *) op)[i] = fits ? (type) v : ( v > 0 ? (hi) : (lo) )
3107
+ switch ( to_dt ) {
3108
+ case CA_INT8: CLIP_STORE(int8_t, INT8_MIN, INT8_MAX); break;
3109
+ case CA_UINT8: CLIP_STORE(uint8_t, 0, UINT8_MAX); break;
3110
+ case CA_INT16: CLIP_STORE(int16_t, INT16_MIN, INT16_MAX); break;
3111
+ case CA_UINT16: CLIP_STORE(uint16_t, 0, UINT16_MAX); break;
3112
+ case CA_INT32: CLIP_STORE(int32_t, INT32_MIN, INT32_MAX); break;
3113
+ case CA_UINT32: CLIP_STORE(uint32_t, 0, UINT32_MAX); break;
3114
+ case CA_INT64: CLIP_STORE(int64_t, INT64_MIN, INT64_MAX); break;
3115
+ case CA_UINT64: CLIP_STORE(uint64_t, 0, UINT64_MAX); break;
3116
+ }
3117
+ #undef CLIP_STORE
3118
+ }
3119
+ ca_detach(src);
3120
+ if ( found_nan ) {
3121
+ rb_raise(rb_eFloatDomainError, "%s: NaN cannot be converted to %s",
3122
+ ca_calling_method_name(), ca_type_name[to_dt]);
3123
+ }
3124
+ if ( ca_has_mask(ca) ) {
3125
+ ca_copy_mask(out, ca);
3126
+ }
3127
+ return vout;
3128
+ }
3129
+
3130
+ #define CLIP_CAST_FUNC(name, to_dt, lo_expr, hi_expr, cast_func) \
2926
3131
  static VALUE \
2927
3132
  rb_ca_##name (VALUE self) \
2928
3133
  { \
3134
+ CArray *ca; \
3135
+ GetCArray(self, ca); \
3136
+ if ( ca->data_type == CA_FLOAT32 || ca->data_type == CA_FLOAT64 ) { \
3137
+ return ca_clip_float_to_integer(self, to_dt); \
3138
+ } \
2929
3139
  return cast_func(rb_ca_clip(self, lo_expr, hi_expr)); \
2930
3140
  }
2931
3141
 
2932
- CLIP_CAST_FUNC(clip_int8, INT2NUM(-128), INT2NUM(127), rb_ca_as_int8)
2933
- CLIP_CAST_FUNC(clip_int16, INT2NUM(-32768), INT2NUM(32767), rb_ca_as_int16)
2934
- CLIP_CAST_FUNC(clip_int32, INT2NUM(-2147483648), INT2NUM(2147483647), rb_ca_as_int32)
2935
- CLIP_CAST_FUNC(clip_int64, LL2NUM(-9223372036854775807-1), LL2NUM(9223372036854775807), rb_ca_as_int64)
2936
- CLIP_CAST_FUNC(clip_uint8, INT2NUM(0), INT2NUM(255), rb_ca_as_uint8)
2937
- CLIP_CAST_FUNC(clip_uint16, INT2NUM(0), INT2NUM(65535), rb_ca_as_uint16)
2938
- CLIP_CAST_FUNC(clip_uint32, INT2NUM(0), UINT2NUM(4294967295U), rb_ca_as_uint32)
3142
+ CLIP_CAST_FUNC(clip_int8, CA_INT8, INT2NUM(-128), INT2NUM(127), rb_ca_as_int8)
3143
+ CLIP_CAST_FUNC(clip_int16, CA_INT16, INT2NUM(-32768), INT2NUM(32767), rb_ca_as_int16)
3144
+ CLIP_CAST_FUNC(clip_int32, CA_INT32, INT2NUM(-2147483648), INT2NUM(2147483647), rb_ca_as_int32)
3145
+ CLIP_CAST_FUNC(clip_int64, CA_INT64, LL2NUM(-9223372036854775807-1), LL2NUM(9223372036854775807), rb_ca_as_int64)
3146
+ CLIP_CAST_FUNC(clip_uint8, CA_UINT8, INT2NUM(0), INT2NUM(255), rb_ca_as_uint8)
3147
+ CLIP_CAST_FUNC(clip_uint16, CA_UINT16, INT2NUM(0), INT2NUM(65535), rb_ca_as_uint16)
3148
+ CLIP_CAST_FUNC(clip_uint32, CA_UINT32, INT2NUM(0), UINT2NUM(4294967295U), rb_ca_as_uint32)
3149
+ CLIP_CAST_FUNC(clip_uint64, CA_UINT64, INT2NUM(0), ULL2NUM(18446744073709551615ULL), rb_ca_as_uint64)
2939
3150
 
2940
3151
  #undef CLIP_CAST_FUNC
2941
3152
 
2942
- /* CArray#clip_uint64 -- dedicated implementation.
2943
-
2944
- UINT64_MAX (= 2^64-1) is not exactly representable in float64; the nearest
2945
- double rounds up to 2^64. A generic clip-then-cast path therefore clamps
2946
- an overflowing float source (e.g. 1e20) to 2^64 and the subsequent cast to
2947
- uint64 wraps around to 0. For floating-point sources we take a saturating
2948
- path: compare in float space against 2^64 (which IS representable) and
2949
- saturate to UINT64_MAX above it and 0 below zero. Other source data types use
2950
- the plain clip-then-cast path. */
2951
- static VALUE
2952
- rb_ca_clip_uint64 (VALUE self)
2953
- {
2954
- CArray *ca;
2955
- GetCArray(self, ca);
2956
- int dt = ca->data_type;
2957
- if ( dt == CA_FLOAT32 || dt == CA_FLOAT64 ) {
2958
- volatile VALUE vsrc = ( dt == CA_FLOAT64 ) ? self : rb_ca_as_float64(self);
2959
- CArray *src;
2960
- GetCArray(vsrc, src);
2961
- volatile VALUE vout = rb_carray_new(CA_UINT64, ca->ndim, ca->dim, 0, NULL);
2962
- CArray *out;
2963
- GetCArray(vout, out);
2964
- ca_attach(src); /* window: nothing raises inside */
2965
- double *sp = (double *) src->ptr;
2966
- uint64_t *op = (uint64_t *) out->ptr;
2967
- ca_size_t n = ca->elements;
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;
2971
- for ( ca_size_t i = 0; i < n; i++ ) {
2972
- double v = sp[i];
2973
- if ( v != v ) { /* NaN has no integer: refused below */
2974
- op[i] = 0;
2975
- if ( ! m || ! m[i] ) found_nan = 1;
2976
- }
2977
- else if ( v >= LIMIT ) op[i] = UINT64_MAX;
2978
- else if ( v <= 0.0 ) op[i] = 0;
2979
- else op[i] = (uint64_t) v;
2980
- }
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
- }
2986
- if ( ca_has_mask(ca) ) {
2987
- ca_copy_mask(out, ca);
2988
- }
2989
- return vout;
2990
- }
2991
- return rb_ca_as_uint64(rb_ca_clip(self, INT2NUM(0),
2992
- ULL2NUM(18446744073709551615ULL)));
2993
- }
2994
-
2995
3153
  /* ------------------------------------------------------------------------
2996
3154
  Global cast functions CA_INT32(data) ... CA_OBJECT(data) etc.
2997
3155
 
@@ -3273,7 +3431,7 @@ rb_ca_read_text_as_number (int argc, VALUE *argv, VALUE self)
3273
3431
  if ( ca->data_type != CA_OBJECT ) {
3274
3432
  rb_raise(rb_eArgError, "decimal reading needs an object array");
3275
3433
  }
3276
- if ( ! ca_is_entity(ca) ) {
3434
+ if ( ! ca_is_entity(ca) || ! ca->ptr ) {
3277
3435
  src = rb_ca_copy(src);
3278
3436
  TypedData_Get_Struct(src, CArray, &carray_data_type, ca);
3279
3437
  }
@@ -3410,7 +3568,7 @@ rb_ca_infer_number_type_of_text (VALUE self)
3410
3568
  if ( ca->data_type != CA_OBJECT ) {
3411
3569
  rb_raise(rb_eArgError, "type inference needs an object array");
3412
3570
  }
3413
- if ( ! ca_is_entity(ca) ) {
3571
+ if ( ! ca_is_entity(ca) || ! ca->ptr ) {
3414
3572
  src = rb_ca_copy(src);
3415
3573
  TypedData_Get_Struct(src, CArray, &carray_data_type, ca);
3416
3574
  }
@@ -3473,7 +3631,7 @@ ca_const_string_buffer_and_ranges (VALUE cs, VALUE *buffer, CArray **ranges)
3473
3631
  *buffer = rb_funcall(cs, rb_intern("buffer"), 0);
3474
3632
  vranges = rb_ca_parent(cs);
3475
3633
  TypedData_Get_Struct(vranges, CArray, &carray_data_type, *ranges);
3476
- if ( ! ca_is_entity(*ranges) ) {
3634
+ if ( ! ca_is_entity(*ranges) || ! (*ranges)->ptr ) {
3477
3635
  vranges = rb_ca_copy(vranges);
3478
3636
  TypedData_Get_Struct(vranges, CArray, &carray_data_type, *ranges);
3479
3637
  }
@@ -24,6 +24,19 @@ type_const = {}
24
24
  type_const[i] = name
25
25
  end
26
26
 
27
+ # The open interval (lo, hi) of doubles whose integer part a type holds:
28
+ # a cast truncates toward zero, so -128.5 still fits int8.
29
+ INTEGER_TRUNC_RANGE = {
30
+ CA_INT8 => ["-129.0", "128.0"],
31
+ CA_UINT8 => ["-1.0", "256.0"],
32
+ CA_INT16 => ["-32769.0", "32768.0"],
33
+ CA_UINT16 => ["-1.0", "65536.0"],
34
+ CA_INT32 => ["-2147483649.0", "2147483648.0"],
35
+ CA_UINT32 => ["-1.0", "4294967296.0"],
36
+ CA_INT64 => ["-9223372036854775808.0", "9223372036854775808.0"],
37
+ CA_UINT64 => ["-1.0", "18446744073709551616.0"],
38
+ }
39
+
27
40
  ctype = {
28
41
  CA_FIXLEN => "fixlen",
29
42
  CA_BOOLEAN => "boolean8_t",
@@ -42,6 +55,12 @@ ctype = {
42
55
  CA_OBJECT => "VALUE"
43
56
  }
44
57
 
58
+ # The type a cast reads and writes a cell through. A view's cells need not
59
+ # be aligned to their size (a struct member at offset 1, a refer with a
60
+ # byte offset), so a pointer to a multi-byte cell is the unaligned typedef
61
+ # from carray.h; ctype stays the plain name the functions are called by.
62
+ utype = Hash.new { |h, c| h[c] = %w[fixlen boolean8_t int8_t uint8_t].include?(c) ? c : "ca_u_#{c}" }
63
+
45
64
  obj2cval = {
46
65
  CA_FIXLEN => nil,
47
66
  CA_BOOLEAN => "OBJ2BOOL",
@@ -256,7 +275,7 @@ FIXLEN.each do |type1|
256
275
  {
257
276
  char *p1 = ptr1;
258
277
  ca_size_t bytes;
259
- VALUE *p2 = ptr2;
278
+ ca_u_VALUE *p2 = ptr2;
260
279
  if ( a1 == NULL ) {
261
280
  rb_raise(rb_eRuntimeError, "[BUG] failed to cast fixlen -> object");
262
281
  }
@@ -291,8 +310,8 @@ puts
291
310
  static void
292
311
  ca_cast_#{ctype1}_#{ctype2}(ca_size_t n, CArray *a1, void *ptr1, CArray *a2, void *ptr2, boolean8_t *m)
293
312
  {
294
- #{ctype1} *p1 = ptr1;
295
- #{ctype2} *p2 = ptr2;
313
+ #{utype[ctype1]} *p1 = ptr1;
314
+ #{utype[ctype2]} *p2 = ptr2;
296
315
  #{ctype1} q;
297
316
  if ( m ) {
298
317
  while ( n-- ) {
@@ -336,22 +355,33 @@ puts
336
355
  ctype1 = ctype[type1]
337
356
  ctype2 = ctype[type2]
338
357
  CA_CAST_TABLE[type1][type2] = "ca_cast_#{ctype1}_#{ctype2}"
339
- # float / complex -> integer: a NaN has no integer and C leaves its
340
- # conversion undefined. A cell that is not masked refuses it before
341
- # anything is written. #to_type masks NaN cells first (UNDEF), so
342
- # only a store, a view and a single cell reach the raise.
358
+ # float / complex -> integer: a NaN, an infinity, or a value whose
359
+ # integer part the type cannot hold has no integer, and C leaves its
360
+ # conversion undefined (x86 gives the type's minimum, ARM saturates). A
361
+ # cell that is not masked refuses it before anything is written.
362
+ # #to_type masks such cells first (UNDEF), so only a store, a view and a
363
+ # single cell reach the raise. The range is the open interval of values
364
+ # that truncate into the type, in double; for int64 the lower end is
365
+ # taken as closed, since -2**63-1 rounds to -2**63 in double.
343
366
  nan_check = ""
344
367
  if (FLOAT+[CA_CMPLX64, CA_CMPLX128]).include?(type1) && INTEGER.include?(type2)
345
- isnan = if [CA_CMPLX64, CA_CMPLX128].include?(type1)
346
- "( isnan(creal(q1[k])) || isnan(cimag(q1[k])) )"
347
- else
348
- "isnan(q1[k])"
349
- end
368
+ complex = [CA_CMPLX64, CA_CMPLX128].include?(type1)
369
+ real = complex ? "creal(q1[k])" : "q1[k]"
370
+ lo, hi = INTEGER_TRUNC_RANGE[type2]
371
+ lo_cmp = ( type2 == CA_INT64 ) ? ">=" : ">"
372
+ fits = "( v #{lo_cmp} #{lo} && v < #{hi} )"
373
+ fits = "( #{fits} && ! isnan(cimag(q1[k])) )" if complex
350
374
  nan_check = <<-END_CHECK.gsub(/^ {6}/, '')
351
375
  for (k=0; k<n; k++) {
352
- if ( ( ! m || ! m[k] ) && #{isnan} ) {
353
- rb_raise(rb_eFloatDomainError, "%s: NaN cannot be converted to %s",
354
- ca_calling_method_name(), ca_type_name[#{type_const[type2]}]);
376
+ double v = (double) #{real};
377
+ if ( ( ! m || ! m[k] ) && ! #{fits} ) {
378
+ if ( isfinite(v)#{complex ? " && ! isnan(cimag(q1[k]))" : ""} ) {
379
+ rb_raise(rb_eRangeError, "%s: %.17g is out of range for %s",
380
+ ca_calling_method_name(), v, ca_type_name[#{type_const[type2]}]);
381
+ }
382
+ rb_raise(rb_eFloatDomainError, "%s: %s cannot be converted to %s",
383
+ ca_calling_method_name(), isinf(v) ? "an infinity" : "NaN",
384
+ ca_type_name[#{type_const[type2]}]);
355
385
  }
356
386
  }
357
387
  END_CHECK
@@ -367,10 +397,10 @@ puts
367
397
  static void
368
398
  ca_cast_#{ctype1}_#{ctype2}(ca_size_t n, CArray *a1, void *ptr1, CArray *a2, void *ptr2, boolean8_t *m)
369
399
  {
370
- #{ctype1} *q1 = ptr1;
371
- #{ctype1} *p1 = q1;
372
- #{ctype2} *q2 = ptr2;
373
- #{ctype2} *p2 = q2;
400
+ #{utype[ctype1]} *q1 = ptr1;
401
+ #{utype[ctype1]} *p1 = q1;
402
+ #{utype[ctype2]} *q2 = ptr2;
403
+ #{utype[ctype2]} *p2 = q2;
374
404
  ca_size_t k;
375
405
  @@NAN_CHECK@@
376
406
  if ( m ) {
@@ -412,8 +442,8 @@ puts
412
442
  static void
413
443
  ca_cast_#{ctype1}_#{ctype2}(ca_size_t n, CArray *a1, void *ptr1, CArray *a2, void *ptr2, boolean8_t *m)
414
444
  {
415
- #{ctype1} *p1 = ptr1;
416
- VALUE *p2 = ptr2;
445
+ #{utype[ctype1]} *p1 = ptr1;
446
+ ca_u_VALUE *p2 = ptr2;
417
447
  if ( m ) {
418
448
  while ( n-- ) {
419
449
  if ( !*m ) { *p2 = (VALUE) #{conv}(*p1); }
@@ -441,7 +471,7 @@ OBJECT.each do |type1|
441
471
  static void
442
472
  ca_cast_#{ctype1}_#{ctype2}(ca_size_t n, CArray *a1, void *ptr1, CArray *a2, void *ptr2, boolean8_t *m)
443
473
  {
444
- VALUE *p1 = ptr1;
474
+ ca_u_VALUE *p1 = ptr1;
445
475
  char *p2 = ptr2;
446
476
  ca_size_t bytes;
447
477
  if ( a2 == NULL ) {
@@ -529,8 +559,8 @@ OBJECT.each do |type1|
529
559
  static void
530
560
  ca_cast_#{ctype1}_#{ctype2}(ca_size_t n, CArray *a1, void *ptr1, CArray *a2, void *ptr2, boolean8_t *m)
531
561
  {
532
- VALUE *p1 = ptr1;
533
- #{ctype2} *p2 = ptr2;
562
+ ca_u_VALUE *p1 = ptr1;
563
+ #{utype[ctype2]} *p2 = ptr2;
534
564
  #{oktype2} tmp;
535
565
  if ( m ) {
536
566
  while ( n-- ) {
@@ -557,8 +587,8 @@ OBJECT.each do |type1|
557
587
  static void
558
588
  ca_cast_#{ctype1}_#{ctype2}(ca_size_t n, CArray *a1, void *ptr1, CArray *a2, void *ptr2, boolean8_t *m)
559
589
  {
560
- VALUE *p1 = ptr1;
561
- #{ctype2} *p2 = ptr2;
590
+ ca_u_VALUE *p1 = ptr1;
591
+ #{utype[ctype2]} *p2 = ptr2;
562
592
  if ( m ) {
563
593
  while ( n-- ) {
564
594
  if ( !*m ) { *p2 = (#{ctype2}) #{conv}(*p1); }