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
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
  }
data/ext/carray_element.c CHANGED
@@ -20,17 +20,22 @@
20
20
  #include "ca_obj_face.h"
21
21
  #include "carray_internal.h" /* ca_obj_extremum_replaces */
22
22
 
23
- /* An element index is an Integer, as it is for `[]`; a Float would be cut
24
- to an integer by NUM2SIZE. */
23
+ /* An element index is read as `[]` reads a key: an Integer, or a Float
24
+ with no fraction. */
25
25
  static inline ca_size_t
26
26
  elem_index_value (VALUE v)
27
27
  {
28
+ ca_size_t k;
28
29
  if ( FIXNUM_P(v) ) return (ca_size_t) FIX2LONG(v);
29
- if ( ! RB_INTEGER_TYPE_P(v) ) {
30
+ switch ( ca_index_key_position(v, &k) ) {
31
+ case 1:
32
+ return k;
33
+ case 2:
34
+ return CA_LENGTH_MAX; /* past int64: past every axis, refused as out of range */
35
+ default:
30
36
  rb_raise(rb_eIndexError, "object '%"PRIsVALUE"' is invalid for an index",
31
37
  rb_inspect(v));
32
38
  }
33
- return NUM2SIZE(v);
34
39
  }
35
40
 
36
41
  /* T_ARRAY index parser. ridx is assumed already known to be T_ARRAY.
@@ -77,8 +82,9 @@ elem_probe_mask (CArray *ca)
77
82
 
78
83
  /* Entity-array fast path: compute flat addr from idx[] and access ptr directly.
79
84
  Bypasses ca_func[ARRAY].xfer_index function-pointer dispatch.
80
- Caller must ensure ca_is_entity(ca) and ca->ptr is attached (= entity
81
- arrays are always attached by definition). */
85
+ Caller must ensure ca_is_entity(ca) and ca->ptr != NULL: an entity can
86
+ leave ptr NULL at rest (a CASource), and then the dispatched path is
87
+ the way in. */
82
88
  static inline ca_size_t
83
89
  elem_entity_addr (CArray *ca, ca_size_t *idx)
84
90
  {
@@ -268,7 +274,7 @@ rb_ca_elem_store (VALUE self, VALUE ridx, VALUE obj)
268
274
  Bypasses rb_ca_store_index/addr -> ca_update_mask + rb_ca_obj2ptr ->
269
275
  cast_table dispatch. Falls through to general path for: views,
270
276
  existing mask, non-numeric data type, UNDEF, or non-Numeric obj. */
271
- if ( ca_is_entity(ca) && ! ca->mask && obj != CA_UNDEF ) {
277
+ if ( ca_is_entity(ca) && ca->ptr && ! ca->mask && obj != CA_UNDEF ) {
272
278
  int8_t dt = ca->data_type;
273
279
  if ( dt == CA_FLOAT64 || dt == CA_FLOAT32 ||
274
280
  dt == CA_INT64 || dt == CA_INT32 ||
@@ -378,7 +384,7 @@ rb_ca_elem_fetch (VALUE self, VALUE ridx)
378
384
  /* Super-hot path: entity + common numeric data type + no mask.
379
385
  Bypass ca_fetch_index dispatch AND rb_ca_ptr2obj_storage cast-table dispatch.
380
386
  Covers the bench's float64 / int64 / int32 cases. */
381
- if ( ca_is_entity(ca) && ! ca->mask ) {
387
+ if ( ca_is_entity(ca) && ca->ptr && ! ca->mask ) {
382
388
  ca_size_t addr = elem_entity_addr(ca, idx);
383
389
  char *p = ca->ptr + ca->bytes * addr;
384
390
  switch ( ca->data_type ) {
@@ -401,7 +407,7 @@ rb_ca_elem_fetch (VALUE self, VALUE ridx)
401
407
  TypedData_Get_Struct). Semantics identical to rb_ca_fetch_index. */
402
408
  if ( ca->bytes <= 64 ) {
403
409
  char v[64];
404
- if ( ca_is_entity(ca) ) {
410
+ if ( ca_is_entity(ca) && ca->ptr ) {
405
411
  ca_size_t addr = elem_entity_addr(ca, idx);
406
412
  memcpy(v, ca->ptr + ca->bytes * addr, ca->bytes);
407
413
  }
@@ -434,12 +440,15 @@ rb_ca_elem_fetch (VALUE self, VALUE ridx)
434
440
  }
435
441
 
436
442
  CA_FACE_STORAGE_TO_ELEMENT_IF_FACE(out, self, ca);
443
+ if ( ca->data_type == CA_BOOLEAN ) {
444
+ out = RTEST(out) && out != INT2FIX(0) ? Qtrue : Qfalse;
445
+ }
437
446
  return out;
438
447
  }
439
448
  else {
440
449
  ca_size_t addr = elem_parse_idx_scalar(ca, ridx);
441
450
  /* Super-hot flat-addr path: entity + numeric + no mask. */
442
- if ( ca_is_entity(ca) && ! ca->mask ) {
451
+ if ( ca_is_entity(ca) && ca->ptr && ! ca->mask ) {
443
452
  char *p = ca->ptr + ca->bytes * addr;
444
453
  switch ( ca->data_type ) {
445
454
  case CA_FLOAT64: return rb_float_new(*(double *)p);
@@ -488,7 +497,7 @@ rb_ca_elem_incr (VALUE self, VALUE ridx1)
488
497
 
489
498
  /* Super-hot path: entity + no mask + integer data type.
490
499
  Reads p directly, modifies in place — no separate fetch/store dispatch. */
491
- if ( ca_is_entity(ca) && ! has_mask ) {
500
+ if ( ca_is_entity(ca) && ca->ptr && ! has_mask ) {
492
501
  ca_size_t addr;
493
502
  if ( TYPE(ridx1) == T_ARRAY ) {
494
503
  elem_parse_idx_array(ca, ridx1, idx1);
@@ -578,7 +587,7 @@ rb_ca_elem_decr (VALUE self, VALUE ridx1)
578
587
  has_mask = elem_probe_mask(ca);
579
588
 
580
589
  /* Super-hot path: mirror of elem_incr. */
581
- if ( ca_is_entity(ca) && ! has_mask ) {
590
+ if ( ca_is_entity(ca) && ca->ptr && ! has_mask ) {
582
591
  ca_size_t addr;
583
592
  if ( TYPE(ridx1) == T_ARRAY ) {
584
593
  elem_parse_idx_array(ca, ridx1, idx1);
@@ -793,7 +802,7 @@ rb_ca_elem_unmask (VALUE self, VALUE ridx)
793
802
  general path, which compares in Ruby and stores as `self[idx] = v`
794
803
  does, so a value that wins but does not fit raises. */
795
804
  #define ELEM_MINMAX_BODY(OP) do { \
796
- if ( ca_is_entity(ca) ) { \
805
+ if ( ca_is_entity(ca) && ca->ptr ) { \
797
806
  ca_size_t addr; \
798
807
  if ( TYPE(ridx) == T_ARRAY ) { \
799
808
  elem_parse_idx_array(ca, ridx, idx); \
@@ -865,7 +865,7 @@ fz_factorize_appearance_body (VALUE arg)
865
865
  element to a 64-bit key; equality within one data type is preserved. */
866
866
  #define FZ_LOOP(T, WIDEN) \
867
867
  do { \
868
- const T *ip = (const T *) p_in; \
868
+ const CA_U(T) *ip = (const CA_U(T) *) p_in; \
869
869
  uint32_t *op = (uint32_t *) p_out; \
870
870
  for ( ca_size_t i = 0; i < n; i++ ) { \
871
871
  if ( m && m[i] ) { op[i] = 0xFFFFFFFFu; continue; } \
@@ -884,7 +884,7 @@ fz_factorize_appearance_body (VALUE arg)
884
884
  kept as-is), so masked source cells store the sentinel. */
885
885
  #define FZ_LOOP_FLOAT(T) \
886
886
  do { \
887
- const T *ip = (const T *) p_in; \
887
+ const CA_U(T) *ip = (const CA_U(T) *) p_in; \
888
888
  uint32_t *op = (uint32_t *) p_out; \
889
889
  for ( ca_size_t i = 0; i < n; i++ ) { \
890
890
  if ( m && m[i] ) { op[i] = 0xFFFFFFFFu; continue; } \
@@ -1073,7 +1073,7 @@ fz_mask_duplicates_body (VALUE arg)
1073
1073
  interns first appearances, so is_new == 0 flags a duplicate. */
1074
1074
  #define MD_LOOP(T, WIDEN) \
1075
1075
  do { \
1076
- const T *ip = (const T *) p_in; \
1076
+ const CA_U(T) *ip = (const CA_U(T) *) p_in; \
1077
1077
  boolean8_t *op = (boolean8_t *) p_out; \
1078
1078
  for ( ca_size_t i = 0; i < n; i++ ) { \
1079
1079
  if ( m && m[i] ) { op[i] = 0; continue; } \
@@ -1091,7 +1091,7 @@ fz_mask_duplicates_body (VALUE arg)
1091
1091
  before the bitwise key. */
1092
1092
  #define MD_LOOP_FLOAT(T) \
1093
1093
  do { \
1094
- const T *ip = (const T *) p_in; \
1094
+ const CA_U(T) *ip = (const CA_U(T) *) p_in; \
1095
1095
  boolean8_t *op = (boolean8_t *) p_out; \
1096
1096
  for ( ca_size_t i = 0; i < n; i++ ) { \
1097
1097
  if ( m && m[i] ) { op[i] = 0; continue; } \
@@ -1225,7 +1225,7 @@ fz_unique_flat_body (VALUE arg)
1225
1225
 
1226
1226
  #define UQ_LOOP(T, WIDEN) \
1227
1227
  do { \
1228
- const T *ip = (const T *) p_in; \
1228
+ const CA_U(T) *ip = (const CA_U(T) *) p_in; \
1229
1229
  for ( ca_size_t i = 0; i < n; i++ ) { \
1230
1230
  if ( m && m[i] ) { continue; } \
1231
1231
  uint64_t key = (uint64_t) (WIDEN ip[i]); \
@@ -1239,7 +1239,7 @@ fz_unique_flat_body (VALUE arg)
1239
1239
  pushed level is the raw first-seen element. */
1240
1240
  #define UQ_LOOP_FLOAT(T) \
1241
1241
  do { \
1242
- const T *ip = (const T *) p_in; \
1242
+ const CA_U(T) *ip = (const CA_U(T) *) p_in; \
1243
1243
  for ( ca_size_t i = 0; i < n; i++ ) { \
1244
1244
  if ( m && m[i] ) { continue; } \
1245
1245
  T v = ip[i]; \
@@ -1340,7 +1340,7 @@ fz_intern_all (ca_iter_state *st_v, fz_hash *h, CArray *cv, fz_levels *lv)
1340
1340
 
1341
1341
  #define FZ_IA(T, WIDEN) \
1342
1342
  do { \
1343
- const T *ip = (const T *) p_v; \
1343
+ const CA_U(T) *ip = (const CA_U(T) *) p_v; \
1344
1344
  for ( ca_size_t i = 0; i < nv; i++ ) { \
1345
1345
  if ( mv && mv[i] ) { continue; } \
1346
1346
  uint64_t key = (uint64_t) (WIDEN ip[i]); \
@@ -1352,7 +1352,7 @@ fz_intern_all (ca_iter_state *st_v, fz_hash *h, CArray *cv, fz_levels *lv)
1352
1352
 
1353
1353
  #define FZ_IA_FLOAT(T) \
1354
1354
  do { \
1355
- const T *ip = (const T *) p_v; \
1355
+ const CA_U(T) *ip = (const CA_U(T) *) p_v; \
1356
1356
  for ( ca_size_t i = 0; i < nv; i++ ) { \
1357
1357
  if ( mv && mv[i] ) { continue; } \
1358
1358
  T v = ip[i]; \
@@ -1493,7 +1493,7 @@ fz_is_in_body (VALUE arg)
1493
1493
 
1494
1494
  #define IN_PROBE(T, WIDEN) \
1495
1495
  do { \
1496
- const T *ip = (const T *) p_in; \
1496
+ const CA_U(T) *ip = (const CA_U(T) *) p_in; \
1497
1497
  boolean8_t *op = (boolean8_t *) p_out; \
1498
1498
  for ( ca_size_t i = 0; i < n; i++ ) { \
1499
1499
  if ( m && m[i] ) { op[i] = 0; continue; } \
@@ -1504,7 +1504,7 @@ fz_is_in_body (VALUE arg)
1504
1504
 
1505
1505
  #define IN_PROBE_FLOAT(T) \
1506
1506
  do { \
1507
- const T *ip = (const T *) p_in; \
1507
+ const CA_U(T) *ip = (const CA_U(T) *) p_in; \
1508
1508
  boolean8_t *op = (boolean8_t *) p_out; \
1509
1509
  for ( ca_size_t i = 0; i < n; i++ ) { \
1510
1510
  if ( m && m[i] ) { op[i] = 0; continue; } \
@@ -1661,7 +1661,7 @@ fz_locate_addr_body (VALUE arg)
1661
1661
 
1662
1662
  #define LOC_BUILD(T, WIDEN) \
1663
1663
  do { \
1664
- const T *ip = (const T *) p_r; \
1664
+ const CA_U(T) *ip = (const CA_U(T) *) p_r; \
1665
1665
  for ( ca_size_t i = 0; i < nr; i++ ) { \
1666
1666
  if ( mr && mr[i] ) { continue; } \
1667
1667
  uint64_t key = (uint64_t) (WIDEN ip[i]); \
@@ -1674,7 +1674,7 @@ fz_locate_addr_body (VALUE arg)
1674
1674
 
1675
1675
  #define LOC_BUILD_FLOAT(T) \
1676
1676
  do { \
1677
- const T *ip = (const T *) p_r; \
1677
+ const CA_U(T) *ip = (const CA_U(T) *) p_r; \
1678
1678
  for ( ca_size_t i = 0; i < nr; i++ ) { \
1679
1679
  if ( mr && mr[i] ) { continue; } \
1680
1680
  T v = ip[i]; \
@@ -1751,7 +1751,7 @@ fz_locate_addr_body (VALUE arg)
1751
1751
 
1752
1752
  #define LOC_PROBE(T, WIDEN) \
1753
1753
  do { \
1754
- const T *ip = (const T *) p_in; \
1754
+ const CA_U(T) *ip = (const CA_U(T) *) p_in; \
1755
1755
  const int64_t *ab = (const int64_t *) addr.p; \
1756
1756
  for ( ca_size_t i = 0; i < n; i++ ) { \
1757
1757
  ca_size_t o = base + i; \
@@ -1765,7 +1765,7 @@ fz_locate_addr_body (VALUE arg)
1765
1765
 
1766
1766
  #define LOC_PROBE_FLOAT(T) \
1767
1767
  do { \
1768
- const T *ip = (const T *) p_in; \
1768
+ const CA_U(T) *ip = (const CA_U(T) *) p_in; \
1769
1769
  const int64_t *ab = (const int64_t *) addr.p; \
1770
1770
  for ( ca_size_t i = 0; i < n; i++ ) { \
1771
1771
  ca_size_t o = base + i; \
@@ -1929,7 +1929,7 @@ fz_set_relation_body (VALUE arg)
1929
1929
 
1930
1930
  #define SR(T, WIDEN) \
1931
1931
  do { \
1932
- const T *ip = (const T *) p; \
1932
+ const CA_U(T) *ip = (const CA_U(T) *) p; \
1933
1933
  for ( ca_size_t i = 0; i < n; i++ ) { \
1934
1934
  if ( m && m[i] ) { continue; } \
1935
1935
  uint64_t key = (uint64_t) (WIDEN ip[i]); \
@@ -1943,7 +1943,7 @@ fz_set_relation_body (VALUE arg)
1943
1943
 
1944
1944
  #define SR_FLOAT(T) \
1945
1945
  do { \
1946
- const T *ip = (const T *) p; \
1946
+ const CA_U(T) *ip = (const CA_U(T) *) p; \
1947
1947
  for ( ca_size_t i = 0; i < n; i++ ) { \
1948
1948
  if ( m && m[i] ) { continue; } \
1949
1949
  T v = ip[i]; \
@@ -2188,7 +2188,7 @@ fz_value_counts_flat_body (VALUE arg)
2188
2188
 
2189
2189
  #define VC_LOOP(T, WIDEN) \
2190
2190
  do { \
2191
- const T *ip = (const T *) p_in; \
2191
+ const CA_U(T) *ip = (const CA_U(T) *) p_in; \
2192
2192
  for ( ca_size_t i = 0; i < n; i++ ) { \
2193
2193
  if ( m && m[i] ) { continue; } \
2194
2194
  uint64_t key = (uint64_t) (WIDEN ip[i]); \
@@ -2206,7 +2206,7 @@ fz_value_counts_flat_body (VALUE arg)
2206
2206
 
2207
2207
  #define VC_LOOP_FLOAT(T) \
2208
2208
  do { \
2209
- const T *ip = (const T *) p_in; \
2209
+ const CA_U(T) *ip = (const CA_U(T) *) p_in; \
2210
2210
  for ( ca_size_t i = 0; i < n; i++ ) { \
2211
2211
  if ( m && m[i] ) { continue; } \
2212
2212
  T v = ip[i]; \
@@ -2516,7 +2516,7 @@ fz_is_mode_body (VALUE arg)
2516
2516
  /* Integer: WIDEN gives the key directly. Two passes over the fiber. */
2517
2517
  #define IM_BODY(T, WIDEN) \
2518
2518
  do { \
2519
- const T *ip = (const T *) p_in; \
2519
+ const CA_U(T) *ip = (const CA_U(T) *) p_in; \
2520
2520
  boolean8_t *op = (boolean8_t *) p_out; \
2521
2521
  for ( ca_size_t i = 0; i < n; i++ ) { \
2522
2522
  if ( m && m[i] ) { continue; } \
@@ -2543,7 +2543,7 @@ fz_is_mode_body (VALUE arg)
2543
2543
  /* Float: NaN collapses to one canonical key; -0.0 / +0.0 share a key. */
2544
2544
  #define IM_BODY_FLOAT(T) \
2545
2545
  do { \
2546
- const T *ip = (const T *) p_in; \
2546
+ const CA_U(T) *ip = (const CA_U(T) *) p_in; \
2547
2547
  boolean8_t *op = (boolean8_t *) p_out; \
2548
2548
  for ( int pass = 0; pass < 2; pass++ ) { \
2549
2549
  int64_t mx = 0; \
@@ -117,7 +117,7 @@ ca_seq_is_real (VALUE v)
117
117
  /* Fill the whole array in row-major order with `step*i + offset`. */
118
118
  #define proc_seq_bang(type, from, to) \
119
119
  { \
120
- type *p = (type *)ca->ptr; \
120
+ CA_U(type) *p = (CA_U(type) *)ca->ptr; \
121
121
  ca_size_t i; \
122
122
  if ( NIL_P(roffset) && NIL_P(rstep) ) { \
123
123
  for (i=0; i<ca->elements; i++) { \
@@ -149,7 +149,7 @@ ca_seq_is_real (VALUE v)
149
149
  `outer_size` come from the enclosing scope (ca_seq_geometry). */
150
150
  #define proc_seq_bang_axis(type, from, to) \
151
151
  { \
152
- type *p = (type *)ca->ptr; \
152
+ CA_U(type) *p = (CA_U(type) *)ca->ptr; \
153
153
  ca_size_t o, k, in; \
154
154
  if ( ca_seq_is_real(rstep) || ca_seq_is_real(roffset) ) { \
155
155
  double offset = (NIL_P(roffset)) ? 0 : NUM2DBL(roffset); \
@@ -289,6 +289,9 @@ ca_seq_bang_object_body (VALUE arg)
289
289
  }
290
290
  else if ( ! NIL_P(rmethod) ) { /* a.seq(obj, :method) */
291
291
  ID id_method = SYM2ID(rmethod);
292
+ if ( ca->elements == 0 ) { /* no first cell to write */
293
+ return Qnil;
294
+ }
292
295
  *p++ = ca_seq_cell(c, rval = roffset);
293
296
  for (i=1; i<ca->elements; i++) {
294
297
  *p++ = ca_seq_cell(c, rval = rb_funcall(rval, id_method, 0));
@@ -297,6 +300,9 @@ ca_seq_bang_object_body (VALUE arg)
297
300
  else { /* a.seq(obj, step) */
298
301
  ID id_plus = rb_intern("+");
299
302
  rstep = ( NIL_P(rstep) ) ? INT2NUM(1) : rstep;
303
+ if ( ca->elements == 0 ) { /* no first cell to write */
304
+ return Qnil;
305
+ }
300
306
  *p++ = ca_seq_cell(c, rval = roffset);
301
307
  for (i=1; i<ca->elements; i++) {
302
308
  *p++ = ca_seq_cell(c, rval = rb_funcall(rval, id_plus, 1, rstep));
@@ -25,6 +25,7 @@
25
25
  #include "carray.h"
26
26
  #include "carray_internal.h" /* ca_attach_window */
27
27
  #include <math.h>
28
+ #include <float.h>
28
29
 
29
30
  /* Edges are the bin boundaries and must read as such: no masked edge (its
30
31
  stored value is no boundary), no NaN, and no edge below the one before
@@ -88,6 +89,8 @@ typedef struct {
88
89
  const double *ep;
89
90
  ca_size_t n; /* number of bins */
90
91
  double lo, hi, inv_dx;
92
+ double window; /* fraction of a bin within which the
93
+ linearised floor is checked */
91
94
  int uniform;
92
95
  int include_max;
93
96
  } histbin_axis_t;
@@ -115,7 +118,7 @@ histbin_index (const histbin_axis_t *ax, double v)
115
118
  Interior values (the bulk) skip the data-dependent edge loads, keeping
116
119
  the fast path vectorisable. */
117
120
  frac = pos - (double) k;
118
- if ( frac < 1e-9 || frac > 1.0 - 1e-9 ) {
121
+ if ( frac < ax->window || frac > 1.0 - ax->window ) {
119
122
  while ( k + 1 < ax->n && v >= ax->ep[k + 1] ) k++;
120
123
  while ( k > 0 && v < ax->ep[k] ) k--;
121
124
  }
@@ -136,7 +139,7 @@ histbin_axis_setup (histbin_axis_t *ax, const double *ep, ca_size_t ne,
136
139
  int include_max)
137
140
  {
138
141
  ca_size_t kk;
139
- double dx, tol;
142
+ double dx, tol, maxdev;
140
143
 
141
144
  ax->ep = ep;
142
145
  ax->n = ne - 1;
@@ -149,8 +152,27 @@ histbin_axis_setup (histbin_axis_t *ax, const double *ep, ca_size_t ne,
149
152
  width infinite, and a subnormal width makes the reciprocal infinite.
150
153
  Either turns the position into NaN, which has no integer. */
151
154
  ax->uniform = isfinite(dx) && dx != 0.0 && isfinite(1.0 / dx);
155
+ maxdev = 0.0;
152
156
  for ( kk = 0; ax->uniform && kk <= ax->n; kk++ ) {
153
- if ( fabs(ax->ep[kk] - (ax->lo + (double) kk * dx)) > tol ) { ax->uniform = 0; break; }
157
+ double dev = fabs(ax->ep[kk] - (ax->lo + (double) kk * dx));
158
+ if ( dev > tol ) { ax->uniform = 0; break; }
159
+ if ( dev > maxdev ) maxdev = dev;
160
+ }
161
+ /* A value can land on the wrong side of the linearised floor by as much
162
+ as an edge strays from the straight line, plus the rounding of the
163
+ position itself: v - lo is rounded at the scale of the edges' magnitude
164
+ (an ulp of lo or hi, which can be most of a bin when the edges are
165
+ close together far from zero), and the product grows with the number
166
+ of bins. Values within that much of a boundary are checked against the
167
+ stored edges. When the window covers a good part of a bin, the binary
168
+ search is cheaper. */
169
+ ax->window = ( ax->uniform )
170
+ ? maxdev / fabs(dx)
171
+ + 4.0 * DBL_EPSILON * (fabs(ax->lo) + fabs(ax->hi)) / fabs(dx)
172
+ + (double) (ax->n + 2) * 8.0 * DBL_EPSILON + 1e-9
173
+ : 0.0;
174
+ if ( ax->window > 0.25 ) {
175
+ ax->uniform = 0;
154
176
  }
155
177
  ax->inv_dx = (dx != 0.0) ? 1.0 / dx : 0.0;
156
178
  ax->include_max = include_max;
@@ -312,7 +334,7 @@ histogram_scatter_walk (VALUE arg)
312
334
  ca_size_t off = fiber_base;
313
335
  int masked = 0;
314
336
  for ( kk = 0; kk < (ca_size_t) M; kk++ ) {
315
- double v = *(const double *)(p + a * ss + kk * cs);
337
+ double v = *(const ca_u_float64_t *)(p + a * ss + kk * cs);
316
338
  if ( (m && m[a * sms + kk * cms]) || isnan(v) ) { masked = 1; break; }
317
339
  off += histbin_index(&axes[kk], v) * ext_stride[kk];
318
340
  }
@@ -164,8 +164,8 @@ ca_classifier_key_too_large (VALUE v, const char *where)
164
164
  Returns 1 with the position in *out, 2 for an Integer past int64 (past
165
165
  every axis, which is no longer than int64), and 0 for anything else (a
166
166
  Float with a fraction or past 2**53, NaN, Inf, a Rational, a String). */
167
- static int
168
- ca_classifier_key_position (VALUE v, ca_size_t *out)
167
+ int
168
+ ca_index_key_position (VALUE v, ca_size_t *out)
169
169
  {
170
170
  if ( FIXNUM_P(v) ) {
171
171
  *out = (ca_size_t) FIX2LONG(v);
@@ -201,7 +201,7 @@ static ca_size_t
201
201
  ca_classifier_key_int_where (VALUE v, int axis, const char *where)
202
202
  {
203
203
  ca_size_t k;
204
- switch ( ca_classifier_key_position(v, &k) ) {
204
+ switch ( ca_index_key_position(v, &k) ) {
205
205
  case 1:
206
206
  return k;
207
207
  case 2:
@@ -232,7 +232,7 @@ static ca_size_t
232
232
  ca_classifier_key_step (VALUE v, int axis)
233
233
  {
234
234
  ca_size_t k;
235
- if ( ca_classifier_key_position(v, &k) == 2 ) {
235
+ if ( ca_index_key_position(v, &k) == 2 ) {
236
236
  return rb_big_sign(v) ? (ca_size_t) INT64_MAX : (ca_size_t) INT64_MIN;
237
237
  }
238
238
  return ca_classifier_key_int(v, axis);