carray 3.0.3 → 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 (221) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +469 -0
  3. data/README.md +3 -2
  4. data/carray.gemspec +1 -1
  5. data/ext/ca_array_pool.c +25 -1
  6. data/ext/ca_axis_descriptor.h +27 -24
  7. data/ext/ca_axis_dispatch.c +14 -26
  8. data/ext/ca_axis_group.c +27 -27
  9. data/ext/ca_binop_dispatch.c +1 -29
  10. data/ext/ca_binop_dispatch.h +0 -1
  11. data/ext/ca_categorical_iterator.c +231 -153
  12. data/ext/ca_compare.c +9 -20
  13. data/ext/ca_composite_dispatch.c +9 -9
  14. data/ext/ca_group_iter.c +16 -17
  15. data/ext/ca_kernel_iterator.c +1240 -2000
  16. data/ext/ca_kernel_iterator.h +502 -519
  17. data/ext/ca_monop_dispatch.c +82 -59
  18. data/ext/ca_monop_dispatch.h +1 -0
  19. data/ext/ca_obj_array.c +152 -45
  20. data/ext/ca_obj_bincmp.c +25 -21
  21. data/ext/ca_obj_binop.c +16 -26
  22. data/ext/ca_obj_bitarray.c +8 -3
  23. data/ext/ca_obj_bitfield.c +63 -49
  24. data/ext/ca_obj_block.c +18 -9
  25. data/ext/ca_obj_byte_swap.c +7 -7
  26. data/ext/ca_obj_const_string.c +188 -22
  27. data/ext/ca_obj_face.c +280 -58
  28. data/ext/ca_obj_face.h +19 -0
  29. data/ext/ca_obj_fake.c +15 -29
  30. data/ext/ca_obj_farray.c +6 -3
  31. data/ext/ca_obj_field.c +30 -8
  32. data/ext/ca_obj_fixlen_string.c +2 -3
  33. data/ext/ca_obj_grid.c +455 -134
  34. data/ext/ca_obj_meld.c +172 -34
  35. data/ext/ca_obj_moncmp.c +23 -8
  36. data/ext/ca_obj_monop.c +113 -67
  37. data/ext/ca_obj_object.c +210 -52
  38. data/ext/ca_obj_record.c +15 -7
  39. data/ext/ca_obj_reduce.c +17 -8
  40. data/ext/ca_obj_refer.c +42 -35
  41. data/ext/ca_obj_remap.c +30 -9
  42. data/ext/ca_obj_repeat.c +9 -10
  43. data/ext/ca_obj_roll.c +31 -10
  44. data/ext/ca_obj_select.c +48 -14
  45. data/ext/ca_obj_select_axis.c +15 -47
  46. data/ext/ca_obj_shift.c +41 -7
  47. data/ext/ca_obj_stack.c +180 -34
  48. data/ext/ca_obj_stride.c +190 -134
  49. data/ext/ca_obj_string.c +80 -4
  50. data/ext/ca_obj_tile.c +32 -11
  51. data/ext/ca_obj_time.c +22 -6
  52. data/ext/ca_obj_timedelta.c +22 -6
  53. data/ext/ca_obj_transpose.c +13 -7
  54. data/ext/ca_obj_triop.c +26 -32
  55. data/ext/ca_obj_window.c +267 -74
  56. data/ext/ca_op_ipower.c +4 -4
  57. data/ext/ca_sort_kernels.h +6 -3
  58. data/ext/ca_sweep_engine.c +2 -2
  59. data/ext/caframe_csv_reader.c +80 -1
  60. data/ext/carray.h +214 -75
  61. data/ext/carray_access.c +200 -99
  62. data/ext/carray_address_basis.c +343 -87
  63. data/ext/carray_attribute.c +113 -6
  64. data/ext/carray_bincount.c +10 -8
  65. data/ext/carray_broadcast.c +13 -8
  66. data/ext/carray_build_flags.h +3 -0
  67. data/ext/carray_call_cfunc.c +108 -0
  68. data/ext/carray_cast.c +375 -96
  69. data/ext/carray_cast_func.rb +90 -20
  70. data/ext/carray_class.c +4 -3
  71. data/ext/carray_config.h +28 -0
  72. data/ext/carray_conversion.c +18 -9
  73. data/ext/carray_copy.c +51 -14
  74. data/ext/carray_core.c +150 -55
  75. data/ext/carray_count.c +56 -20
  76. data/ext/carray_element.c +81 -33
  77. data/ext/carray_factorize.c +138 -84
  78. data/ext/carray_generate.c +199 -52
  79. data/ext/carray_histogram.c +63 -18
  80. data/ext/carray_hold.c +12 -15
  81. data/ext/carray_index_classifier.c +388 -176
  82. data/ext/carray_internal.h +348 -29
  83. data/ext/carray_kernels_bincmp.c +4453 -0
  84. data/ext/carray_kernels_binop.c +11368 -0
  85. data/ext/carray_kernels_init.c +1734 -0
  86. data/ext/carray_kernels_moncmp.c +2104 -0
  87. data/ext/carray_kernels_monop.c +18369 -0
  88. data/ext/carray_kernels_reduce_aggregate.c +23818 -0
  89. data/ext/carray_kernels_reduce_boolean.c +406 -0
  90. data/ext/carray_kernels_reduce_cumulative.c +10086 -0
  91. data/ext/carray_kernels_reduce_extreme.c +17634 -0
  92. data/ext/carray_kernels_reduce_variance.c +3849 -0
  93. data/ext/carray_kernels_scan.c +4302 -0
  94. data/ext/carray_kernels_search.c +33829 -0
  95. data/ext/carray_kernels_sort.c +10897 -0
  96. data/ext/carray_kernels_triop.c +1864 -0
  97. data/ext/carray_key_windows.c +220 -0
  98. data/ext/carray_lazy.c +170 -111
  99. data/ext/carray_mask.c +337 -94
  100. data/ext/carray_median_percentile.c +82 -28
  101. data/ext/carray_memory_view.c +669 -561
  102. data/ext/carray_operator.c +129 -52
  103. data/ext/carray_order.c +604 -77
  104. data/ext/carray_partition.c +28 -27
  105. data/ext/carray_random.c +85 -31
  106. data/ext/carray_round.c +273 -0
  107. data/ext/carray_scatter.c +87 -45
  108. data/ext/carray_segment.c +3 -3
  109. data/ext/carray_slab.c +123 -37
  110. data/ext/carray_sort.c +259 -51
  111. data/ext/carray_sort_kernel.c +5 -7
  112. data/ext/carray_struct.c +54 -61
  113. data/ext/carray_test.c +26 -5
  114. data/ext/carray_time_text.c +4 -2
  115. data/ext/carray_undef.c +4 -0
  116. data/ext/carray_utils.c +309 -66
  117. data/ext/mk_call_cfunc.rb +19 -2
  118. data/ext/mkkernel.rb +403 -350
  119. data/ext/ruby_carray.c +10 -4
  120. data/ext/version.h +4 -4
  121. data/lib/carray/attribute.rb +139 -22
  122. data/lib/carray/autoload_carray.rb +8 -31
  123. data/lib/carray/autoload_method_extension.rb +14 -5
  124. data/lib/carray/axis_group.rb +255 -106
  125. data/lib/carray/basics.rb +105 -17
  126. data/lib/carray/bincount_nd.rb +12 -2
  127. data/lib/carray/block_iterator.rb +126 -23
  128. data/lib/carray/boolean_reduce.rb +17 -17
  129. data/lib/carray/categorical.rb +195 -59
  130. data/lib/carray/categorical_iterator.rb +132 -29
  131. data/lib/carray/complex.rb +5 -5
  132. data/lib/carray/conditional.rb +67 -18
  133. data/lib/carray/const_string.rb +24 -5
  134. data/lib/carray/construct.rb +15 -11
  135. data/lib/carray/data_type_extension.rb +9 -1
  136. data/lib/carray/distance_limit.rb +77 -0
  137. data/lib/carray/fixlen_string.rb +48 -3
  138. data/lib/carray/frame/column_verbs.rb +71 -0
  139. data/lib/carray/frame/concat.rb +17 -3
  140. data/lib/carray/frame/convert.rb +69 -0
  141. data/lib/carray/frame/csv_parser.rb +111 -17
  142. data/lib/carray/frame/describe.rb +3 -2
  143. data/lib/carray/frame/frame.rb +252 -28
  144. data/lib/carray/frame/group.rb +603 -101
  145. data/lib/carray/frame/io.rb +117 -28
  146. data/lib/carray/frame/join.rb +312 -51
  147. data/lib/carray/frame/records.rb +83 -7
  148. data/lib/carray/frame/reshape.rb +42 -35
  149. data/lib/carray/frame/sort.rb +7 -19
  150. data/lib/carray/frame/verbs.rb +188 -48
  151. data/lib/carray/frame/windows.rb +117 -0
  152. data/lib/carray/frame.rb +2 -0
  153. data/lib/carray/fuse_source.rb +42 -6
  154. data/lib/carray/fusion.rb +40 -16
  155. data/lib/carray/histogram.rb +6 -1
  156. data/lib/carray/inspect.rb +31 -8
  157. data/lib/carray/iterator.rb +85 -7
  158. data/lib/carray/key_windows.rb +620 -0
  159. data/lib/carray/lazy.rb +217 -183
  160. data/lib/carray/mask_gap_fill.rb +63 -57
  161. data/lib/carray/meld_reduce.rb +12 -14
  162. data/lib/carray/methods/bin.rb +26 -18
  163. data/lib/carray/methods/bincount.rb +16 -0
  164. data/lib/carray/methods/bit_string.rb +1 -1
  165. data/lib/carray/methods/choose.rb +16 -3
  166. data/lib/carray/methods/composition.rb +3 -1
  167. data/lib/carray/methods/gather_nd.rb +14 -5
  168. data/lib/carray/methods/insert_block.rb +2 -2
  169. data/lib/carray/methods/is_between.rb +59 -0
  170. data/lib/carray/methods/is_in.rb +61 -37
  171. data/lib/carray/methods/join.rb +1 -6
  172. data/lib/carray/methods/lag.rb +49 -0
  173. data/lib/carray/methods/locate_addr.rb +47 -51
  174. data/lib/carray/methods/mode.rb +1 -1
  175. data/lib/carray/methods/nunique.rb +11 -6
  176. data/lib/carray/methods/resize.rb +3 -3
  177. data/lib/carray/methods/value_counts.rb +4 -11
  178. data/lib/carray/record_order.rb +3 -3
  179. data/lib/carray/rng.rb +3 -0
  180. data/lib/carray/runtime.rb +53 -8
  181. data/lib/carray/segment_iterator.rb +323 -88
  182. data/lib/carray/serialize.rb +45 -14
  183. data/lib/carray/slab_iterator.rb +50 -0
  184. data/lib/carray/stack.rb +15 -12
  185. data/lib/carray/string.rb +113 -5
  186. data/lib/carray/string_operation_extension.rb +21 -6
  187. data/lib/carray/struct.rb +9 -21
  188. data/lib/carray/struct_builder.rb +24 -11
  189. data/lib/carray/time.rb +819 -158
  190. data/lib/carray/window_iterator.rb +122 -16
  191. data/lib/carray.rb +7 -0
  192. data/yard-stubs/ca_obj_bitfield.rb +1 -1
  193. data/yard-stubs/ca_obj_field.rb +4 -0
  194. data/yard-stubs/ca_obj_grid.rb +21 -10
  195. data/yard-stubs/ca_obj_refer.rb +2 -1
  196. data/yard-stubs/ca_obj_roll.rb +2 -2
  197. data/yard-stubs/ca_obj_shift.rb +5 -1
  198. data/yard-stubs/ca_obj_tile.rb +1 -1
  199. data/yard-stubs/ca_obj_transpose.rb +5 -4
  200. data/yard-stubs/ca_obj_window.rb +8 -3
  201. data/yard-stubs/carray_access.rb +6 -1
  202. data/yard-stubs/carray_attribute.rb +3 -6
  203. data/yard-stubs/carray_cast.rb +12 -0
  204. data/yard-stubs/carray_conversion.rb +7 -2
  205. data/yard-stubs/carray_copy.rb +4 -2
  206. data/yard-stubs/carray_count.rb +4 -4
  207. data/yard-stubs/carray_element.rb +17 -10
  208. data/yard-stubs/carray_generate.rb +21 -1
  209. data/yard-stubs/carray_mask.rb +32 -3
  210. data/yard-stubs/carray_math.rb +41 -5
  211. data/yard-stubs/carray_median_percentile.rb +5 -2
  212. data/yard-stubs/carray_memory_view.rb +40 -23
  213. data/yard-stubs/carray_order.rb +129 -22
  214. data/yard-stubs/carray_random.rb +13 -4
  215. data/yard-stubs/carray_scatter.rb +17 -10
  216. data/yard-stubs/carray_slab.rb +9 -2
  217. data/yard-stubs/carray_sort.rb +6 -5
  218. data/yard-stubs/carray_test.rb +5 -3
  219. metadata +25 -3
  220. data/lib/carray/arrow_tensor.rb +0 -401
  221. data/lib/carray/table.rb +0 -108
@@ -1,120 +1,109 @@
1
1
  /* ---------------------------------------------------------------------------
2
2
 
3
- ca_categorical_iterator.c — counting-sort scatter for CACategoricalIterator.
4
-
5
- __categorical_scatter__ lays a flat payload out as a category-contiguous
6
- copy in a single pass, driven by the categorical codes and a per-category
7
- write cursor (a mutable copy of reduceat_index's segment starts). No
8
- permutation array is built; O(n), stable (ascending scan keeps each
9
- category's members in source order). It is the discrete, value-carrying
10
- sibling of histogram_scatter_ki (ext/carray_histogram.c): where the histogram
11
- scatters a +1 into counts, this scatters the payload cell into grouped.
12
-
13
- Two masks meet here and are kept distinct:
14
- - the CODES mask is authoritative for exclusion — a masked code cell does
15
- not join any group (the sentinel value never has to be read; `c < k` is a
16
- defensive assert only).
17
- - the VALUE mask propagates into grouped, so a group with a masked payload
18
- cell reduces as CArray does (the cell is skipped by mask-aware reductions).
19
-
20
- The output position is data-dependent (cursor[code]++), which the aligned
21
- kernel_iterator macros do not model, so the flat inputs are materialised here:
22
- ca_attach aliases a contiguous entity (codes / a contiguous value) and gathers
23
- a view. Codes dispatch on their native integer type (no coercion); the value
24
- move is a bytes-wide memcpy (grouped shares the value data type, so no value-type
25
- dispatch is needed).
26
-
27
- Surface (private): codes.__categorical_scatter__(value, cursor, grouped, k)
3
+ ca_categorical_iterator.c — kernels for CACategorical and CACategoricalIterator.
4
+
5
+ addresses_by_category orders the cells of a categorical by category, the
6
+ counting sort behind CACategorical#sort_addr: one pass over the codes
7
+ writes each cell's address at its category's cursor (a mutable copy of the
8
+ segment starts), so every cell of category 0 comes first, then 1, ..., in
9
+ source order within a category (stable). A cell in no category -- a masked
10
+ code, or a code outside 0...k -- is written in the same pass at a second
11
+ cursor that starts after the last category, so the excluded cells follow
12
+ in source order.
13
+
14
+ The output position depends on the code, which the aligned kernel_iterator
15
+ macros do not model, so the codes are materialised here: ca_attach aliases a
16
+ contiguous entity and gathers a view.
17
+
18
+ Surface (private): codes.addresses_by_category(cursor, k, nvalid)
28
19
  self = codes (integer, carries the exclusion mask), read flat
29
- value = payload (any data type, may carry a mask), read flat, same length
30
20
  cursor = int64 length-k segment starts (mutated in place, consumed)
31
- grouped = pre-allocated contiguous entity of the value data type, length nvalid
32
21
  k = number of categories
33
- Returns grouped.
22
+ nvalid = number of cells in a category (where the excluded cells start)
23
+ Returns a new int64 CArray of codes.elements addresses.
34
24
 
35
25
  --------------------------------------------------------------------------- */
36
26
 
37
27
  #include "carray.h"
38
28
  #include "carray_internal.h" /* CA_EXTREMUM_REPLACES */
29
+ #include "ca_sort_kernels.h" /* ca_partition_nan_f64 */
39
30
  #include <stdlib.h> /* qsort */
40
31
  #include <math.h> /* floor */
41
32
 
42
- #define CATEGORICAL_SCATTER_BODY(CODE_T) \
33
+ #define ADDRESSES_BY_CATEGORY_BODY(CODE_T) \
43
34
  do { \
44
35
  const CODE_T *cp = (const CODE_T *) codes->ptr; \
45
36
  for ( j = 0; j < n; j++ ) { \
46
- if ( cmask && cmask[j] ) continue; /* excluded by codes mask */ \
47
37
  c = (int64_t) cp[j]; \
48
- if ( c < 0 || c >= k ) continue; /* defensive */ \
49
- pos = (ca_size_t) cur[c]; \
50
- cur[c] = (int64_t) (pos + 1); \
51
- memcpy(gp + pos * bytes, vp + j * bytes, (size_t) bytes); \
52
- if ( vmask && vmask[j] ) gmask[pos] = 1; /* propagate value mask */ \
38
+ if ( ( cmask && cmask[j] ) || c < 0 || c >= k ) { \
39
+ if ( tail >= (int64_t) n ) { overrun = 1; break; } \
40
+ out[tail++] = (int64_t) j; /* in no category */ \
41
+ continue; \
42
+ } \
43
+ if ( cur[c] < 0 || cur[c] >= nvalid ) { overrun = 1; break; } \
44
+ out[cur[c]++] = (int64_t) j; \
53
45
  } \
54
46
  } while (0)
55
47
 
56
48
  static VALUE
57
- rb_ca_categorical_scatter (VALUE self, VALUE rvalue, VALUE rcursor,
58
- VALUE rgrouped, VALUE rk)
49
+ rb_ca_addresses_by_category (VALUE self, VALUE rcursor, VALUE rk, VALUE rnvalid)
59
50
  {
60
- CArray *codes, *value, *cursor, *grouped;
61
- int64_t k, c, *cur;
62
- ca_size_t n, j, pos, bytes;
63
- boolean8_t *cmask, *vmask, *gmask = NULL;
64
- char *gp, *vp;
65
-
66
- GetCArray(self, codes);
67
- GetCArray(rvalue, value);
68
- GetCArray(rcursor, cursor);
69
- GetCArray(rgrouped, grouped);
70
- k = (int64_t) NUM2LL(rk);
71
-
72
- n = codes->elements;
73
- bytes = value->bytes;
74
- if ( value->elements != n ) {
75
- rb_raise(rb_eArgError,
76
- "__categorical_scatter__: value length %lld != codes length %lld",
77
- (long long) value->elements, (long long) n);
78
- }
79
- if ( cursor->data_type != CA_INT64 || cursor->elements != k ) {
80
- rb_raise(rb_eArgError, "__categorical_scatter__: cursor must be int64[k]");
51
+ CArray *codes, *cursor, *result;
52
+ VALUE rresult;
53
+ int64_t k, c, *cur, *out, tail, nvalid;
54
+ ca_size_t n, j, dim;
55
+ int overrun = 0;
56
+ boolean8_t *cmask;
57
+
58
+ GetCArray(self, codes);
59
+ GetCArray(rcursor, cursor);
60
+ k = (int64_t) NUM2LL(rk);
61
+ nvalid = (int64_t) NUM2LL(rnvalid);
62
+ n = codes->elements;
63
+
64
+ if ( cursor->data_type != CA_INT64 || cursor->elements != k ||
65
+ ! ca_is_entity(cursor) ) {
66
+ rb_raise(rb_eArgError, "addresses_by_category: cursor must be an int64[k] entity");
81
67
  }
82
- if ( grouped->bytes != bytes ) {
83
- rb_raise(rb_eArgError, "__categorical_scatter__: grouped/value data type mismatch");
68
+ if ( nvalid < 0 || nvalid > (int64_t) n ) {
69
+ rb_raise(rb_eArgError, "addresses_by_category: nvalid %lld out of 0..%lld",
70
+ (long long) nvalid, (long long) n);
84
71
  }
85
-
86
- ca_attach_n(2, codes, value); /* window: detached before each raise inside */
87
- cmask = ca_mask_ptr(codes);
88
- vmask = ca_mask_ptr(value);
89
- cur = (int64_t *) cursor->ptr;
90
- gp = grouped->ptr;
91
- vp = value->ptr;
92
-
93
- if ( vmask ) { /* grouped needs a mask to receive it */
94
- ca_create_mask(grouped);
95
- gmask = (boolean8_t *) grouped->mask->ptr;
96
- }
97
-
98
72
  switch ( codes->data_type ) {
99
- case CA_INT8: CATEGORICAL_SCATTER_BODY(int8_t); break;
100
- case CA_UINT8: CATEGORICAL_SCATTER_BODY(uint8_t); break;
101
- case CA_INT16: CATEGORICAL_SCATTER_BODY(int16_t); break;
102
- case CA_UINT16: CATEGORICAL_SCATTER_BODY(uint16_t); break;
103
- case CA_INT32: CATEGORICAL_SCATTER_BODY(int32_t); break;
104
- case CA_UINT32: CATEGORICAL_SCATTER_BODY(uint32_t); break;
105
- case CA_INT64: CATEGORICAL_SCATTER_BODY(int64_t); break;
106
- case CA_UINT64: CATEGORICAL_SCATTER_BODY(uint64_t); break;
73
+ case CA_INT8: case CA_UINT8: case CA_INT16: case CA_UINT16:
74
+ case CA_INT32: case CA_UINT32: case CA_INT64: case CA_UINT64:
75
+ break;
107
76
  default:
108
- ca_detach(codes);
109
- ca_detach(value);
110
77
  rb_raise(rb_eCADataTypeError,
111
- "__categorical_scatter__: integer codes required (got data_type %d)",
78
+ "addresses_by_category: integer codes required (got data_type %d)",
112
79
  codes->data_type);
113
80
  }
114
81
 
82
+ dim = n;
83
+ rresult = rb_carray_new(CA_INT64, 1, &dim, 0, NULL);
84
+ GetCArray(rresult, result);
85
+ out = (int64_t *) result->ptr;
86
+ cur = (int64_t *) cursor->ptr;
87
+ tail = nvalid;
88
+
89
+ ca_attach(codes); /* window: nothing below raises */
90
+ cmask = ca_mask_ptr(codes);
91
+ switch ( codes->data_type ) {
92
+ case CA_INT8: ADDRESSES_BY_CATEGORY_BODY(int8_t); break;
93
+ case CA_UINT8: ADDRESSES_BY_CATEGORY_BODY(uint8_t); break;
94
+ case CA_INT16: ADDRESSES_BY_CATEGORY_BODY(int16_t); break;
95
+ case CA_UINT16: ADDRESSES_BY_CATEGORY_BODY(uint16_t); break;
96
+ case CA_INT32: ADDRESSES_BY_CATEGORY_BODY(int32_t); break;
97
+ case CA_UINT32: ADDRESSES_BY_CATEGORY_BODY(uint32_t); break;
98
+ case CA_INT64: ADDRESSES_BY_CATEGORY_BODY(int64_t); break;
99
+ case CA_UINT64: ADDRESSES_BY_CATEGORY_BODY(uint64_t); break;
100
+ }
115
101
  ca_detach(codes);
116
- ca_detach(value);
117
- return rgrouped;
102
+ if ( overrun ) {
103
+ rb_raise(rb_eArgError,
104
+ "addresses_by_category: the cursor and nvalid do not match the codes");
105
+ }
106
+ return rresult;
118
107
  }
119
108
 
120
109
  /* ---------------------------------------------------------------------------
@@ -151,7 +140,22 @@ rb_ca_categorical_scatter (VALUE self, VALUE rvalue, VALUE rcursor,
151
140
  #define CAT_NAN_NEVER(v) (0)
152
141
  #define CAT_NAN_CHECK(v) ((v) != (v))
153
142
 
154
- #define REDUCEAT_MOMENTS_BODY(T) \
143
+ /* Whether v takes the place of the accumulator acc: never when v is NaN,
144
+ always when acc is NaN and v is not. */
145
+ #define CAT_REPLACES(v, acc, CMP, ISNAN) \
146
+ ( ! ISNAN(v) && ( ISNAN(acc) || (v) CMP (acc) ) )
147
+
148
+ /* A segment without masked cells of at least CAT_LANES_FROM cells is read
149
+ with eight independent sums and eight min / max lanes, so no add or
150
+ compare waits on the one before it; a shorter one would spend more on
151
+ setting up and merging the lanes than it saves.
152
+ Each lane starts from the segment's first value; a lane can end on NaN
153
+ only if every value it saw was NaN, so merging the lanes with the same
154
+ rule gives the answer one lane would. The sums are added in a different
155
+ order than the cells, which is the license every float reduction has. */
156
+ #define CAT_LANES_FROM 64
157
+
158
+ #define REDUCEAT_MOMENTS_BODY(T, ISNAN, LANES_FROM) \
155
159
  do { \
156
160
  const T *gp = (const T *) grouped->ptr; \
157
161
  T *minv = (T *) minp, *maxv = (T *) maxp; \
@@ -161,16 +165,95 @@ rb_ca_categorical_scatter (VALUE self, VALUE rvalue, VALUE rcursor,
161
165
  ca_size_t j, cnt = 0; \
162
166
  double sacc = 0.0; \
163
167
  T mn = 0, mx = 0; int seen = 0; \
164
- for ( j = lo; j < hi; j++ ) { \
165
- if ( gm && gm[j] ) continue; /* masked value cell */ \
166
- { T v = gp[j]; \
167
- sacc += (double) v; \
168
- if ( ! seen ) { mn = v; mx = v; seen = 1; } \
169
- else { \
170
- if ( CA_EXTREMUM_REPLACES(v, mn, <) ) mn = v; \
171
- if ( CA_EXTREMUM_REPLACES(v, mx, >) ) mx = v; \
168
+ if ( ! gm && hi - lo < (ca_size_t) (LANES_FROM) ) { \
169
+ if ( hi > lo ) { \
170
+ mn = mx = gp[lo]; \
171
+ for ( j = lo; j < hi; j++ ) { \
172
+ T v = gp[j]; \
173
+ sacc += (double) v; \
174
+ if ( CAT_REPLACES(v, mn, <, ISNAN) ) mn = v; \
175
+ if ( CAT_REPLACES(v, mx, >, ISNAN) ) mx = v; \
176
+ } \
177
+ cnt = hi - lo; seen = 1; \
178
+ } \
179
+ } \
180
+ else if ( ! gm ) { \
181
+ { \
182
+ double s0 = 0, s1 = 0, s2 = 0, s3 = 0, s4 = 0, s5 = 0, s6 = 0, s7 = 0; \
183
+ T n0, n1, n2, n3, n4, n5, n6, n7, x0, x1, x2, x3, x4, x5, x6, x7; \
184
+ n0 = n1 = n2 = n3 = n4 = n5 = n6 = n7 = gp[lo]; \
185
+ x0 = x1 = x2 = x3 = x4 = x5 = x6 = x7 = gp[lo]; \
186
+ for ( j = lo; j + 8 <= hi; j += 8 ) { \
187
+ T v0 = gp[j], v1 = gp[j+1], v2 = gp[j+2], v3 = gp[j+3]; \
188
+ T v4 = gp[j+4], v5 = gp[j+5], v6 = gp[j+6], v7 = gp[j+7]; \
189
+ s0 += (double) v0; s1 += (double) v1; \
190
+ s2 += (double) v2; s3 += (double) v3; \
191
+ s4 += (double) v4; s5 += (double) v5; \
192
+ s6 += (double) v6; s7 += (double) v7; \
193
+ if ( CAT_REPLACES(v0, n0, <, ISNAN) ) n0 = v0; \
194
+ if ( CAT_REPLACES(v1, n1, <, ISNAN) ) n1 = v1; \
195
+ if ( CAT_REPLACES(v2, n2, <, ISNAN) ) n2 = v2; \
196
+ if ( CAT_REPLACES(v3, n3, <, ISNAN) ) n3 = v3; \
197
+ if ( CAT_REPLACES(v4, n4, <, ISNAN) ) n4 = v4; \
198
+ if ( CAT_REPLACES(v5, n5, <, ISNAN) ) n5 = v5; \
199
+ if ( CAT_REPLACES(v6, n6, <, ISNAN) ) n6 = v6; \
200
+ if ( CAT_REPLACES(v7, n7, <, ISNAN) ) n7 = v7; \
201
+ if ( CAT_REPLACES(v0, x0, >, ISNAN) ) x0 = v0; \
202
+ if ( CAT_REPLACES(v1, x1, >, ISNAN) ) x1 = v1; \
203
+ if ( CAT_REPLACES(v2, x2, >, ISNAN) ) x2 = v2; \
204
+ if ( CAT_REPLACES(v3, x3, >, ISNAN) ) x3 = v3; \
205
+ if ( CAT_REPLACES(v4, x4, >, ISNAN) ) x4 = v4; \
206
+ if ( CAT_REPLACES(v5, x5, >, ISNAN) ) x5 = v5; \
207
+ if ( CAT_REPLACES(v6, x6, >, ISNAN) ) x6 = v6; \
208
+ if ( CAT_REPLACES(v7, x7, >, ISNAN) ) x7 = v7; \
209
+ } \
210
+ for ( ; j < hi; j++ ) { \
211
+ T v = gp[j]; \
212
+ s0 += (double) v; \
213
+ if ( CAT_REPLACES(v, n0, <, ISNAN) ) n0 = v; \
214
+ if ( CAT_REPLACES(v, x0, >, ISNAN) ) x0 = v; \
215
+ } \
216
+ sacc = ((s0 + s1) + (s2 + s3)) + ((s4 + s5) + (s6 + s7)); \
217
+ mn = n0; \
218
+ if ( CAT_REPLACES(n1, mn, <, ISNAN) ) mn = n1; \
219
+ if ( CAT_REPLACES(n2, mn, <, ISNAN) ) mn = n2; \
220
+ if ( CAT_REPLACES(n3, mn, <, ISNAN) ) mn = n3; \
221
+ if ( CAT_REPLACES(n4, mn, <, ISNAN) ) mn = n4; \
222
+ if ( CAT_REPLACES(n5, mn, <, ISNAN) ) mn = n5; \
223
+ if ( CAT_REPLACES(n6, mn, <, ISNAN) ) mn = n6; \
224
+ if ( CAT_REPLACES(n7, mn, <, ISNAN) ) mn = n7; \
225
+ mx = x0; \
226
+ if ( CAT_REPLACES(x1, mx, >, ISNAN) ) mx = x1; \
227
+ if ( CAT_REPLACES(x2, mx, >, ISNAN) ) mx = x2; \
228
+ if ( CAT_REPLACES(x3, mx, >, ISNAN) ) mx = x3; \
229
+ if ( CAT_REPLACES(x4, mx, >, ISNAN) ) mx = x4; \
230
+ if ( CAT_REPLACES(x5, mx, >, ISNAN) ) mx = x5; \
231
+ if ( CAT_REPLACES(x6, mx, >, ISNAN) ) mx = x6; \
232
+ if ( CAT_REPLACES(x7, mx, >, ISNAN) ) mx = x7; \
233
+ /* Of equal values a lane keeps its first, but the merge does not \
234
+ know which lane saw its value first in cell order. Only 0.0 and \
235
+ -0.0 are equal and tell apart: take the first zero, as one loop \
236
+ over the cells does. */ \
237
+ if ( mn == 0 ) { \
238
+ for ( j = lo; j < hi; j++ ) { if ( gp[j] == 0 ) { mn = gp[j]; break; } } \
239
+ } \
240
+ if ( mx == 0 ) { \
241
+ for ( j = lo; j < hi; j++ ) { if ( gp[j] == 0 ) { mx = gp[j]; break; } } \
172
242
  } \
173
- cnt++; } \
243
+ cnt = hi - lo; seen = 1; \
244
+ } \
245
+ } \
246
+ else { \
247
+ for ( j = lo; j < hi && gm[j]; j++ ) ; /* skip to the first present */ \
248
+ if ( j < hi ) { mn = mx = gp[j]; seen = 1; } \
249
+ for ( ; j < hi; j++ ) { \
250
+ T v = gp[j]; \
251
+ if ( gm[j] ) continue; /* masked value cell */ \
252
+ sacc += (double) v; \
253
+ cnt++; \
254
+ if ( CAT_REPLACES(v, mn, <, ISNAN) ) mn = v; \
255
+ if ( CAT_REPLACES(v, mx, >, ISNAN) ) mx = v; \
256
+ } \
174
257
  } \
175
258
  countp[c] = (int64_t) cnt; \
176
259
  sump[c] = sacc; \
@@ -221,16 +304,16 @@ rb_ca_reduceat_moments (VALUE self, VALUE roffsets, VALUE rcounts,
221
304
  maxm = (boolean8_t *) maxs->mask->ptr;
222
305
 
223
306
  switch ( grouped->data_type ) {
224
- case CA_INT8: REDUCEAT_MOMENTS_BODY(int8_t); break;
225
- case CA_UINT8: REDUCEAT_MOMENTS_BODY(uint8_t); break;
226
- case CA_INT16: REDUCEAT_MOMENTS_BODY(int16_t); break;
227
- case CA_UINT16: REDUCEAT_MOMENTS_BODY(uint16_t); break;
228
- case CA_INT32: REDUCEAT_MOMENTS_BODY(int32_t); break;
229
- case CA_UINT32: REDUCEAT_MOMENTS_BODY(uint32_t); break;
230
- case CA_INT64: REDUCEAT_MOMENTS_BODY(int64_t); break;
231
- case CA_UINT64: REDUCEAT_MOMENTS_BODY(uint64_t); break;
232
- case CA_FLOAT32: REDUCEAT_MOMENTS_BODY(float32_t); break;
233
- case CA_FLOAT64: REDUCEAT_MOMENTS_BODY(float64_t); break;
307
+ case CA_INT8: REDUCEAT_MOMENTS_BODY(int8_t, CAT_NAN_NEVER, CA_LENGTH_MAX); break;
308
+ case CA_UINT8: REDUCEAT_MOMENTS_BODY(uint8_t, CAT_NAN_NEVER, CA_LENGTH_MAX); break;
309
+ case CA_INT16: REDUCEAT_MOMENTS_BODY(int16_t, CAT_NAN_NEVER, CA_LENGTH_MAX); break;
310
+ case CA_UINT16: REDUCEAT_MOMENTS_BODY(uint16_t, CAT_NAN_NEVER, CA_LENGTH_MAX); break;
311
+ case CA_INT32: REDUCEAT_MOMENTS_BODY(int32_t, CAT_NAN_NEVER, CA_LENGTH_MAX); break;
312
+ case CA_UINT32: REDUCEAT_MOMENTS_BODY(uint32_t, CAT_NAN_NEVER, CA_LENGTH_MAX); break;
313
+ case CA_INT64: REDUCEAT_MOMENTS_BODY(int64_t, CAT_NAN_NEVER, CA_LENGTH_MAX); break;
314
+ case CA_UINT64: REDUCEAT_MOMENTS_BODY(uint64_t, CAT_NAN_NEVER, CA_LENGTH_MAX); break;
315
+ case CA_FLOAT32: REDUCEAT_MOMENTS_BODY(float32_t, CAT_NAN_CHECK, CAT_LANES_FROM); break;
316
+ case CA_FLOAT64: REDUCEAT_MOMENTS_BODY(float64_t, CAT_NAN_CHECK, CAT_LANES_FROM); break;
234
317
  default:
235
318
  rb_raise(rb_eCADataTypeError,
236
319
  "__reduceat_moments__: numeric value required (got data_type %d)",
@@ -294,19 +377,12 @@ ca_nth_element_double (double *a, ca_size_t n, ca_size_t kth)
294
377
  { double f = (double) (m - 1) * p / 100.0; \
295
378
  ca_size_t ki = (ca_size_t) floor(f); \
296
379
  double vlo, vhi; \
297
- /* Push NaN to the tail, which is where CArray's sort puts it, so the \
298
- ki-th element here is the ki-th of the sorted segment. Selection \
299
- runs on the numeric prefix, since a comparison-based quickselect \
300
- has nothing to say about a NaN; a position or an upper neighbour \
301
- landing in the tail is NaN. The blend is CArray#percentile's, so \
302
- both answer alike for NaN and infinities. */ \
303
- ca_size_t nnum = m, t0 = 0; \
304
- while ( t0 < nnum ) { \
305
- if ( scratch[t0] != scratch[t0] ) { \
306
- double tmp = scratch[--nnum]; \
307
- scratch[nnum] = scratch[t0]; scratch[t0] = tmp; \
308
- } else t0++; \
309
- } \
380
+ /* NaN to the tail, where CArray's sort puts it (ca_partition_nan), \
381
+ so the ki-th element here is the ki-th of the sorted segment. \
382
+ Selection runs on the numeric prefix; a position or an upper \
383
+ neighbour landing in the tail is NaN. The blend is \
384
+ CArray#percentile's, so both answer alike for NaN and infinities. */\
385
+ ca_size_t nnum = ca_partition_nan_f64(scratch, m); \
310
386
  if ( ki < nnum ) { \
311
387
  ca_nth_element_double(scratch, nnum, ki); /* scratch[ki] = ki-th */ \
312
388
  vlo = scratch[ki]; \
@@ -316,9 +392,7 @@ ca_nth_element_double (double *a, ca_size_t n, ca_size_t kth)
316
392
  for ( t = ki + 2; t < nnum; t++ ) if ( scratch[t] < mn ) mn = scratch[t]; \
317
393
  vhi = mn; \
318
394
  } else vhi = (double) NAN; \
319
- { double r = f - (double) ki; \
320
- outp[c] = ( r == 0.0 || ki + 1 >= m ) ? vlo \
321
- : vlo * (1.0 - r) + vhi * r; } } \
395
+ outp[c] = ca_percentile_linear(vlo, vhi, f - (double) ki, ki, m); } \
322
396
  } \
323
397
  } while (0)
324
398
 
@@ -383,20 +457,23 @@ rb_ca_reduceat_percentile (VALUE self, VALUE roffsets, VALUE rp, VALUE rout)
383
457
 
384
458
  /* ---------------------------------------------------------------------------
385
459
 
386
- __reduceat_variance__ — centred two-pass SAMPLE variance (ddof=1) reduceat.
387
- The means (from the cached moments: sum/count) drive a second walk that
388
- accumulates the per-segment centred sum of squares; variance = SS / (n-1).
389
- Centred (not the one-pass sum-of-squares) so the ε-close contract holds — a
390
- one-pass formula cancels catastrophically. Matches CArray#variance per group:
391
- count 0 or 1 -> masked (the sample variance is undefined), count >= 2 ->
392
- SS / (count-1).
460
+ __reduceat_variance__ — centred two-pass variance reduceat, sample (ddof=1)
461
+ or population (ddof=0). The means (from the cached moments: sum/count)
462
+ drive a second walk that accumulates the per-segment centred sum of
463
+ squares; variance = SS / (n - ddof). Centred (not the one-pass
464
+ sum-of-squares) so the ε-close contract holds — a one-pass formula cancels
465
+ catastrophically. Matches CArray#variance / #variancep per group: count
466
+ <= ddof (or 0) -> masked, otherwise SS / (count - ddof), divided once as
467
+ the core does.
393
468
 
394
- Surface (private): grouped.__reduceat_variance__(offsets, means, counts, out)
469
+ Surface (private):
470
+ grouped.__reduceat_variance__(offsets, means, counts, out, ddof)
395
471
  self = grouped (numeric value data type, may carry a mask)
396
472
  offsets = int64[k] segment STARTS
397
473
  means = float64[k] per-segment mean (ignored where count < 2)
398
474
  counts = int64[k] per-segment present count
399
- out = float64[k] output: variance; count 0 or 1 masked
475
+ out = float64[k] output: variance; count <= ddof masked
476
+ ddof = 1 (sample) or 0 (population)
400
477
 
401
478
  --------------------------------------------------------------------------- */
402
479
 
@@ -405,7 +482,7 @@ rb_ca_reduceat_percentile (VALUE self, VALUE roffsets, VALUE rp, VALUE rout)
405
482
  const T *gp = (const T *) grouped->ptr; \
406
483
  for ( c = 0; c < k; c++ ) { \
407
484
  int64_t cnt = countp[c]; \
408
- if ( cnt < 2 ) { outp[c] = 0.0; outm[c] = 1; continue; } /* undefined */ \
485
+ if ( cnt <= ddof || cnt < 1 ) { outp[c] = 0.0; outm[c] = 1; continue; } \
409
486
  { double mean = meanp[c], ss = 0.0; \
410
487
  ca_size_t lo = (ca_size_t) offs[c]; \
411
488
  ca_size_t hi = (c + 1 < k) ? (ca_size_t) offs[c+1] : n, j; \
@@ -420,14 +497,15 @@ rb_ca_reduceat_percentile (VALUE self, VALUE roffsets, VALUE rp, VALUE rout)
420
497
  double d = (double) gp[j] - mean; ss += d * d; \
421
498
  } \
422
499
  } \
423
- outp[c] = ss / (double) (cnt - 1); } \
500
+ outp[c] = ss / (double) (cnt - ddof); } \
424
501
  } \
425
502
  } while (0)
426
503
 
427
504
  static VALUE
428
505
  rb_ca_reduceat_variance (VALUE self, VALUE roffsets, VALUE rmeans,
429
- VALUE rcounts, VALUE rout)
506
+ VALUE rcounts, VALUE rout, VALUE rddof)
430
507
  {
508
+ int64_t ddof = NUM2LL(rddof);
431
509
  CArray *grouped, *offsets, *means, *counts, *out;
432
510
  int64_t k, *offs, *countp;
433
511
  ca_size_t n, c;
@@ -444,8 +522,9 @@ rb_ca_reduceat_variance (VALUE self, VALUE roffsets, VALUE rmeans,
444
522
  n = grouped->elements;
445
523
  if ( offsets->data_type != CA_INT64 || counts->data_type != CA_INT64 ||
446
524
  means->data_type != CA_FLOAT64 || out->data_type != CA_FLOAT64 ||
447
- means->elements != k || counts->elements != k || out->elements != k ) {
448
- rb_raise(rb_eArgError, "__reduceat_variance__: offsets/counts int64, means/out float64[k]");
525
+ means->elements != k || counts->elements != k || out->elements != k ||
526
+ ( ddof != 0 && ddof != 1 ) ) {
527
+ rb_raise(rb_eArgError, "__reduceat_variance__: offsets/counts int64, means/out float64[k], ddof 0 or 1");
449
528
  }
450
529
 
451
530
  offs = (int64_t *) offsets->ptr;
@@ -693,19 +772,16 @@ cmp_double (const void *a, const void *b)
693
772
  scratch[m++] = (double) gp[jj]; \
694
773
  } \
695
774
  if ( m == 0 ) { for ( t = 0; t < 5; t++ ) { outp[t][c] = 0.0; outm[t][c] = 1; } continue; } \
775
+ /* NaN sorts last, as in the core's sort, so a position or an upper \
776
+ neighbour in the tail is NaN, as the core answers. */ \
696
777
  qsort(scratch, (size_t) m, sizeof(double), cmp_double); \
697
- { ca_size_t nnum = m; \
698
- while ( nnum > 0 && scratch[nnum-1] != scratch[nnum-1] ) nnum--; \
699
778
  for ( t = 0; t < 5; t++ ) { \
700
779
  double f = (double) (m - 1) * P[t] / 100.0; \
701
780
  ca_size_t ki = (ca_size_t) floor(f); \
702
- double vlo = scratch[ki]; \
703
- /* Clamp the upper neighbour into the numeric prefix: with NaN sorted \
704
- last, a position whose neighbour is a NaN interpolates against \
705
- itself, which is what the core answers. */ \
706
- double vhi = (ki + 1 < nnum) ? scratch[ki+1] : vlo; \
707
- outp[t][c] = vlo + (f - (double) ki) * (vhi - vlo); \
708
- } } \
781
+ double vhi = (ki + 1 < m) ? scratch[ki+1] : scratch[ki]; \
782
+ outp[t][c] = ca_percentile_linear(scratch[ki], vhi, f - (double) ki, \
783
+ ki, m); \
784
+ } \
709
785
  } \
710
786
  } while (0)
711
787
 
@@ -857,7 +933,7 @@ rb_ca_reduceat_wsum_wmean (VALUE self, VALUE roffsets, VALUE rwg,
857
933
 
858
934
  Not aligned kernel_iterator: output position depends on the codes value
859
935
  (data-dependent scatter), so ca_attach materialises the inputs into
860
- contiguous flat buffers — same pattern as sibling __categorical_scatter__.
936
+ contiguous flat buffers — same pattern as sibling addresses_by_category.
861
937
 
862
938
  Surface (private):
863
939
  h.__fiber_scatter_moments__(codes, axis, K, counts_out, sums_out)
@@ -1033,8 +1109,13 @@ rb_ca_fiber_scatter_moments (VALUE self, VALUE rcodes, VALUE raxis, VALUE rk,
1033
1109
  h->data_type);
1034
1110
  }
1035
1111
 
1112
+ ca_detach(h);
1113
+ ca_detach(codes);
1114
+
1036
1115
  /* Mask empty (count == 0) cells in mins/maxs: value slot is 0 but meaningless.
1037
- Matches __reduceat_moments__ contract for empty segments. */
1116
+ Matches __reduceat_moments__ contract for empty segments. After the
1117
+ detach: creating a mask allocates, and a raise must not leave h and
1118
+ codes attached. */
1038
1119
  ca_create_mask(mins);
1039
1120
  ca_create_mask(maxs);
1040
1121
  minm = (boolean8_t *) mins->mask->ptr;
@@ -1042,9 +1123,6 @@ rb_ca_fiber_scatter_moments (VALUE self, VALUE rcodes, VALUE raxis, VALUE rk,
1042
1123
  for ( cell = 0; cell < total; cell++ ) {
1043
1124
  if ( countp[cell] == 0 ) { minm[cell] = 1; maxm[cell] = 1; }
1044
1125
  }
1045
-
1046
- ca_detach(h);
1047
- ca_detach(codes);
1048
1126
  return Qnil;
1049
1127
  }
1050
1128
 
@@ -1397,8 +1475,8 @@ rb_ca_fiber_scatter_wsum_wmean (VALUE self, VALUE rcodes, VALUE rweights,
1397
1475
  void
1398
1476
  Init_ca_categorical_iterator (void)
1399
1477
  {
1400
- rb_define_private_method(rb_cCArray, "__categorical_scatter__",
1401
- rb_ca_categorical_scatter, 4);
1478
+ rb_define_private_method(rb_cCArray, "addresses_by_category",
1479
+ rb_ca_addresses_by_category, 3);
1402
1480
  rb_define_private_method(rb_cCArray, "__fiber_scatter_moments__",
1403
1481
  rb_ca_fiber_scatter_moments, 7);
1404
1482
  rb_define_private_method(rb_cCArray, "__fiber_scatter_prod__",
@@ -1410,7 +1488,7 @@ Init_ca_categorical_iterator (void)
1410
1488
  rb_define_private_method(rb_cCArray, "__reduceat_percentile__",
1411
1489
  rb_ca_reduceat_percentile, 3);
1412
1490
  rb_define_private_method(rb_cCArray, "__reduceat_variance__",
1413
- rb_ca_reduceat_variance, 4);
1491
+ rb_ca_reduceat_variance, 5);
1414
1492
  rb_define_private_method(rb_cCArray, "__reduceat_prod__",
1415
1493
  rb_ca_reduceat_prod, 2);
1416
1494
  rb_define_private_method(rb_cCArray, "__reduceat_argminmax__",
data/ext/ca_compare.c CHANGED
@@ -14,32 +14,28 @@
14
14
  #include "ruby.h"
15
15
  #include "carray.h"
16
16
  #include "ca_compare.h"
17
- #include "carray_internal.h" /* ca_obj_cmp, CA_OBJ_ISNAN */
17
+ #include "carray_internal.h" /* CA_FLOAT_ORDER_CMP, ca_obj_order_cmp */
18
18
  #include <math.h>
19
19
 
20
20
  #define DEFINE_ELEM_CMP(type) \
21
21
  static int \
22
22
  elem_cmp_## type (const void *va, const void *vb) \
23
23
  { \
24
- const type *a = (const type *) va; \
25
- const type *b = (const type *) vb; \
24
+ const CA_U(type) *a = (const CA_U(type) *) va; \
25
+ const CA_U(type) *b = (const CA_U(type) *) vb; \
26
26
  if ( *a > *b ) return 1; \
27
27
  if ( *a < *b ) return -1; \
28
28
  return 0; \
29
29
  }
30
30
 
31
- /* Float order: NaN sorts last (matches the sort kernels' NaN-at-end). */
31
+ /* Float order: NaN after every number (CA_FLOAT_ORDER_CMP). */
32
32
  #define DEFINE_ELEM_CMP_FLOAT(type) \
33
33
  static int \
34
34
  elem_cmp_## type (const void *va, const void *vb) \
35
35
  { \
36
- const type *a = (const type *) va; \
37
- const type *b = (const type *) vb; \
38
- if ( isnan(*a) && ( ! isnan(*b) ) ) return 1; \
39
- if ( isnan(*b) && ( ! isnan(*a) ) ) return -1; \
40
- if ( *a > *b ) return 1; \
41
- if ( *a < *b ) return -1; \
42
- return 0; \
36
+ const type a = *(const CA_U(type) *) va; \
37
+ const type b = *(const CA_U(type) *) vb; \
38
+ return CA_FLOAT_ORDER_CMP(a, b); \
43
39
  }
44
40
 
45
41
  DEFINE_ELEM_CMP(boolean8_t)
@@ -54,18 +50,11 @@ DEFINE_ELEM_CMP(uint64_t)
54
50
  DEFINE_ELEM_CMP_FLOAT(float32_t)
55
51
  DEFINE_ELEM_CMP_FLOAT(float64_t)
56
52
 
57
- /* CA_OBJECT: compare by `<=>`, as sort orders object cells: a Float NaN
58
- after every other value, and a pair `<=>` cannot order raises
59
- "comparison of A with B failed". */
53
+ /* CA_OBJECT: the object order (ca_obj_order_cmp). */
60
54
  static int
61
55
  elem_cmp_VALUE (const void *va, const void *vb)
62
56
  {
63
- const VALUE *a = (const VALUE *) va;
64
- const VALUE *b = (const VALUE *) vb;
65
- int nan_a = CA_OBJ_ISNAN(*a);
66
- int nan_b = CA_OBJ_ISNAN(*b);
67
- if ( nan_a || nan_b ) return nan_a - nan_b;
68
- return ca_obj_cmp(*a, *b);
57
+ return ca_obj_order_cmp(*(const VALUE *) va, *(const VALUE *) vb);
69
58
  }
70
59
 
71
60
  /* FIXLEN / retired / otherwise unorderable data_types: callers must handle
@@ -32,29 +32,29 @@ ca_fill_typed (char *dst, const char *val, ca_size_t bytes, ca_size_t n)
32
32
  break;
33
33
  }
34
34
  case 2: {
35
- uint16_t v = *(const uint16_t *)val;
36
- uint16_t *p = (uint16_t *)dst;
35
+ uint16_t v = *(const ca_u_uint16_t *)val;
36
+ ca_u_uint16_t *p = (ca_u_uint16_t *)dst;
37
37
  for ( i = 0; i < n; i++ ) p[i] = v;
38
38
  break;
39
39
  }
40
40
  case 4: {
41
- uint32_t v = *(const uint32_t *)val;
42
- uint32_t *p = (uint32_t *)dst;
41
+ uint32_t v = *(const ca_u_uint32_t *)val;
42
+ ca_u_uint32_t *p = (ca_u_uint32_t *)dst;
43
43
  for ( i = 0; i < n; i++ ) p[i] = v;
44
44
  break;
45
45
  }
46
46
  case 8: {
47
- uint64_t v = *(const uint64_t *)val;
48
- uint64_t *p = (uint64_t *)dst;
47
+ uint64_t v = *(const ca_u_uint64_t *)val;
48
+ ca_u_uint64_t *p = (ca_u_uint64_t *)dst;
49
49
  for ( i = 0; i < n; i++ ) p[i] = v;
50
50
  break;
51
51
  }
52
52
  case 16: {
53
53
  /* complex128 etc. Two uint64_t stores per element. Compiler
54
54
  pairs them into a NEON 128-bit or SSE2 store. */
55
- uint64_t v0 = ((const uint64_t *)val)[0];
56
- uint64_t v1 = ((const uint64_t *)val)[1];
57
- uint64_t *p = (uint64_t *)dst;
55
+ uint64_t v0 = ((const ca_u_uint64_t *)val)[0];
56
+ uint64_t v1 = ((const ca_u_uint64_t *)val)[1];
57
+ ca_u_uint64_t *p = (ca_u_uint64_t *)dst;
58
58
  for ( i = 0; i < n; i++ ) {
59
59
  p[2*i] = v0;
60
60
  p[2*i + 1] = v1;