carray 3.0.1 → 3.0.3

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