carray 3.0.2 → 3.0.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (247) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +568 -0
  3. data/README.md +6 -6
  4. data/carray.gemspec +1 -1
  5. data/ext/ca_axis_descriptor.h +27 -24
  6. data/ext/ca_axis_dispatch.c +220 -0
  7. data/ext/ca_axis_group.c +201 -123
  8. data/ext/ca_binop_dispatch.c +46 -52
  9. data/ext/ca_binop_dispatch.h +1 -1
  10. data/ext/ca_categorical_iterator.c +249 -183
  11. data/ext/ca_compare.c +7 -12
  12. data/ext/ca_for_buffer.h +21 -68
  13. data/ext/ca_group_iter.c +38 -23
  14. data/ext/ca_kernel_iterator.c +1765 -2076
  15. data/ext/ca_kernel_iterator.h +584 -566
  16. data/ext/ca_monop_dispatch.c +86 -63
  17. data/ext/ca_monop_dispatch.h +5 -0
  18. data/ext/ca_obj_array.c +223 -56
  19. data/ext/ca_obj_bincmp.c +172 -138
  20. data/ext/ca_obj_binop.c +116 -230
  21. data/ext/ca_obj_bitarray.c +40 -26
  22. data/ext/ca_obj_bitfield.c +100 -68
  23. data/ext/ca_obj_block.c +19 -9
  24. data/ext/ca_obj_byte_swap.c +45 -46
  25. data/ext/ca_obj_const_string.c +269 -44
  26. data/ext/ca_obj_face.c +612 -89
  27. data/ext/ca_obj_face.h +111 -47
  28. data/ext/ca_obj_fake.c +36 -79
  29. data/ext/ca_obj_farray.c +7 -3
  30. data/ext/ca_obj_field.c +33 -8
  31. data/ext/ca_obj_fixlen_string.c +23 -24
  32. data/ext/ca_obj_grid.c +541 -210
  33. data/ext/ca_obj_meld.c +331 -111
  34. data/ext/ca_obj_moncmp.c +80 -65
  35. data/ext/ca_obj_monop.c +177 -149
  36. data/ext/ca_obj_object.c +664 -248
  37. data/ext/ca_obj_record.c +63 -23
  38. data/ext/ca_obj_reduce.c +18 -16
  39. data/ext/ca_obj_refer.c +140 -58
  40. data/ext/ca_obj_remap.c +85 -24
  41. data/ext/ca_obj_repeat.c +24 -9
  42. data/ext/ca_obj_roll.c +121 -16
  43. data/ext/ca_obj_select.c +61 -58
  44. data/ext/ca_obj_select_axis.c +73 -107
  45. data/ext/ca_obj_shift.c +32 -102
  46. data/ext/ca_obj_stack.c +372 -122
  47. data/ext/ca_obj_stride.c +348 -140
  48. data/ext/ca_obj_string.c +88 -22
  49. data/ext/ca_obj_tile.c +38 -21
  50. data/ext/ca_obj_time.c +45 -31
  51. data/ext/ca_obj_timedelta.c +43 -29
  52. data/ext/ca_obj_transpose.c +17 -6
  53. data/ext/ca_obj_triop.c +125 -141
  54. data/ext/ca_obj_window.c +330 -44
  55. data/ext/ca_op_ipower.c +24 -11
  56. data/ext/ca_op_powi.h +57 -30
  57. data/ext/ca_pow5_table.h +656 -0
  58. data/ext/ca_sort_kernels.h +70 -0
  59. data/ext/ca_sweep_engine.c +104 -81
  60. data/ext/ca_sweep_engine.h +7 -20
  61. data/ext/ca_triop_dispatch.c +4 -1
  62. data/ext/ca_triop_dispatch.h +5 -2
  63. data/ext/caframe_csv_reader.c +542 -0
  64. data/ext/carray.h +241 -92
  65. data/ext/carray_access.c +608 -276
  66. data/ext/carray_address_basis.c +494 -97
  67. data/ext/carray_attribute.c +129 -4
  68. data/ext/carray_bincount.c +39 -7
  69. data/ext/carray_broadcast.c +56 -19
  70. data/ext/carray_build_flags.h +1 -1
  71. data/ext/carray_call_cfunc.c +108 -28
  72. data/ext/carray_cast.c +1576 -223
  73. data/ext/carray_cast_func.rb +77 -24
  74. data/ext/carray_class.c +4 -3
  75. data/ext/carray_conversion.c +208 -75
  76. data/ext/carray_copy.c +82 -24
  77. data/ext/carray_core.c +646 -162
  78. data/ext/carray_count.c +171 -88
  79. data/ext/carray_element.c +94 -60
  80. data/ext/carray_factorize.c +670 -324
  81. data/ext/carray_generate.c +260 -83
  82. data/ext/carray_histogram.c +209 -106
  83. data/ext/carray_hold.c +14 -16
  84. data/ext/carray_index_classifier.c +422 -243
  85. data/ext/carray_internal.h +692 -3
  86. data/ext/carray_kernels_bincmp.c +7 -0
  87. data/ext/carray_kernels_binop.c +487 -120
  88. data/ext/carray_kernels_init.c +653 -50
  89. data/ext/carray_kernels_moncmp.c +15 -8
  90. data/ext/carray_kernels_monop.c +99 -43
  91. data/ext/carray_kernels_reduce_aggregate.c +3925 -6112
  92. data/ext/carray_kernels_reduce_boolean.c +112 -36
  93. data/ext/carray_kernels_reduce_cumulative.c +2127 -6903
  94. data/ext/carray_kernels_reduce_extreme.c +3032 -2892
  95. data/ext/carray_kernels_reduce_variance.c +439 -500
  96. data/ext/carray_kernels_scan.c +1097 -488
  97. data/ext/carray_kernels_search.c +3787 -2176
  98. data/ext/carray_kernels_sort.c +1200 -920
  99. data/ext/carray_kernels_triop.c +479 -7
  100. data/ext/carray_key_windows.c +216 -0
  101. data/ext/carray_lazy.c +967 -153
  102. data/ext/carray_loop.c +81 -65
  103. data/ext/carray_mask.c +910 -324
  104. data/ext/carray_math_kernel.h +1 -0
  105. data/ext/carray_median_percentile.c +299 -100
  106. data/ext/carray_memory_view.c +872 -758
  107. data/ext/carray_operator.c +1171 -364
  108. data/ext/carray_order.c +505 -133
  109. data/ext/carray_partition.c +112 -65
  110. data/ext/carray_random.c +327 -239
  111. data/ext/carray_round.c +273 -0
  112. data/ext/carray_scatter.c +346 -78
  113. data/ext/carray_segment.c +257 -0
  114. data/ext/carray_slab.c +249 -86
  115. data/ext/carray_sort.c +224 -227
  116. data/ext/carray_sort_kernel.c +8 -1
  117. data/ext/carray_struct.c +118 -39
  118. data/ext/carray_test.c +193 -69
  119. data/ext/carray_time_text.c +1182 -0
  120. data/ext/carray_undef.c +4 -0
  121. data/ext/carray_utils.c +579 -113
  122. data/ext/extconf.rb +27 -2
  123. data/ext/mk_call_cfunc.rb +19 -4
  124. data/ext/mk_pow5_table.rb +35 -0
  125. data/ext/mkkernel.rb +1202 -1014
  126. data/ext/ruby_carray.c +17 -5
  127. data/ext/version.h +4 -4
  128. data/ext/version.rb +4 -2
  129. data/lib/carray/attribute.rb +160 -17
  130. data/lib/carray/autoload_carray.rb +23 -27
  131. data/lib/carray/autoload_method_extension.rb +20 -5
  132. data/lib/carray/axis_group.rb +360 -63
  133. data/lib/carray/basics.rb +162 -28
  134. data/lib/carray/bincount_nd.rb +18 -2
  135. data/lib/carray/block_iterator.rb +231 -58
  136. data/lib/carray/boolean_reduce.rb +34 -42
  137. data/lib/carray/categorical.rb +242 -52
  138. data/lib/carray/categorical_iterator.rb +437 -827
  139. data/lib/carray/complex.rb +41 -34
  140. data/lib/carray/conditional.rb +150 -14
  141. data/lib/carray/const_string.rb +59 -4
  142. data/lib/carray/construct.rb +15 -11
  143. data/lib/carray/data_type_extension.rb +1 -0
  144. data/lib/carray/distance_limit.rb +77 -0
  145. data/lib/carray/fixlen_string.rb +7 -1
  146. data/lib/carray/frame/column_verbs.rb +70 -0
  147. data/lib/carray/frame/concat.rb +157 -6
  148. data/lib/carray/frame/convert.rb +82 -7
  149. data/lib/carray/frame/csv_parser.rb +688 -87
  150. data/lib/carray/frame/describe.rb +80 -0
  151. data/lib/carray/frame/frame.rb +369 -45
  152. data/lib/carray/frame/group.rb +631 -90
  153. data/lib/carray/frame/io.rb +506 -79
  154. data/lib/carray/frame/join.rb +411 -62
  155. data/lib/carray/frame/records.rb +116 -19
  156. data/lib/carray/frame/reshape.rb +455 -0
  157. data/lib/carray/frame/sort.rb +30 -18
  158. data/lib/carray/frame/verbs.rb +868 -61
  159. data/lib/carray/frame/windows.rb +117 -0
  160. data/lib/carray/frame.rb +4 -0
  161. data/lib/carray/fuse_source.rb +118 -2
  162. data/lib/carray/fusion.rb +257 -42
  163. data/lib/carray/histogram.rb +6 -1
  164. data/lib/carray/inspect.rb +54 -13
  165. data/lib/carray/iterator.rb +99 -9
  166. data/lib/carray/key_windows.rb +620 -0
  167. data/lib/carray/lazy.rb +216 -193
  168. data/lib/carray/mask_gap_fill.rb +112 -92
  169. data/lib/carray/math.rb +32 -19
  170. data/lib/carray/meld_reduce.rb +52 -21
  171. data/lib/carray/methods/align_addr.rb +8 -4
  172. data/lib/carray/methods/bin.rb +26 -18
  173. data/lib/carray/methods/bincount.rb +18 -2
  174. data/lib/carray/methods/bit_string.rb +15 -4
  175. data/lib/carray/methods/choose.rb +16 -3
  176. data/lib/carray/methods/composition.rb +18 -10
  177. data/lib/carray/methods/factorize.rb +2 -2
  178. data/lib/carray/methods/gather_nd.rb +25 -5
  179. data/lib/carray/methods/insert_block.rb +9 -14
  180. data/lib/carray/methods/is_between.rb +59 -0
  181. data/lib/carray/methods/is_in.rb +140 -10
  182. data/lib/carray/methods/join.rb +2 -7
  183. data/lib/carray/methods/lag.rb +49 -0
  184. data/lib/carray/methods/locate_addr.rb +182 -45
  185. data/lib/carray/methods/mode.rb +18 -1
  186. data/lib/carray/methods/nunique.rb +11 -6
  187. data/lib/carray/methods/repeat.rb +1 -20
  188. data/lib/carray/methods/resize.rb +21 -23
  189. data/lib/carray/methods/snap.rb +34 -12
  190. data/lib/carray/methods/unique.rb +11 -2
  191. data/lib/carray/methods/value_counts.rb +4 -11
  192. data/lib/carray/record_order.rb +208 -0
  193. data/lib/carray/rng.rb +3 -0
  194. data/lib/carray/runtime.rb +65 -17
  195. data/lib/carray/segment_iterator.rb +1221 -0
  196. data/lib/carray/serialize.rb +214 -30
  197. data/lib/carray/slab_iterator.rb +101 -27
  198. data/lib/carray/stack.rb +10 -4
  199. data/lib/carray/string.rb +105 -5
  200. data/lib/carray/string_operation_extension.rb +12 -4
  201. data/lib/carray/struct.rb +137 -58
  202. data/lib/carray/struct_builder.rb +124 -17
  203. data/lib/carray/time.rb +1222 -186
  204. data/lib/carray/window_iterator.rb +242 -81
  205. data/lib/carray.rb +9 -2
  206. data/yard-stubs/ca_obj_array.rb +4 -4
  207. data/yard-stubs/ca_obj_bitarray.rb +7 -0
  208. data/yard-stubs/ca_obj_bitfield.rb +23 -12
  209. data/yard-stubs/ca_obj_fake.rb +1 -1
  210. data/yard-stubs/ca_obj_field.rb +5 -1
  211. data/yard-stubs/ca_obj_grid.rb +21 -10
  212. data/yard-stubs/ca_obj_refer.rb +5 -4
  213. data/yard-stubs/ca_obj_roll.rb +2 -3
  214. data/yard-stubs/ca_obj_shift.rb +5 -1
  215. data/yard-stubs/ca_obj_stride.rb +10 -7
  216. data/yard-stubs/ca_obj_tile.rb +1 -1
  217. data/yard-stubs/ca_obj_transpose.rb +1 -1
  218. data/yard-stubs/ca_obj_window.rb +9 -5
  219. data/yard-stubs/carray_access.rb +11 -5
  220. data/yard-stubs/carray_attribute.rb +0 -5
  221. data/yard-stubs/carray_broadcast.rb +1 -1
  222. data/yard-stubs/carray_cast.rb +88 -43
  223. data/yard-stubs/carray_conversion.rb +15 -2
  224. data/yard-stubs/carray_copy.rb +12 -5
  225. data/yard-stubs/carray_core.rb +3 -3
  226. data/yard-stubs/carray_count.rb +9 -0
  227. data/yard-stubs/carray_element.rb +10 -4
  228. data/yard-stubs/carray_generate.rb +21 -1
  229. data/yard-stubs/carray_lazy.rb +5 -1
  230. data/yard-stubs/carray_loop.rb +4 -1
  231. data/yard-stubs/carray_mask.rb +60 -12
  232. data/yard-stubs/carray_math.rb +52 -14
  233. data/yard-stubs/carray_median_percentile.rb +15 -8
  234. data/yard-stubs/carray_memory_view.rb +40 -23
  235. data/yard-stubs/carray_order.rb +61 -29
  236. data/yard-stubs/carray_random.rb +13 -4
  237. data/yard-stubs/carray_scatter.rb +21 -9
  238. data/yard-stubs/carray_segment.rb +72 -0
  239. data/yard-stubs/carray_slab.rb +10 -2
  240. data/yard-stubs/carray_sort.rb +49 -8
  241. data/yard-stubs/carray_test.rb +15 -3
  242. data/yard-stubs/carray_utils.rb +0 -26
  243. metadata +19 -5
  244. data/ext/ca_for_each_element.h +0 -239
  245. data/ext/carray_kernels_map.c +0 -3467
  246. data/lib/carray/arrow_tensor.rb +0 -401
  247. data/lib/carray/table.rb +0 -108
data/ext/carray_scatter.c CHANGED
@@ -9,14 +9,19 @@
9
9
  duplicates unbuffered (sequential) — collisions accumulate
10
10
  mask pair skipped when any of addrs[i] / vals[i] / self[addrs[i]] is masked
11
11
  cast vals silently cast to self.data_type
12
- data type arithmetic (add / sub / mul / min / max): numeric only
12
+ data type add / sub / mul: numeric, complex included
13
+ min / max: real numeric only (complex has no order)
13
14
  (boolean / object / fixlen → CADataTypeError; the bang
14
15
  cannot widen self, same rationale as fma! / fms!)
15
16
  replace: numeric or boolean (assignment, no widening)
17
+ scalar any Numeric, true / false, or UNDEF; converted to self's
18
+ type as the indexer's store converts it (a value that does
19
+ not fit raises, as `self[i] = v` does)
16
20
 
17
21
  --------------------------------------------------------------------------- */
18
22
 
19
23
  #include "carray.h"
24
+ #include "carray_internal.h" /* ca_attach_window */
20
25
 
21
26
  /* ---------- common kernel macros ----------
22
27
 
@@ -26,8 +31,8 @@
26
31
  APPLY(qp, v) := one of:
27
32
  OP_ADD *(qp) += (v)
28
33
  OP_SUB *(qp) -= (v)
29
- OP_MIN_FLT if ((v) < *(qp) || *(qp) != *(qp)) *(qp) = (v) (fmin policy)
30
- OP_MAX_FLT if ((v) > *(qp) || *(qp) != *(qp)) *(qp) = (v)
34
+ OP_MIN_FLT a NaN loses (CA_EXTREMUM_REPLACES, carray_internal.h)
35
+ OP_MAX_FLT likewise
31
36
  OP_MIN_INT if ((v) < *(qp)) *(qp) = (v)
32
37
  OP_MAX_INT if ((v) > *(qp)) *(qp) = (v)
33
38
  */
@@ -36,14 +41,30 @@
36
41
  #define OP_SUB(qp, v) (*(qp) -= (v))
37
42
  #define OP_MUL(qp, v) (*(qp) *= (v))
38
43
  #define OP_REPLACE(qp, v) (*(qp) = (v))
39
- #define OP_MIN_FLT(qp, v) do { if ((v) < *(qp) || *(qp) != *(qp)) *(qp) = (v); } while (0)
40
- #define OP_MAX_FLT(qp, v) do { if ((v) > *(qp) || *(qp) != *(qp)) *(qp) = (v); } while (0)
44
+ #define OP_MIN_FLT(qp, v) do { if ( CA_EXTREMUM_REPLACES((v), *(qp), <) ) *(qp) = (v); } while (0)
45
+ #define OP_MAX_FLT(qp, v) do { if ( CA_EXTREMUM_REPLACES((v), *(qp), >) ) *(qp) = (v); } while (0)
41
46
  #define OP_MIN_INT(qp, v) do { if ((v) < *(qp)) *(qp) = (v); } while (0)
42
47
  #define OP_MAX_INT(qp, v) do { if ((v) > *(qp)) *(qp) = (v); } while (0)
43
48
 
44
- #define LOOP_SCALAR(T, APPLY) do { \
45
- T *q = (T *) ca->ptr; \
46
- T vs = (T) (v_is_float ? (double)vd : (double)vl); \
49
+ /* The scalar operand, already converted to self's type by rb_ca_obj2ptr
50
+ (the indexer's store conversion) and held as one cell in vcell. */
51
+ typedef union {
52
+ char bytes[16];
53
+ double d;
54
+ int64_t l;
55
+ #ifdef HAVE_COMPLEX_H
56
+ cmplx128_t z;
57
+ #endif
58
+ } ca_scatter_cell_t;
59
+
60
+ #define SCALAR_REAL(T) (*(const T *) vcell.bytes)
61
+ #define SCALAR_CMPLX(T) (*(const T *) vcell.bytes)
62
+
63
+ #define LOOP_SCALAR(T, APPLY) LOOP_SCALAR_V(T, APPLY, SCALAR_REAL(T))
64
+
65
+ #define LOOP_SCALAR_V(T, APPLY, VS) do { \
66
+ T *q = (T *) qbase; \
67
+ T vs = VS; \
47
68
  if ( mself ) { \
48
69
  for (i = 0; i < n; i++) { \
49
70
  if ( maddrs && maddrs[i] ) continue; \
@@ -64,7 +85,7 @@
64
85
  } while (0)
65
86
 
66
87
  #define LOOP_VEC(T, APPLY) do { \
67
- T *q = (T *) ca->ptr; \
88
+ T *q = (T *) qbase; \
68
89
  T *r = (T *) cv->ptr; \
69
90
  if ( mself ) { \
70
91
  for (i = 0; i < n; i++) { \
@@ -93,23 +114,29 @@
93
114
  both value and mask (indexer contract, `self[addrs] = vals`).
94
115
  - masked vals[i] flips target to masked (writes UNDEF); valid
95
116
  vals[i] writes value and clears the target's mask bit.
96
- Scalar vals is always valid (Fixnum / Float), so scalar path just
97
- writes and clears target mask. */
117
+ A scalar UNDEF masks every target (self's mask exists by then); any
118
+ other scalar writes and clears the target's mask. */
98
119
 
99
- #define LOOP_SCALAR_REPLACE(T) do { \
100
- T *q = (T *) ca->ptr; \
101
- T vs = (T) (v_is_float ? (double)vd : (double)vl); \
120
+ #define LOOP_SCALAR_REPLACE(T) LOOP_SCALAR_REPLACE_V(T, SCALAR_REAL(T))
121
+
122
+ #define LOOP_SCALAR_REPLACE_V(T, VS) do { \
123
+ T *q = (T *) qbase; \
124
+ T vs = VS; \
102
125
  for (i = 0; i < n; i++) { \
103
126
  if ( maddrs && maddrs[i] ) continue; \
104
127
  addr = p[i]; \
105
128
  CA_CHECK_INDEX(addr, elements); \
129
+ if ( vals_undef ) { \
130
+ mself[addr] = 1; \
131
+ continue; \
132
+ } \
106
133
  q[addr] = vs; \
107
134
  if ( mself ) mself[addr] = 0; \
108
135
  } \
109
136
  } while (0)
110
137
 
111
138
  #define LOOP_VEC_REPLACE(T) do { \
112
- T *q = (T *) ca->ptr; \
139
+ T *q = (T *) qbase; \
113
140
  T *r = (T *) cv->ptr; \
114
141
  for (i = 0; i < n; i++) { \
115
142
  if ( maddrs && maddrs[i] ) continue; \
@@ -137,13 +164,13 @@
137
164
  case CA_UINT32: if (vals_scalar) LOOP_SCALAR_REPLACE(uint32_t); else LOOP_VEC_REPLACE(uint32_t); break; \
138
165
  case CA_UINT16: if (vals_scalar) LOOP_SCALAR_REPLACE(uint16_t); else LOOP_VEC_REPLACE(uint16_t); break; \
139
166
  case CA_UINT8: if (vals_scalar) LOOP_SCALAR_REPLACE(uint8_t); else LOOP_VEC_REPLACE(uint8_t); break; \
167
+ CASES_CMPLX_REPLACE \
140
168
  default: \
141
169
  rb_bug("carray_scatter: unsupported data_type %d after numeric check", ca->data_type); \
142
170
  } \
143
171
  } while (0)
144
172
 
145
- #define DISPATCH_NUMERIC(APPLY_INT, APPLY_FLT) do { \
146
- switch ( ca->data_type ) { \
173
+ #define CASES_REAL(APPLY_INT, APPLY_FLT) \
147
174
  case CA_FLOAT64: if (vals_scalar) LOOP_SCALAR(double, APPLY_FLT); else LOOP_VEC(double, APPLY_FLT); break; \
148
175
  case CA_FLOAT32: if (vals_scalar) LOOP_SCALAR(float, APPLY_FLT); else LOOP_VEC(float, APPLY_FLT); break; \
149
176
  case CA_INT64: if (vals_scalar) LOOP_SCALAR(int64_t, APPLY_INT); else LOOP_VEC(int64_t, APPLY_INT); break; \
@@ -153,121 +180,361 @@
153
180
  case CA_UINT64: if (vals_scalar) LOOP_SCALAR(uint64_t, APPLY_INT); else LOOP_VEC(uint64_t, APPLY_INT); break; \
154
181
  case CA_UINT32: if (vals_scalar) LOOP_SCALAR(uint32_t, APPLY_INT); else LOOP_VEC(uint32_t, APPLY_INT); break; \
155
182
  case CA_UINT16: if (vals_scalar) LOOP_SCALAR(uint16_t, APPLY_INT); else LOOP_VEC(uint16_t, APPLY_INT); break; \
156
- case CA_UINT8: if (vals_scalar) LOOP_SCALAR(uint8_t, APPLY_INT); else LOOP_VEC(uint8_t, APPLY_INT); break; \
183
+ case CA_UINT8: if (vals_scalar) LOOP_SCALAR(uint8_t, APPLY_INT); else LOOP_VEC(uint8_t, APPLY_INT); break;
184
+
185
+ #ifdef HAVE_COMPLEX_H
186
+ #define CASES_CMPLX(APPLY) \
187
+ case CA_CMPLX64: if (vals_scalar) LOOP_SCALAR_V(cmplx64_t, APPLY, SCALAR_CMPLX(cmplx64_t)); \
188
+ else LOOP_VEC(cmplx64_t, APPLY); break; \
189
+ case CA_CMPLX128: if (vals_scalar) LOOP_SCALAR_V(cmplx128_t, APPLY, SCALAR_CMPLX(cmplx128_t)); \
190
+ else LOOP_VEC(cmplx128_t, APPLY); break;
191
+ #define CASES_CMPLX_REPLACE \
192
+ case CA_CMPLX64: if (vals_scalar) LOOP_SCALAR_REPLACE_V(cmplx64_t, SCALAR_CMPLX(cmplx64_t)); \
193
+ else LOOP_VEC_REPLACE(cmplx64_t); break; \
194
+ case CA_CMPLX128: if (vals_scalar) LOOP_SCALAR_REPLACE_V(cmplx128_t, SCALAR_CMPLX(cmplx128_t)); \
195
+ else LOOP_VEC_REPLACE(cmplx128_t); break;
196
+ #else
197
+ #define CASES_CMPLX(APPLY)
198
+ #define CASES_CMPLX_REPLACE
199
+ #endif
200
+
201
+ /* add / sub / mul: every numeric type, complex included. */
202
+ #define DISPATCH_ARITH(APPLY) do { \
203
+ switch ( ca->data_type ) { \
204
+ CASES_REAL(APPLY, APPLY) \
205
+ CASES_CMPLX(APPLY) \
206
+ default: \
207
+ rb_bug("carray_scatter: unsupported data_type %d after numeric check", ca->data_type); \
208
+ } \
209
+ } while (0)
210
+
211
+ /* min / max: real types only; the setup has already refused complex. */
212
+ #define DISPATCH_ORDERED(APPLY_INT, APPLY_FLT) do { \
213
+ switch ( ca->data_type ) { \
214
+ CASES_REAL(APPLY_INT, APPLY_FLT) \
157
215
  default: \
158
- /* numeric check passed before dispatch; complex types fall through to bug */ \
159
216
  rb_bug("carray_scatter: unsupported data_type %d after numeric check", ca->data_type); \
160
217
  } \
161
218
  } while (0)
162
219
 
163
- /* Setup boilerplate: parse args, wrap addrs/vals, attach.
220
+ /* A value that is one cell for every address: a Numeric, true / false,
221
+ or UNDEF. Anything else (a CArray, an Array, ...) is coerced to an
222
+ array of values. */
223
+ static int
224
+ ca_scatter_is_scalar (VALUE v)
225
+ {
226
+ return v == CA_UNDEF || v == Qtrue || v == Qfalse
227
+ || rb_obj_is_kind_of(v, rb_cNumeric);
228
+ }
229
+
230
+ /* Setup boilerplate: parse args and wrap addrs/vals.
164
231
 
165
232
  Outputs into caller's locals:
166
- ca, ci, cv (CArray*), n, elements (ca_size_t),
167
- p, maddrs, mvals, mself (pointers),
168
- vals_scalar (int), v_is_float (int), vd (double), vl (long).
233
+ ca, ci, cv (CArray*), n (ca_size_t),
234
+ vals_scalar, vals_undef (int), vcell (the scalar as one cell).
235
+
236
+ The kernel then runs through AT_RUN, and an address out of range
237
+ raising part way still leaves the cells already written.
169
238
 
170
- Caller must `ca_sync(ca); ca_detach_n(...);` after kernel.
239
+ An address or value array that shares self's root is copied first:
240
+ read in place, it would show the kernel cells it has already written.
171
241
  */
172
242
  /* Body shared by arithmetic and replace setups. Caller precondition:
173
243
  the data_type gate has already run (arithmetic = numeric only, replace
174
- = numeric or boolean). For the replace variant, `true` / `false`
175
- scalars are accepted as vals (bridged as vl = 1 / 0). */
176
- #define AT_SETUP_BODY(name, allow_bool_scalar) \
244
+ = numeric or boolean). A scalar is converted the way `self[i] = v`
245
+ converts it, so true / false reach only a boolean self and a value that
246
+ does not fit raises. A scalar UNDEF is a masked value: the arithmetic
247
+ family skips every pair, replace masks every target. */
248
+ #define AT_SETUP_BODY(name) \
249
+ ca_check_index_array(raddrs); \
177
250
  raddrs = rb_ca_wrap_readonly(raddrs, INT2NUM(CA_SIZE)); \
178
251
  TypedData_Get_Struct(raddrs, CArray, &carray_data_type, ci); \
252
+ if ( ca_shares_root(ci, ca) ) { \
253
+ raddrs = rb_ca_copy(raddrs); \
254
+ TypedData_Get_Struct(raddrs, CArray, &carray_data_type, ci); \
255
+ } \
179
256
  n = ci->elements; \
180
257
  if ( n == 0 ) return self; \
181
- vals_scalar = (RB_FLOAT_TYPE_P(rvals) || FIXNUM_P(rvals) \
182
- || ((allow_bool_scalar) && (rvals == Qtrue || rvals == Qfalse))); \
258
+ vals_scalar = ca_scatter_is_scalar(rvals); \
183
259
  if ( vals_scalar ) { \
184
- if ( RB_FLOAT_TYPE_P(rvals) ) { vd = RFLOAT_VALUE(rvals); v_is_float = 1; } \
185
- else if ( FIXNUM_P(rvals) ) { vl = FIX2LONG(rvals); v_is_float = 0; } \
186
- else { vl = (rvals == Qtrue) ? 1 : 0; v_is_float = 0; } \
260
+ if ( rvals == CA_UNDEF ) { \
261
+ vals_undef = 1; \
262
+ } \
263
+ else { \
264
+ rb_ca_obj2ptr(self, rvals, vcell.bytes); \
265
+ } \
187
266
  } \
188
267
  else { \
268
+ /* coerce: a store into the receiver */ \
189
269
  rvals = rb_ca_wrap_readonly(rvals, INT2NUM(ca->data_type)); \
190
270
  TypedData_Get_Struct(rvals, CArray, &carray_data_type, cv); \
271
+ if ( ca_shares_root(cv, ca) ) { \
272
+ rvals = rb_ca_copy(rvals); \
273
+ TypedData_Get_Struct(rvals, CArray, &carray_data_type, cv); \
274
+ } \
191
275
  if ( cv->elements != n ) { \
192
276
  rb_raise(rb_eArgError, \
193
277
  name ": vals length (%lld) doesn't match addrs length (%lld)", \
194
278
  (long long)cv->elements, (long long)n); \
195
279
  } \
196
- } \
197
- if ( vals_scalar ) ca_attach_n(2, ca, ci); \
198
- else ca_attach_n(3, ca, ci, cv); \
199
- p = (ca_size_t *) ci->ptr; \
200
- maddrs = ci->mask ? (boolean8_t *) ci->mask->ptr : NULL; \
201
- mvals = (cv && cv->mask) ? (boolean8_t *) cv->mask->ptr : NULL; \
202
- mself = ca->mask ? (boolean8_t *) ca->mask->ptr : NULL; \
203
- elements = ca->elements;
204
-
205
- #define AT_SETUP_OR_RETURN(name) \
280
+ }
281
+
282
+ #define AT_SETUP_LOCALS \
206
283
  CArray *ca, *ci, *cv = NULL; \
207
- ca_size_t i, n, addr, elements; \
208
- ca_size_t *p; \
209
- boolean8_t *maddrs, *mvals = NULL, *mself; \
210
- int vals_scalar, v_is_float = 0; \
211
- double vd = 0.0; long vl = 0; \
284
+ ca_size_t n; \
285
+ int vals_scalar, vals_undef = 0; \
286
+ ca_scatter_cell_t vcell = {{0}};
287
+
288
+ #define AT_SETUP_OR_RETURN(name, ordered) \
289
+ AT_SETUP_LOCALS \
212
290
  rb_ca_modify(self); \
213
291
  TypedData_Get_Struct(self, CArray, &carray_data_type, ca); \
214
292
  if ( ! ca_is_numeric_type(ca) ) { \
215
293
  rb_raise(rb_eCADataTypeError, name " requires a numeric array"); \
216
294
  } \
217
- AT_SETUP_BODY(name, 0)
295
+ if ( (ordered) && ca_is_complex_type(ca) ) { \
296
+ rb_raise(rb_eCADataTypeError, \
297
+ name " requires a real array (complex values have no order)"); \
298
+ } \
299
+ AT_SETUP_BODY(name) \
300
+ if ( vals_undef ) return self;
218
301
 
219
302
  /* replace variant: accepts boolean self (assignment, no widening) and
220
303
  Ruby true / false as scalar vals. */
221
304
  #define AT_SETUP_OR_RETURN_REPLACE(name) \
222
- CArray *ca, *ci, *cv = NULL; \
223
- ca_size_t i, n, addr, elements; \
224
- ca_size_t *p; \
225
- boolean8_t *maddrs, *mvals = NULL, *mself; \
226
- int vals_scalar, v_is_float = 0; \
227
- double vd = 0.0; long vl = 0; \
305
+ AT_SETUP_LOCALS \
228
306
  rb_ca_modify(self); \
229
307
  TypedData_Get_Struct(self, CArray, &carray_data_type, ca); \
230
308
  if ( ! ca_is_numeric_type(ca) && ca->data_type != CA_BOOLEAN ) { \
231
309
  rb_raise(rb_eCADataTypeError, \
232
310
  name " requires a numeric or boolean array"); \
233
311
  } \
234
- AT_SETUP_BODY(name, 1)
312
+ AT_SETUP_BODY(name) \
313
+ if ( vals_undef ) ca_create_mask(ca);
314
+
315
+ /* The kernel works either on self's own memory or on a compact region:
316
+ the distinct cells the addresses touch, read out of self and written
317
+ back to the same cells. In the region form `region` is set, the
318
+ addresses are replaced by slots into it, and skip marks the pairs the
319
+ kernel must pass over (a masked address, or any pair at or after the
320
+ first address out of range). */
321
+ typedef struct {
322
+ char *data;
323
+ boolean8_t *mask;
324
+ ca_size_t m;
325
+ ca_size_t *slots;
326
+ boolean8_t *skip;
327
+ ca_size_t n;
328
+ } ca_scatter_region_t;
329
+
330
+ typedef struct {
331
+ CArray *ca, *ci, *cv;
332
+ ca_size_t n;
333
+ int vals_scalar, vals_undef;
334
+ ca_scatter_cell_t vcell;
335
+ ca_scatter_region_t *region;
336
+ } ca_scatter_ctx_t;
337
+
338
+ /* Defines fname, the kernel body running DISPATCH with the names the
339
+ loop macros expect. */
340
+ #define AT_BODY(fname, DISPATCH) \
341
+ static VALUE \
342
+ fname (VALUE arg) \
343
+ { \
344
+ ca_scatter_ctx_t *c_ = (ca_scatter_ctx_t *) arg; \
345
+ ca_scatter_region_t *rg_ = c_->region; \
346
+ CArray *ca = c_->ca, *ci = c_->ci, *cv = c_->cv; \
347
+ ca_size_t i, addr; \
348
+ ca_size_t n = rg_ ? rg_->n : c_->n; \
349
+ ca_size_t elements = rg_ ? rg_->m : ca->elements; \
350
+ char *qbase = rg_ ? rg_->data : ca->ptr; \
351
+ ca_size_t *p = rg_ ? rg_->slots : (ca_size_t *) ci->ptr; \
352
+ boolean8_t *maddrs = rg_ ? rg_->skip \
353
+ : (ci->mask ? (boolean8_t *) ci->mask->ptr : NULL); \
354
+ boolean8_t *mvals = (cv && cv->mask) ? (boolean8_t *) cv->mask->ptr : NULL; \
355
+ boolean8_t *mself = rg_ ? rg_->mask \
356
+ : (ca->mask ? (boolean8_t *) ca->mask->ptr : NULL); \
357
+ int vals_scalar = c_->vals_scalar, vals_undef = c_->vals_undef; \
358
+ ca_scatter_cell_t vcell = c_->vcell; \
359
+ (void) cv; (void) mvals; (void) vals_undef; (void) vcell; \
360
+ DISPATCH; \
361
+ return Qnil; \
362
+ }
363
+
364
+ static int
365
+ ca_scatter_cmp_addr (const void *a, const void *b)
366
+ {
367
+ ca_size_t x = *(const ca_size_t *) a, y = *(const ca_size_t *) b;
368
+ return (x > y) - (x < y);
369
+ }
370
+
371
+ typedef struct {
372
+ ca_scatter_ctx_t *ctx;
373
+ VALUE (*kernel)(VALUE);
374
+ } ca_scatter_region_arg_t;
375
+
376
+ /* Window body for a self that does not lend its memory (a selection, a
377
+ converting view, ...). Attaching such a self would materialise every
378
+ cell and push every cell back, so cells the addresses never name would
379
+ make a round trip through the view -- and come back changed when the
380
+ view converts lossily. Only the cells named are read and written.
235
381
 
236
- #define AT_TEARDOWN() do { \
237
- ca_sync(ca); \
238
- if ( vals_scalar ) ca_detach_n(2, ca, ci); \
239
- else ca_detach_n(3, ca, ci, cv); \
382
+ The pairs before the first address out of range are applied, then the
383
+ range error is raised, as in the direct form. */
384
+ static VALUE
385
+ ca_scatter_region_body (VALUE varg)
386
+ {
387
+ ca_scatter_region_arg_t *a = (ca_scatter_region_arg_t *) varg;
388
+ ca_scatter_ctx_t *c = a->ctx;
389
+ CArray *ca = c->ca, *ci = c->ci;
390
+ ca_size_t n = c->n, elements = ca->elements;
391
+ ca_size_t *p = (ca_size_t *) ci->ptr;
392
+ boolean8_t *maddrs = ci->mask ? (boolean8_t *) ci->mask->ptr : NULL;
393
+ ca_scatter_region_t rg;
394
+ ca_size_t *addrs, *uniq, i, nv, m, bad = 0;
395
+ boolean8_t *skip;
396
+ int self_masked = ca_has_mask(ca);
397
+ volatile VALUE h1 = 0, h2 = 0, h3 = 0, h4 = 0, h5 = 0;
398
+
399
+ addrs = ALLOCV_N(ca_size_t, h1, n);
400
+ skip = ALLOCV_N(boolean8_t, h2, n);
401
+ uniq = ALLOCV_N(ca_size_t, h3, n);
402
+
403
+ /* Normalise the addresses up to the first one out of range. */
404
+ m = 0;
405
+ for (nv = 0; nv < n; nv++) {
406
+ ca_size_t addr = p[nv];
407
+ if ( maddrs && maddrs[nv] ) {
408
+ skip[nv] = 1;
409
+ continue;
410
+ }
411
+ if ( addr < 0 ) {
412
+ addr += elements;
413
+ }
414
+ if ( addr < 0 || addr >= elements ) {
415
+ bad = p[nv];
416
+ break;
417
+ }
418
+ skip[nv] = 0;
419
+ addrs[nv] = addr;
420
+ uniq[m++] = addr;
421
+ }
422
+
423
+ qsort(uniq, (size_t) m, sizeof(ca_size_t), ca_scatter_cmp_addr);
424
+ if ( m > 0 ) {
425
+ ca_size_t k = 1;
426
+ for (i = 1; i < m; i++) {
427
+ if ( uniq[i] != uniq[k-1] ) {
428
+ uniq[k++] = uniq[i];
429
+ }
430
+ }
431
+ m = k;
432
+ }
433
+
434
+ /* Each address becomes its slot in uniq. */
435
+ for (i = 0; i < nv; i++) {
436
+ if ( ! skip[i] ) {
437
+ ca_size_t *hit = bsearch(&addrs[i], uniq, (size_t) m, sizeof(ca_size_t),
438
+ ca_scatter_cmp_addr);
439
+ addrs[i] = hit - uniq;
440
+ }
441
+ }
442
+
443
+ rg.m = m;
444
+ rg.slots = addrs;
445
+ rg.skip = skip;
446
+ rg.n = nv;
447
+ rg.data = ALLOCV(h4, (size_t) (m > 0 ? m : 1) * ca->bytes);
448
+ rg.mask = NULL;
449
+
450
+ if ( m > 0 ) {
451
+ ca_xfer_addrs(ca, m, uniq, rg.data, CA_XFER_GET);
452
+ if ( self_masked ) {
453
+ ca_update_mask(ca);
454
+ rg.mask = ALLOCV_N(boolean8_t, h5, m);
455
+ ca_xfer_addrs(ca->mask, m, uniq, rg.mask, CA_XFER_GET);
456
+ }
457
+ c->region = &rg;
458
+ a->kernel((VALUE) c);
459
+ c->region = NULL;
460
+ ca_xfer_addrs(ca, m, uniq, rg.data, CA_XFER_PUT);
461
+ if ( rg.mask ) {
462
+ ca_xfer_addrs(ca->mask, m, uniq, rg.mask, CA_XFER_PUT);
463
+ }
464
+ }
465
+
466
+ if ( nv < n ) {
467
+ CA_CHECK_INDEX(bad, elements); /* raises */
468
+ }
469
+
470
+ if ( h5 ) { ALLOCV_END(h5); }
471
+ ALLOCV_END(h4);
472
+ ALLOCV_END(h3);
473
+ ALLOCV_END(h2);
474
+ ALLOCV_END(h1);
475
+ return Qnil;
476
+ }
477
+
478
+ /* Self is written ('w'); the addresses and the values are read ('r').
479
+ A self that lends its memory is written in place; any other self goes
480
+ through the region form above, with only the addresses and the values
481
+ opened. */
482
+ #define AT_RUN(body) do { \
483
+ ca_scatter_ctx_t c_; \
484
+ CArray *list_[3]; \
485
+ c_.ca = ca; c_.ci = ci; c_.cv = cv; c_.n = n; \
486
+ c_.vals_scalar = vals_scalar; c_.vals_undef = vals_undef; \
487
+ c_.vcell = vcell; \
488
+ c_.region = NULL; \
489
+ if ( ca_attach_is_alias(ca) ) { \
490
+ list_[0] = ca; list_[1] = ci; list_[2] = cv; \
491
+ ca_attach_window(vals_scalar ? 2 : 3, list_, "wrr", body, (VALUE) &c_); \
492
+ } \
493
+ else { \
494
+ ca_scatter_region_arg_t ra_; \
495
+ ra_.ctx = &c_; ra_.kernel = body; \
496
+ list_[0] = ci; list_[1] = cv; \
497
+ ca_attach_window(vals_scalar ? 1 : 2, list_, "rr", \
498
+ ca_scatter_region_body, (VALUE) &ra_); \
499
+ } \
500
+ /* the views AT_SETUP_BODY made are reached only through ci and cv */ \
501
+ RB_GC_GUARD(raddrs); \
502
+ RB_GC_GUARD(rvals); \
240
503
  } while (0)
241
504
 
242
505
  /* --------------------------------------------------------------- */
243
506
 
507
+ AT_BODY(ca_scatter_add_body, DISPATCH_ARITH(OP_ADD))
508
+
244
509
  /* CArray#scatter_add!(addrs, vals) — for each i, self[addrs[i]] +=
245
510
  * vals[i] (or += vals when scalar). Duplicate addrs accumulate
246
511
  * (unbuffered), unlike self[addrs] += vals which is last-wins. */
247
512
  static VALUE
248
513
  rb_ca_scatter_add_bang (VALUE self, VALUE raddrs, VALUE rvals)
249
514
  {
250
- AT_SETUP_OR_RETURN("scatter_add!");
251
- DISPATCH_NUMERIC(OP_ADD, OP_ADD);
252
- AT_TEARDOWN();
515
+ AT_SETUP_OR_RETURN("scatter_add!", 0);
516
+ AT_RUN(ca_scatter_add_body);
253
517
  return self;
254
518
  }
255
519
 
256
520
  /* --------------------------------------------------------------- */
257
521
 
522
+ AT_BODY(ca_scatter_sub_body, DISPATCH_ARITH(OP_SUB))
523
+
258
524
  /* CArray#scatter_sub!(addrs, vals) — for each i, self[addrs[i]] -=
259
525
  * vals[i]. Same mask/cast/bounds policy as scatter_add!. */
260
526
  static VALUE
261
527
  rb_ca_scatter_sub_bang (VALUE self, VALUE raddrs, VALUE rvals)
262
528
  {
263
- AT_SETUP_OR_RETURN("scatter_sub!");
264
- DISPATCH_NUMERIC(OP_SUB, OP_SUB);
265
- AT_TEARDOWN();
529
+ AT_SETUP_OR_RETURN("scatter_sub!", 0);
530
+ AT_RUN(ca_scatter_sub_body);
266
531
  return self;
267
532
  }
268
533
 
269
534
  /* --------------------------------------------------------------- */
270
535
 
536
+ AT_BODY(ca_scatter_mul_body, DISPATCH_ARITH(OP_MUL))
537
+
271
538
  /* CArray#scatter_mul!(addrs, vals) — for each i, self[addrs[i]] *=
272
539
  * vals[i]. NaN/inf follow standard C arithmetic (no fmin-style
273
540
  * missing-value rule); integer overflow wraps. Otherwise identical
@@ -275,14 +542,15 @@ rb_ca_scatter_sub_bang (VALUE self, VALUE raddrs, VALUE rvals)
275
542
  static VALUE
276
543
  rb_ca_scatter_mul_bang (VALUE self, VALUE raddrs, VALUE rvals)
277
544
  {
278
- AT_SETUP_OR_RETURN("scatter_mul!");
279
- DISPATCH_NUMERIC(OP_MUL, OP_MUL);
280
- AT_TEARDOWN();
545
+ AT_SETUP_OR_RETURN("scatter_mul!", 0);
546
+ AT_RUN(ca_scatter_mul_body);
281
547
  return self;
282
548
  }
283
549
 
284
550
  /* --------------------------------------------------------------- */
285
551
 
552
+ AT_BODY(ca_scatter_min_body, DISPATCH_ORDERED(OP_MIN_INT, OP_MIN_FLT))
553
+
286
554
  /* CArray#scatter_min!(addrs, vals) — for each i, self[addrs[i]] =
287
555
  * min(self[addrs[i]], vals[i]). Float types follow the fmin rule
288
556
  * (NaN is treated as missing). Same mask/cast/bounds policy as
@@ -290,28 +558,30 @@ rb_ca_scatter_mul_bang (VALUE self, VALUE raddrs, VALUE rvals)
290
558
  static VALUE
291
559
  rb_ca_scatter_min_bang (VALUE self, VALUE raddrs, VALUE rvals)
292
560
  {
293
- AT_SETUP_OR_RETURN("scatter_min!");
294
- DISPATCH_NUMERIC(OP_MIN_INT, OP_MIN_FLT);
295
- AT_TEARDOWN();
561
+ AT_SETUP_OR_RETURN("scatter_min!", 1);
562
+ AT_RUN(ca_scatter_min_body);
296
563
  return self;
297
564
  }
298
565
 
299
566
  /* --------------------------------------------------------------- */
300
567
 
568
+ AT_BODY(ca_scatter_max_body, DISPATCH_ORDERED(OP_MAX_INT, OP_MAX_FLT))
569
+
301
570
  /* CArray#scatter_max!(addrs, vals) — for each i, self[addrs[i]] =
302
571
  * max(self[addrs[i]], vals[i]). Float types follow the fmax rule.
303
572
  * Otherwise identical to scatter_add!. */
304
573
  static VALUE
305
574
  rb_ca_scatter_max_bang (VALUE self, VALUE raddrs, VALUE rvals)
306
575
  {
307
- AT_SETUP_OR_RETURN("scatter_max!");
308
- DISPATCH_NUMERIC(OP_MAX_INT, OP_MAX_FLT);
309
- AT_TEARDOWN();
576
+ AT_SETUP_OR_RETURN("scatter_max!", 1);
577
+ AT_RUN(ca_scatter_max_body);
310
578
  return self;
311
579
  }
312
580
 
313
581
  /* --------------------------------------------------------------- */
314
582
 
583
+ AT_BODY(ca_scatter_replace_body, DISPATCH_NUMERIC_REPLACE())
584
+
315
585
  /* CArray#scatter_replace!(addrs, vals) — for each i, self[addrs[i]] =
316
586
  * vals[i] (or = vals when scalar). Semantically equivalent to
317
587
  * self[addrs] = vals (last-write-wins on duplicate addrs) but bypasses
@@ -325,8 +595,7 @@ rb_ca_scatter_replace_bang (VALUE self, VALUE raddrs, VALUE rvals)
325
595
  /* Pre-scan: if vals is a masked CArray and self isn't yet masked,
326
596
  promote self so the kernel can flip target cells to masked (indexer
327
597
  `self[addrs] = vals` establishes self.mask lazily the same way). */
328
- if ( ! (RB_FLOAT_TYPE_P(rvals) || FIXNUM_P(rvals))
329
- && rb_obj_is_carray(rvals) ) {
598
+ if ( rb_obj_is_carray(rvals) ) {
330
599
  CArray *cv_pre;
331
600
  TypedData_Get_Struct(rvals, CArray, &carray_data_type, cv_pre);
332
601
  if ( ca_has_mask(cv_pre) ) {
@@ -338,8 +607,7 @@ rb_ca_scatter_replace_bang (VALUE self, VALUE raddrs, VALUE rvals)
338
607
  }
339
608
  }
340
609
  AT_SETUP_OR_RETURN_REPLACE("scatter_replace!");
341
- DISPATCH_NUMERIC_REPLACE();
342
- AT_TEARDOWN();
610
+ AT_RUN(ca_scatter_replace_body);
343
611
  return self;
344
612
  }
345
613