carray 3.0.4 → 3.0.5

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 (111) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +205 -0
  3. data/carray.gemspec +1 -1
  4. data/ext/ca_array_pool.c +25 -1
  5. data/ext/ca_axis_dispatch.c +13 -20
  6. data/ext/ca_categorical_iterator.c +27 -20
  7. data/ext/ca_compare.c +4 -4
  8. data/ext/ca_composite_dispatch.c +9 -9
  9. data/ext/ca_kernel_iterator.c +6 -6
  10. data/ext/ca_kernel_iterator.h +112 -83
  11. data/ext/ca_obj_array.c +69 -19
  12. data/ext/ca_obj_bincmp.c +1 -0
  13. data/ext/ca_obj_binop.c +1 -0
  14. data/ext/ca_obj_bitarray.c +5 -1
  15. data/ext/ca_obj_bitfield.c +58 -43
  16. data/ext/ca_obj_byte_swap.c +2 -1
  17. data/ext/ca_obj_const_string.c +15 -3
  18. data/ext/ca_obj_face.c +42 -1
  19. data/ext/ca_obj_field.c +13 -3
  20. data/ext/ca_obj_grid.c +52 -5
  21. data/ext/ca_obj_monop.c +46 -10
  22. data/ext/ca_obj_object.c +64 -12
  23. data/ext/ca_obj_record.c +3 -0
  24. data/ext/ca_obj_reduce.c +4 -0
  25. data/ext/ca_obj_refer.c +18 -9
  26. data/ext/ca_obj_roll.c +26 -6
  27. data/ext/ca_obj_select.c +34 -5
  28. data/ext/ca_obj_shift.c +31 -7
  29. data/ext/ca_obj_stack.c +1 -0
  30. data/ext/ca_obj_stride.c +28 -19
  31. data/ext/ca_obj_string.c +11 -2
  32. data/ext/ca_obj_tile.c +27 -7
  33. data/ext/ca_obj_transpose.c +8 -4
  34. data/ext/ca_obj_triop.c +3 -0
  35. data/ext/ca_obj_window.c +79 -50
  36. data/ext/ca_op_ipower.c +4 -4
  37. data/ext/ca_sort_kernels.h +6 -3
  38. data/ext/carray.h +33 -0
  39. data/ext/carray_access.c +16 -3
  40. data/ext/carray_attribute.c +24 -0
  41. data/ext/carray_cast.c +243 -82
  42. data/ext/carray_cast_func.rb +56 -26
  43. data/ext/carray_conversion.c +2 -2
  44. data/ext/carray_core.c +3 -0
  45. data/ext/carray_count.c +6 -0
  46. data/ext/carray_element.c +9 -4
  47. data/ext/carray_factorize.c +20 -20
  48. data/ext/carray_generate.c +8 -2
  49. data/ext/carray_histogram.c +1 -1
  50. data/ext/carray_index_classifier.c +4 -4
  51. data/ext/carray_internal.h +12 -0
  52. data/ext/carray_kernels_bincmp.c +168 -168
  53. data/ext/carray_kernels_binop.c +900 -900
  54. data/ext/carray_kernels_init.c +4 -4
  55. data/ext/carray_kernels_moncmp.c +96 -96
  56. data/ext/carray_kernels_monop.c +1984 -1984
  57. data/ext/carray_kernels_reduce_aggregate.c +1733 -1733
  58. data/ext/carray_kernels_reduce_cumulative.c +642 -642
  59. data/ext/carray_kernels_reduce_extreme.c +1150 -1150
  60. data/ext/carray_kernels_reduce_variance.c +258 -258
  61. data/ext/carray_kernels_scan.c +128 -128
  62. data/ext/carray_kernels_search.c +340 -340
  63. data/ext/carray_kernels_sort.c +295 -304
  64. data/ext/carray_kernels_triop.c +114 -114
  65. data/ext/carray_key_windows.c +4 -0
  66. data/ext/carray_mask.c +45 -22
  67. data/ext/carray_memory_view.c +2 -2
  68. data/ext/carray_operator.c +5 -5
  69. data/ext/carray_order.c +227 -8
  70. data/ext/carray_random.c +18 -18
  71. data/ext/carray_scatter.c +24 -7
  72. data/ext/carray_slab.c +32 -9
  73. data/ext/carray_sort.c +224 -16
  74. data/ext/carray_sort_kernel.c +5 -7
  75. data/ext/carray_struct.c +35 -43
  76. data/ext/carray_time_text.c +4 -2
  77. data/ext/carray_utils.c +63 -21
  78. data/ext/mkkernel.rb +210 -115
  79. data/ext/version.h +4 -4
  80. data/lib/carray/attribute.rb +17 -4
  81. data/lib/carray/basics.rb +27 -7
  82. data/lib/carray/categorical.rb +30 -16
  83. data/lib/carray/categorical_iterator.rb +6 -6
  84. data/lib/carray/const_string.rb +10 -0
  85. data/lib/carray/data_type_extension.rb +8 -1
  86. data/lib/carray/fixlen_string.rb +41 -2
  87. data/lib/carray/frame/column_verbs.rb +4 -3
  88. data/lib/carray/frame/concat.rb +17 -3
  89. data/lib/carray/frame/frame.rb +6 -7
  90. data/lib/carray/frame/group.rb +100 -13
  91. data/lib/carray/frame/io.rb +3 -1
  92. data/lib/carray/fuse_source.rb +31 -2
  93. data/lib/carray/lazy.rb +35 -0
  94. data/lib/carray/methods/is_in.rb +8 -1
  95. data/lib/carray/segment_iterator.rb +12 -8
  96. data/lib/carray/serialize.rb +19 -2
  97. data/lib/carray/stack.rb +15 -12
  98. data/lib/carray/string.rb +8 -0
  99. data/lib/carray/string_operation_extension.rb +13 -5
  100. data/lib/carray/struct_builder.rb +5 -3
  101. data/lib/carray/time.rb +107 -68
  102. data/yard-stubs/ca_obj_transpose.rb +5 -4
  103. data/yard-stubs/carray_attribute.rb +3 -1
  104. data/yard-stubs/carray_cast.rb +2 -0
  105. data/yard-stubs/carray_conversion.rb +5 -1
  106. data/yard-stubs/carray_element.rb +7 -6
  107. data/yard-stubs/carray_mask.rb +8 -3
  108. data/yard-stubs/carray_math.rb +14 -4
  109. data/yard-stubs/carray_order.rb +100 -4
  110. data/yard-stubs/carray_scatter.rb +7 -5
  111. metadata +1 -1
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);
@@ -2017,7 +2039,9 @@ rb_ca_wrap_readonly (VALUE arg, VALUE rtype)
2017
2039
  else {
2018
2040
  CArray *ca_tmp;
2019
2041
  volatile VALUE ref = obj;
2020
- ca_size_t dim = RSTRING_LEN(ref)/ca_sizeof[data_type];
2042
+ ca_size_t bytes = ca_sizeof[data_type];
2043
+ CA_CHECK_BYTES_GIVEN(data_type, bytes);
2044
+ ca_size_t dim = RSTRING_LEN(ref)/bytes;
2021
2045
  obj = rb_carray_new(data_type, 1, &dim, 0, NULL);
2022
2046
  TypedData_Get_Struct(obj, CArray, &carray_data_type, ca_tmp);
2023
2047
  memcpy(ca_tmp->ptr, RSTRING_PTR(ref), dim * ca_sizeof[data_type]);
@@ -2150,7 +2174,15 @@ ca_fixlen_scalar_operand (volatile VALUE *scalar, VALUE reference)
2150
2174
  if ( cr->data_type != CA_FIXLEN ) {
2151
2175
  return 0;
2152
2176
  }
2153
- *scalar = rb_cscalar_new_with_value(CA_FIXLEN, cr->bytes, *scalar);
2177
+ /* A String longer than the cell keeps its length: cut to the cell, it
2178
+ would equal a cell it does not (a two-byte "ab" equal to "abX"). The
2179
+ wider scalar is then compared as two fixlen widths are. */
2180
+ {
2181
+ ca_size_t len = (ca_size_t) RSTRING_LEN(*scalar);
2182
+ *scalar = rb_cscalar_new_with_value(CA_FIXLEN,
2183
+ len > cr->bytes ? len : cr->bytes,
2184
+ *scalar);
2185
+ }
2154
2186
  return 1;
2155
2187
  }
2156
2188
 
@@ -2240,6 +2272,69 @@ ca_integer_scalar_operand (volatile VALUE *scalar, VALUE reference)
2240
2272
  return 1;
2241
2273
  }
2242
2274
 
2275
+ /* A Ruby Array operand against a numeric or boolean array is read as its
2276
+ cells, as the array CArray.wrap_readonly makes of it (nested Arrays give
2277
+ its axes). Its data type is the
2278
+ one the cells would take one at a time as scalar operands of the same
2279
+ operation (an Integer
2280
+ against an integer array takes the array's type and has to fit it),
2281
+ promoted together. Against an object array, a Face or a fixlen array an
2282
+ Array stays one value. */
2283
+ static int
2284
+ ca_array_operand (volatile VALUE *ary, VALUE reference, int both)
2285
+ {
2286
+ CArray *cr;
2287
+ volatile VALUE flat, probe_self, probe;
2288
+ long i, n;
2289
+ int8_t type = -1, seen_fix = -1, seen_big = -1, seen_flo = -1, seen_cpx = -1,
2290
+ seen_bool = -1;
2291
+
2292
+ if ( ! RB_TYPE_P(*ary, T_ARRAY) || ! rb_obj_is_carray(reference) ) {
2293
+ return 0;
2294
+ }
2295
+ TypedData_Get_Struct(reference, CArray, &carray_data_type, cr);
2296
+ if ( ca_is_face(cr) || ca_is_object_type(cr) || ca_is_fixlen_type(cr) ) {
2297
+ return 0;
2298
+ }
2299
+
2300
+ flat = rb_funcall(*ary, rb_intern("flatten"), 0);
2301
+ n = RARRAY_LEN(flat);
2302
+ for (i = 0; i < n; i++) {
2303
+ VALUE e = RARRAY_AREF(flat, i);
2304
+ int8_t *seen, t;
2305
+ CArray *cp;
2306
+ if ( FIXNUM_P(e) ) seen = &seen_fix;
2307
+ else if ( RB_TYPE_P(e, T_BIGNUM) ) seen = &seen_big;
2308
+ else if ( RB_FLOAT_TYPE_P(e) ) seen = &seen_flo;
2309
+ else if ( RB_TYPE_P(e, T_COMPLEX) ) seen = &seen_cpx;
2310
+ else if ( e == Qtrue || e == Qfalse ) seen = &seen_bool;
2311
+ else seen = NULL;
2312
+ if ( RB_INTEGER_TYPE_P(e) && ca_is_integer_type(cr) ) {
2313
+ ca_check_integer_fits(e, cr->data_type);
2314
+ }
2315
+ if ( seen && *seen >= 0 ) {
2316
+ continue;
2317
+ }
2318
+ probe_self = reference;
2319
+ probe = e;
2320
+ if ( both ) {
2321
+ rb_ca_cast_self_or_other(&probe_self, &probe);
2322
+ }
2323
+ else {
2324
+ rb_ca_cast_other((VALUE *) &probe_self, (volatile VALUE *) &probe);
2325
+ }
2326
+ TypedData_Get_Struct(probe, CArray, &carray_data_type, cp);
2327
+ t = cp->data_type;
2328
+ if ( seen ) *seen = t;
2329
+ type = ( type < 0 ) ? t : ca_promote_type(type, t);
2330
+ }
2331
+ if ( type < 0 ) {
2332
+ type = cr->data_type;
2333
+ }
2334
+ *ary = rb_ca_wrap_readonly(*ary, INT2NUM(type));
2335
+ return 1;
2336
+ }
2337
+
2243
2338
  /* CArray.cast(value) -- singleton entry delegating to rb_ca_cast, which
2244
2339
  returns a CArray unchanged and coerces a Ruby value to a CScalar / CArray.
2245
2340
  User doc lives in yard-stubs/carray_cast.rb. */
@@ -2304,6 +2399,10 @@ rb_ca_cast_self_or_other (volatile VALUE *self, volatile VALUE *other)
2304
2399
  *other = ca_operand_from_memory_view(*other);
2305
2400
  }
2306
2401
 
2402
+ if ( ! ca_array_operand(other, *self, 1) ) {
2403
+ ca_array_operand(self, *other, 1);
2404
+ }
2405
+
2307
2406
  if ( ! rb_obj_is_carray(*self) ) {
2308
2407
  self_is_object = 1;
2309
2408
  if ( rb_ca_is_object_type(*other) ) {
@@ -2427,12 +2526,32 @@ rb_ca_cast_self_or_other (volatile VALUE *self, volatile VALUE *other)
2427
2526
  return;
2428
2527
  }
2429
2528
 
2430
- rb_raise(rb_eTypeError,
2529
+ /* A data type the operation does not take, as `sqrt!` on an integer
2530
+ array is refused (CArray::DataTypeError is a TypeError). */
2531
+ rb_raise(rb_eCADataTypeError,
2431
2532
  "can't coerce carray with data_types of '%s' and '%s'",
2432
2533
  ca_coerce_operand_name(*self, ca),
2433
2534
  ca_coerce_operand_name(*other, cb));
2434
2535
  }
2435
2536
 
2537
+ /* Refuses a pair of arrays with no common data_type in the words of
2538
+ rb_ca_cast_self_or_other (the eager operators'), and returns otherwise
2539
+ without casting either. A lazy builder calls it before it works out
2540
+ the kernel's data_types, so that a pair the eager operator refuses is
2541
+ refused the same way. */
2542
+ void
2543
+ ca_refuse_without_common_type (VALUE self, VALUE other)
2544
+ {
2545
+ CArray *ca, *cb;
2546
+ TypedData_Get_Struct(self, CArray, &carray_data_type, ca);
2547
+ TypedData_Get_Struct(other, CArray, &carray_data_type, cb);
2548
+ if ( ca->data_type != cb->data_type
2549
+ && ca_cast_table[cb->data_type][ca->data_type] < 0
2550
+ && ca_cast_table[ca->data_type][cb->data_type] < 0 ) {
2551
+ rb_ca_cast_self_or_other(&self, &other); /* raises */
2552
+ }
2553
+ }
2554
+
2436
2555
  /* CArray.cast_self_or_other(self, other) — promote self and other
2437
2556
  * to a common data_type and return [self', other']. Wraps the
2438
2557
  * void rb_ca_cast_self_or_other above into a class method.
@@ -2471,6 +2590,15 @@ ca_arg_to_data_type (VALUE obj)
2471
2590
  /* MemoryView producer -> its format's data_type (T_STRING excluded above,
2472
2591
  so a String never reaches here). data_type already assigned. */
2473
2592
  }
2593
+ else if ( rb_memory_view_available_p(obj) ) {
2594
+ /* A MemoryView producer whose format says nothing usable (typeless, the
2595
+ other byte order) is read as the operators read it, and refused in
2596
+ the same words. */
2597
+ VALUE ca_obj = ca_operand_from_memory_view(obj);
2598
+ CArray *ca;
2599
+ TypedData_Get_Struct(ca_obj, CArray, &carray_data_type, ca);
2600
+ data_type = ca->data_type;
2601
+ }
2474
2602
  else {
2475
2603
  data_type = ca_value_to_data_type(obj);
2476
2604
  }
@@ -2606,6 +2734,24 @@ ca_query_match_type (int8_t ref, VALUE query)
2606
2734
  return ref;
2607
2735
  }
2608
2736
 
2737
+ /* A fixlen array searched or counted for a String longer than its cells
2738
+ is read as cells of the String's width (NUL-padded), so the query is not
2739
+ cut to the cell: "abX" matches no two-byte cell, as a wider scalar does
2740
+ not in `eq`. Anything else is returned as given. */
2741
+ VALUE
2742
+ ca_fixlen_widen_for_query (VALUE self, VALUE query)
2743
+ {
2744
+ CArray *ca;
2745
+ if ( ! RB_TYPE_P(query, T_STRING) || ! rb_obj_is_carray(self) ) {
2746
+ return self;
2747
+ }
2748
+ TypedData_Get_Struct(self, CArray, &carray_data_type, ca);
2749
+ if ( ca->data_type != CA_FIXLEN || RSTRING_LEN(query) <= ca->bytes ) {
2750
+ return self;
2751
+ }
2752
+ return rb_ca_to_type(self, INT2NUM(CA_FIXLEN), SIZE2NUM(RSTRING_LEN(query)));
2753
+ }
2754
+
2609
2755
  /* ca.query_match_type(query) -- ca_query_match_type with self as the
2610
2756
  reference, for the set family and locate_addr. */
2611
2757
  static VALUE
@@ -2641,7 +2787,7 @@ ca_arg_is_scalar (VALUE obj)
2641
2787
  if ( TYPE(obj) == T_SYMBOL || TYPE(obj) == T_STRING || TYPE(obj) == T_CLASS ) {
2642
2788
  return 0;
2643
2789
  }
2644
- return ca_mv_probe_data_type(obj) < 0;
2790
+ return ! rb_memory_view_available_p(obj);
2645
2791
  }
2646
2792
 
2647
2793
  /* The type a scalar `obj` (of type `st`) and an array of type `at` share,
@@ -2852,6 +2998,8 @@ rb_ca_cast_other (VALUE *self, volatile VALUE *other)
2852
2998
  *other = ca_operand_from_memory_view(*other);
2853
2999
  }
2854
3000
 
3001
+ ca_array_operand(other, *self, 0);
3002
+
2855
3003
  if ( ! rb_obj_is_carray(*other) ) {
2856
3004
  if ( rb_ca_is_object_type(*self) ) {
2857
3005
  *other = rb_cscalar_new_with_value(CA_OBJECT, 0, *other);
@@ -2894,7 +3042,9 @@ rb_ca_cast_other (VALUE *self, volatile VALUE *other)
2894
3042
  return;
2895
3043
  }
2896
3044
 
2897
- rb_raise(rb_eTypeError,
3045
+ /* A data type the operation does not take, as `sqrt!` on an integer
3046
+ array is refused (CArray::DataTypeError is a TypeError). */
3047
+ rb_raise(rb_eCADataTypeError,
2898
3048
  "can't coerce carray with data_types of '%s' and '%s'",
2899
3049
  ca_coerce_operand_name(*self, ca),
2900
3050
  ca_coerce_operand_name(*other, cb));
@@ -2920,78 +3070,89 @@ rb_ca_cast_with (VALUE self, VALUE other)
2920
3070
  /* -----------------------------------------------------------------------
2921
3071
  clip_<type> family — clip into the target data type's representable
2922
3072
  range, then cast. Sugar for `clip(lo, hi).as_<type>`.
3073
+
3074
+ A float source takes a saturating path instead. The type's maximum is
3075
+ not always exact in the source float (INT64_MAX and UINT64_MAX round up
3076
+ in float64, INT32_MAX and UINT32_MAX in float32), so clip-then-cast
3077
+ would hand the cast a value just past the range. Instead each cell is
3078
+ compared in double against the range the cast truncates into
3079
+ (ca_double_fits_integer) and saturates to the type's minimum or maximum
3080
+ outside it. A NaN that is not masked is refused.
2923
3081
  ----------------------------------------------------------------------- */
2924
3082
 
2925
- #define CLIP_CAST_FUNC(name, lo_expr, hi_expr, cast_func) \
3083
+ static VALUE
3084
+ ca_clip_float_to_integer (VALUE self, int8_t to_dt)
3085
+ {
3086
+ CArray *ca;
3087
+ GetCArray(self, ca);
3088
+ volatile VALUE vsrc = ( ca->data_type == CA_FLOAT64 ) ? self : rb_ca_as_float64(self);
3089
+ CArray *src;
3090
+ GetCArray(vsrc, src);
3091
+ volatile VALUE vout = rb_carray_new(to_dt, ca->ndim, ca->dim, 0, NULL);
3092
+ CArray *out;
3093
+ GetCArray(vout, out);
3094
+ ca_attach(src); /* window: nothing raises inside */
3095
+ double *sp = (double *) src->ptr;
3096
+ char *op = out->ptr;
3097
+ ca_size_t n = ca->elements;
3098
+ boolean8_t *m = ca_has_mask(src) ? (boolean8_t *) src->mask->ptr : NULL; /* attached with src */
3099
+ int found_nan = 0;
3100
+ for ( ca_size_t i = 0; i < n; i++ ) {
3101
+ double v = sp[i];
3102
+ int fits = ca_double_fits_integer(v, to_dt);
3103
+ if ( v != v ) { /* NaN has no integer: refused below */
3104
+ v = 0.0;
3105
+ fits = 1;
3106
+ if ( ! m || ! m[i] ) found_nan = 1;
3107
+ }
3108
+ #define CLIP_STORE(type, lo, hi) \
3109
+ ((type *) op)[i] = fits ? (type) v : ( v > 0 ? (hi) : (lo) )
3110
+ switch ( to_dt ) {
3111
+ case CA_INT8: CLIP_STORE(int8_t, INT8_MIN, INT8_MAX); break;
3112
+ case CA_UINT8: CLIP_STORE(uint8_t, 0, UINT8_MAX); break;
3113
+ case CA_INT16: CLIP_STORE(int16_t, INT16_MIN, INT16_MAX); break;
3114
+ case CA_UINT16: CLIP_STORE(uint16_t, 0, UINT16_MAX); break;
3115
+ case CA_INT32: CLIP_STORE(int32_t, INT32_MIN, INT32_MAX); break;
3116
+ case CA_UINT32: CLIP_STORE(uint32_t, 0, UINT32_MAX); break;
3117
+ case CA_INT64: CLIP_STORE(int64_t, INT64_MIN, INT64_MAX); break;
3118
+ case CA_UINT64: CLIP_STORE(uint64_t, 0, UINT64_MAX); break;
3119
+ }
3120
+ #undef CLIP_STORE
3121
+ }
3122
+ ca_detach(src);
3123
+ if ( found_nan ) {
3124
+ rb_raise(rb_eFloatDomainError, "%s: NaN cannot be converted to %s",
3125
+ ca_calling_method_name(), ca_type_name[to_dt]);
3126
+ }
3127
+ if ( ca_has_mask(ca) ) {
3128
+ ca_copy_mask(out, ca);
3129
+ }
3130
+ return vout;
3131
+ }
3132
+
3133
+ #define CLIP_CAST_FUNC(name, to_dt, lo_expr, hi_expr, cast_func) \
2926
3134
  static VALUE \
2927
3135
  rb_ca_##name (VALUE self) \
2928
3136
  { \
3137
+ CArray *ca; \
3138
+ GetCArray(self, ca); \
3139
+ if ( ca->data_type == CA_FLOAT32 || ca->data_type == CA_FLOAT64 ) { \
3140
+ return ca_clip_float_to_integer(self, to_dt); \
3141
+ } \
2929
3142
  return cast_func(rb_ca_clip(self, lo_expr, hi_expr)); \
2930
3143
  }
2931
3144
 
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)
3145
+ CLIP_CAST_FUNC(clip_int8, CA_INT8, INT2NUM(-128), INT2NUM(127), rb_ca_as_int8)
3146
+ CLIP_CAST_FUNC(clip_int16, CA_INT16, INT2NUM(-32768), INT2NUM(32767), rb_ca_as_int16)
3147
+ CLIP_CAST_FUNC(clip_int32, CA_INT32, INT2NUM(-2147483648), INT2NUM(2147483647), rb_ca_as_int32)
3148
+ CLIP_CAST_FUNC(clip_int64, CA_INT64, LL2NUM(-9223372036854775807-1), LL2NUM(9223372036854775807), rb_ca_as_int64)
3149
+ CLIP_CAST_FUNC(clip_uint8, CA_UINT8, INT2NUM(0), INT2NUM(255), rb_ca_as_uint8)
3150
+ CLIP_CAST_FUNC(clip_uint16, CA_UINT16, INT2NUM(0), INT2NUM(65535), rb_ca_as_uint16)
3151
+ CLIP_CAST_FUNC(clip_uint32, CA_UINT32, INT2NUM(0), UINT2NUM(4294967295U), rb_ca_as_uint32)
3152
+ CLIP_CAST_FUNC(clip_uint64, CA_UINT64, INT2NUM(0), ULL2NUM(18446744073709551615ULL), rb_ca_as_uint64)
2939
3153
 
2940
3154
  #undef CLIP_CAST_FUNC
2941
3155
 
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
3156
  /* ------------------------------------------------------------------------
2996
3157
  Global cast functions CA_INT32(data) ... CA_OBJECT(data) etc.
2997
3158
 
@@ -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); }
@@ -30,14 +30,14 @@
30
30
  /* Leaf macro: data type-specialized inner loop. Produces `n` VALUE objects
31
31
  from a contig ptr base + element width, storing into `dst` (Ruby Array). */
32
32
  #define TO_A_LEAF_LOOP(T, TO_VALUE) do { \
33
- const T *src = (const T *) base; \
33
+ const CA_U(T) *src = (const CA_U(T) *) base; \
34
34
  for (li = 0; li < n; li++) { \
35
35
  rb_ary_store(dst, li, TO_VALUE(src[li])); \
36
36
  } \
37
37
  } while (0)
38
38
 
39
39
  #define TO_A_LEAF_LOOP_MASKED(T, TO_VALUE) do { \
40
- const T *src = (const T *) base; \
40
+ const CA_U(T) *src = (const CA_U(T) *) base; \
41
41
  for (li = 0; li < n; li++) { \
42
42
  rb_ary_store(dst, li, mask[li] ? CA_UNDEF : TO_VALUE(src[li])); \
43
43
  } \
data/ext/carray_core.c CHANGED
@@ -2955,6 +2955,9 @@ ca_face_field_lookup (VALUE self, VALUE sym)
2955
2955
  vtype_sym = ID2SYM(rb_intern(vtype_name));
2956
2956
  word_view = rb_funcall(receiver, rb_intern("field"), 2,
2957
2957
  SIZE2NUM(start_byte), vtype_sym);
2958
+ /* little-endian on every host, as the layout numbers the bits */
2959
+ word_view = rb_funcall(word_view, rb_intern("endian"), 1,
2960
+ ID2SYM(rb_intern("little")));
2958
2961
  range = rb_range_new(LONG2NUM(bit_in_word),
2959
2962
  SIZE2NUM(bit_in_word + bits - 1), 0);
2960
2963
  obj = rb_funcall(word_view, rb_intern("bitfield"), 1, range);
data/ext/carray_count.c CHANGED
@@ -252,8 +252,10 @@ rb_ca_count (int argc, VALUE *argv, VALUE self)
252
252
  int8_t slab_axes[CA_RANK_MAX];
253
253
  int8_t naxes;
254
254
  VALUE axis_val = Qnil;
255
+ int keep_axis = 0;
255
256
  if ( ! NIL_P(opt_hash) ) {
256
257
  axis_val = rb_hash_lookup2(opt_hash, ID2SYM(rb_intern("axis")), Qnil);
258
+ keep_axis = RTEST(rb_hash_lookup2(opt_hash, ID2SYM(rb_intern("keep_axis")), Qfalse));
257
259
  }
258
260
  naxes = rb_ca_parse_reduce_axes_kw(axis_val, src, slab_axes);
259
261
  (void) inner_axes; /* axes no longer parsed from inner_axes */
@@ -270,6 +272,9 @@ rb_ca_count (int argc, VALUE *argv, VALUE self)
270
272
  out_dim[out_ndim++] = src->dim[k];
271
273
  base_elements *= src->dim[k];
272
274
  }
275
+ else if ( keep_axis ) {
276
+ out_dim[out_ndim++] = 1; /* the reduced axis kept at length 1 */
277
+ }
273
278
  }
274
279
  /* Full reduction (naxes == ndim): base_elements = 1, no base axes. */
275
280
  /* Append v.shape. */
@@ -377,6 +382,7 @@ rb_ca_count (int argc, VALUE *argv, VALUE self)
377
382
  self = rb_ca_wrap_readonly(self, INT2NUM(qt));
378
383
  }
379
384
  }
385
+ self = ca_fixlen_widen_for_query(self, rval);
380
386
  /* Pass full argv; count_equal_ki pops argv[0] as value_arg. */
381
387
  return rb_ca_count_equal_ki(argc, argv, self);
382
388
  }