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_random.c CHANGED
@@ -18,6 +18,7 @@
18
18
  ---------------------------------------------------------------------------- */
19
19
 
20
20
  #include "carray.h"
21
+ #include "carray_internal.h" /* ca_attach_window */
21
22
  #include <math.h>
22
23
  #include <string.h>
23
24
  #include <stdint.h>
@@ -47,12 +48,12 @@ enum {
47
48
  typedef struct {
48
49
  int kind;
49
50
  VALUE rng;
50
- CArray *state; /* attached for CA_RNG_OWN, else NULL */
51
- int64_t *cells;
51
+ CArray *state; /* the CArray::Rng state for CA_RNG_OWN, else NULL */
52
+ int64_t *cells; /* state->ptr, once the window has attached it */
52
53
  } ca_rng_t;
53
54
 
54
55
  static void
55
- ca_rng_open (VALUE rng, ca_rng_t *source)
56
+ ca_rng_resolve (VALUE rng, ca_rng_t *source)
56
57
  {
57
58
  source->rng = rng;
58
59
  source->state = NULL;
@@ -64,27 +65,48 @@ ca_rng_open (VALUE rng, ca_rng_t *source)
64
65
  VALUE state = rb_ivar_get(rng, rb_intern("@state"));
65
66
  CArray *ca;
66
67
  TypedData_Get_Struct(state, CArray, &carray_data_type, ca);
67
- ca_attach(ca);
68
68
  source->kind = CA_RNG_OWN;
69
69
  source->state = ca;
70
- source->cells = (int64_t *) ca->ptr;
71
70
  }
72
71
  else {
73
72
  source->kind = CA_RNG_RUBY;
74
73
  }
75
74
  }
76
75
 
77
- /* Writes the advanced state back where it came from. Safe to call twice,
78
- which is what lets an error path close before it raises. */
76
+ /* The fills below draw inside a window over the array and, for a
77
+ CArray::Rng, the generator's state. A Ruby generator (rng: a
78
+ ::Random or anything with #rand) is called per draw and can raise part
79
+ way; the window then pushes back the cells drawn so far, and the state
80
+ they advanced, as it does when the fill completes. */
81
+
82
+ typedef struct {
83
+ CArray *ca;
84
+ ca_rng_t source;
85
+ ca_size_t n;
86
+ int is_default;
87
+ double low_dbl, high_dbl;
88
+ long low_long;
89
+ unsigned long limit;
90
+ int axis; /* shuffle!: -1 = all cells */
91
+ CArray *mask; /* shuffle!: self's mask, moved with the values */
92
+ } ca_random_ctx_t;
93
+
79
94
  static void
80
- ca_rng_close (ca_rng_t *source)
95
+ ca_random_run (ca_random_ctx_t *c, VALUE rng, VALUE (*fill)(VALUE))
81
96
  {
82
- if (source->state) {
83
- ca_sync(source->state);
84
- ca_detach(source->state);
85
- source->state = NULL;
86
- source->cells = NULL;
87
- }
97
+ CArray *list[3];
98
+ ca_rng_resolve(rng, &c->source);
99
+ list[0] = c->ca;
100
+ list[1] = c->source.state;
101
+ list[2] = c->mask;
102
+ ca_attach_window(c->mask ? 3 : 2, list, "www", fill, (VALUE) c);
103
+ }
104
+
105
+ /* The state is attached once the window is open. */
106
+ static void
107
+ ca_rng_bind (ca_rng_t *source)
108
+ {
109
+ source->cells = source->state ? (int64_t *) source->state->ptr : NULL;
88
110
  }
89
111
 
90
112
  static inline double
@@ -129,117 +151,28 @@ ca_random_ulong_limited (ca_rng_t *source, unsigned long limit)
129
151
 
130
152
  /* ---- random! ----------------------------------------------------------- */
131
153
 
132
- /* CArray#random!([low,] [high], rng:) — fill self with uniform random
133
- * numbers in-place, returning self. Range surface mirrors Numo/NumPy
134
- * (half-open [low, high) as the default) plus Ruby idiom (`..` closed,
135
- * `...` half-open):
136
- *
137
- * random! float [0.0, 1.0); integer -> ArgumentError
138
- * random!(high) [0, high) (Ruby rand shorthand)
139
- * random!(low, high) [low, high) (Numo positional)
140
- * random!(a..b) [a, b] closed (integer: b included)
141
- * random!(a...b) [a, b) half-open
142
- * random!(..., rng: r) any of the above with a custom Random source
143
- *
144
- * For float, `..` and `...` return the same distribution (endpoint
145
- * probability ~2^-53); the closed form is honored syntactically but not
146
- * enforced at the mantissa level, matching NumPy/SciPy convention.
147
- * Boolean fills 0/1 with 50% probability each and ignores the range
148
- * argument. Rejects CA_OBJECT / CA_FIXLEN. */
149
154
  static VALUE
150
- rb_ca_random_bang(int argc, VALUE *argv, VALUE self)
155
+ ca_random_bang_fill (VALUE arg)
151
156
  {
152
- CArray *ca;
153
- VALUE arg1 = Qnil, arg2 = Qnil, opts = Qnil;
154
- VALUE low_val = Qnil, high_val = Qnil;
155
- int is_default = 1;
156
- int high_is_closed = 0;
157
- double low_dbl = 0.0, high_dbl = 0.0;
158
- long low_long = 0, high_long = 0;
159
- unsigned long limit = 0;
160
- ca_size_t i, n;
161
- VALUE rng = Qnil;
162
- ca_rng_t source;
163
-
164
- rb_scan_args(argc, argv, "02:", &arg1, &arg2, &opts);
165
- rb_scan_options(opts, "rng", &rng);
166
-
167
- /* Parse positional args into (low_val, high_val) + high_is_closed. */
168
- if (rb_obj_is_kind_of(arg1, rb_cRange)) {
169
- if (!NIL_P(arg2)) {
170
- rb_raise(rb_eArgError,
171
- "random: cannot combine a Range with a second positional arg");
172
- }
173
- low_val = rb_funcall(arg1, rb_intern("begin"), 0);
174
- high_val = rb_funcall(arg1, rb_intern("end"), 0);
175
- if (NIL_P(low_val) || NIL_P(high_val)) {
176
- rb_raise(rb_eArgError,
177
- "random: Range must have finite begin and end");
178
- }
179
- high_is_closed = ! RTEST(rb_funcall(arg1, rb_intern("exclude_end?"), 0));
180
- is_default = 0;
181
- } else if (!NIL_P(arg1) && !NIL_P(arg2)) {
182
- low_val = arg1;
183
- high_val = arg2;
184
- is_default = 0;
185
- } else if (!NIL_P(arg1)) {
186
- low_val = INT2FIX(0);
187
- high_val = arg1;
188
- is_default = 0;
189
- }
190
-
191
- rb_ca_modify(self);
192
- TypedData_Get_Struct(self, CArray, &carray_data_type, ca);
193
-
194
- if (ca->data_type == CA_OBJECT || ca->data_type == CA_FIXLEN) {
195
- rb_raise(rb_eCADataTypeError,
196
- "random! is not supported for object/fixlen arrays");
197
- }
198
-
199
- /* Materialize (low, high) as the correct scalar type and validate.
200
- * For integer data types, `..` closed adds 1 to high (turns into half-open
201
- * for the sampler); for float data types, closed and half-open are
202
- * equivalent so no adjustment. */
203
- if (!is_default) {
204
- int is_integer_type = (ca->data_type >= CA_INT8
205
- && ca->data_type <= CA_UINT64);
206
- if (is_integer_type) {
207
- low_long = NUM2LONG(low_val);
208
- high_long = NUM2LONG(high_val);
209
- if (high_is_closed) high_long += 1;
210
- if (low_long >= high_long) {
211
- rb_raise(rb_eArgError,
212
- "random: low must be less than high "
213
- "(got low=%ld, high=%ld)",
214
- low_long, high_long);
215
- }
216
- limit = (unsigned long)(high_long - low_long) - 1;
217
- } else {
218
- low_dbl = NUM2DBL(low_val);
219
- high_dbl = NUM2DBL(high_val);
220
- if (low_dbl >= high_dbl) {
221
- rb_raise(rb_eArgError,
222
- "random: low must be less than high "
223
- "(got low=%g, high=%g)",
224
- low_dbl, high_dbl);
225
- }
226
- }
227
- }
228
-
229
- n = ca->elements;
230
- ca_attach(ca);
231
- ca_rng_open(rng, &source);
232
-
157
+ ca_random_ctx_t *c = (ca_random_ctx_t *) arg;
158
+ CArray *ca = c->ca;
159
+ ca_size_t i, n = c->n;
160
+ int is_default = c->is_default;
161
+ double low_dbl = c->low_dbl, high_dbl = c->high_dbl;
162
+ long low_long = c->low_long;
163
+ unsigned long limit = c->limit;
164
+
165
+ ca_rng_bind(&c->source);
233
166
  switch (ca->data_type) {
234
167
  case CA_FLOAT64: {
235
168
  double *p = (double *)ca->ptr;
236
169
  if (is_default) {
237
170
  for (i = 0; i < n; i++)
238
- p[i] = ca_random_real(&source);
171
+ p[i] = ca_random_real(&c->source);
239
172
  } else {
240
173
  double range = high_dbl - low_dbl;
241
174
  for (i = 0; i < n; i++)
242
- p[i] = low_dbl + ca_random_real(&source) * range;
175
+ p[i] = low_dbl + ca_random_real(&c->source) * range;
243
176
  }
244
177
  break;
245
178
  }
@@ -247,11 +180,11 @@ rb_ca_random_bang(int argc, VALUE *argv, VALUE self)
247
180
  float *p = (float *)ca->ptr;
248
181
  if (is_default) {
249
182
  for (i = 0; i < n; i++)
250
- p[i] = (float)ca_random_real(&source);
183
+ p[i] = (float)ca_random_real(&c->source);
251
184
  } else {
252
185
  double range = high_dbl - low_dbl;
253
186
  for (i = 0; i < n; i++)
254
- p[i] = (float)(low_dbl + ca_random_real(&source) * range);
187
+ p[i] = (float)(low_dbl + ca_random_real(&c->source) * range);
255
188
  }
256
189
  break;
257
190
  }
@@ -259,12 +192,12 @@ rb_ca_random_bang(int argc, VALUE *argv, VALUE self)
259
192
  double complex *p = (double complex *)ca->ptr;
260
193
  if (is_default) {
261
194
  for (i = 0; i < n; i++)
262
- p[i] = ca_random_real(&source) + ca_random_real(&source) * I;
195
+ p[i] = ca_random_real(&c->source) + ca_random_real(&c->source) * I;
263
196
  } else {
264
197
  double range = high_dbl - low_dbl;
265
198
  for (i = 0; i < n; i++)
266
- p[i] = (low_dbl + ca_random_real(&source) * range)
267
- + (low_dbl + ca_random_real(&source) * range) * I;
199
+ p[i] = (low_dbl + ca_random_real(&c->source) * range)
200
+ + (low_dbl + ca_random_real(&c->source) * range) * I;
268
201
  }
269
202
  break;
270
203
  }
@@ -272,27 +205,24 @@ rb_ca_random_bang(int argc, VALUE *argv, VALUE self)
272
205
  float complex *p = (float complex *)ca->ptr;
273
206
  if (is_default) {
274
207
  for (i = 0; i < n; i++)
275
- p[i] = (float)ca_random_real(&source) + (float)ca_random_real(&source) * I;
208
+ p[i] = (float)ca_random_real(&c->source) + (float)ca_random_real(&c->source) * I;
276
209
  } else {
277
210
  double range = high_dbl - low_dbl;
278
211
  for (i = 0; i < n; i++)
279
- p[i] = (float)(low_dbl + ca_random_real(&source) * range)
280
- + (float)(low_dbl + ca_random_real(&source) * range) * I;
212
+ p[i] = (float)(low_dbl + ca_random_real(&c->source) * range)
213
+ + (float)(low_dbl + ca_random_real(&c->source) * range) * I;
281
214
  }
282
215
  break;
283
216
  }
284
217
  case CA_BOOLEAN: {
285
218
  boolean8_t *p = (boolean8_t *)ca->ptr;
286
219
  for (i = 0; i < n; i++)
287
- p[i] = (ca_random_real(&source) < 0.5) ? 1 : 0;
220
+ p[i] = (ca_random_real(&c->source) < 0.5) ? 1 : 0;
288
221
  break;
289
222
  }
290
223
  default: {
291
224
  /* integer types: CA_INT8..CA_UINT64 */
292
225
  if (is_default) {
293
- ca_rng_close(&source);
294
- ca_sync(ca);
295
- ca_detach(ca);
296
226
  rb_raise(rb_eArgError,
297
227
  "random! on an integer array requires a range: "
298
228
  "a.random!(high), a.random!(low, high), or "
@@ -302,55 +232,52 @@ rb_ca_random_bang(int argc, VALUE *argv, VALUE self)
302
232
  case CA_INT8: {
303
233
  int8_t *p = (int8_t *)ca->ptr;
304
234
  for (i = 0; i < n; i++)
305
- p[i] = (int8_t)(low_long + ca_random_ulong_limited(&source, limit));
235
+ p[i] = (int8_t)(low_long + ca_random_ulong_limited(&c->source, limit));
306
236
  break;
307
237
  }
308
238
  case CA_UINT8: {
309
239
  uint8_t *p = (uint8_t *)ca->ptr;
310
240
  for (i = 0; i < n; i++)
311
- p[i] = (uint8_t)(low_long + ca_random_ulong_limited(&source, limit));
241
+ p[i] = (uint8_t)(low_long + ca_random_ulong_limited(&c->source, limit));
312
242
  break;
313
243
  }
314
244
  case CA_INT16: {
315
245
  int16_t *p = (int16_t *)ca->ptr;
316
246
  for (i = 0; i < n; i++)
317
- p[i] = (int16_t)(low_long + ca_random_ulong_limited(&source, limit));
247
+ p[i] = (int16_t)(low_long + ca_random_ulong_limited(&c->source, limit));
318
248
  break;
319
249
  }
320
250
  case CA_UINT16: {
321
251
  uint16_t *p = (uint16_t *)ca->ptr;
322
252
  for (i = 0; i < n; i++)
323
- p[i] = (uint16_t)(low_long + ca_random_ulong_limited(&source, limit));
253
+ p[i] = (uint16_t)(low_long + ca_random_ulong_limited(&c->source, limit));
324
254
  break;
325
255
  }
326
256
  case CA_INT32: {
327
257
  int32_t *p = (int32_t *)ca->ptr;
328
258
  for (i = 0; i < n; i++)
329
- p[i] = (int32_t)(low_long + ca_random_ulong_limited(&source, limit));
259
+ p[i] = (int32_t)(low_long + ca_random_ulong_limited(&c->source, limit));
330
260
  break;
331
261
  }
332
262
  case CA_UINT32: {
333
263
  uint32_t *p = (uint32_t *)ca->ptr;
334
264
  for (i = 0; i < n; i++)
335
- p[i] = (uint32_t)(low_long + ca_random_ulong_limited(&source, limit));
265
+ p[i] = (uint32_t)(low_long + ca_random_ulong_limited(&c->source, limit));
336
266
  break;
337
267
  }
338
268
  case CA_INT64: {
339
269
  int64_t *p = (int64_t *)ca->ptr;
340
270
  for (i = 0; i < n; i++)
341
- p[i] = (int64_t)(low_long + ca_random_ulong_limited(&source, limit));
271
+ p[i] = (int64_t)(low_long + ca_random_ulong_limited(&c->source, limit));
342
272
  break;
343
273
  }
344
274
  case CA_UINT64: {
345
275
  uint64_t *p = (uint64_t *)ca->ptr;
346
276
  for (i = 0; i < n; i++)
347
- p[i] = (uint64_t)(low_long + ca_random_ulong_limited(&source, limit));
277
+ p[i] = (uint64_t)(low_long + ca_random_ulong_limited(&c->source, limit));
348
278
  break;
349
279
  }
350
280
  default:
351
- ca_rng_close(&source);
352
- ca_sync(ca);
353
- ca_detach(ca);
354
281
  rb_raise(rb_eCADataTypeError,
355
282
  "random! is not supported for this data type");
356
283
  }
@@ -358,9 +285,153 @@ rb_ca_random_bang(int argc, VALUE *argv, VALUE self)
358
285
  }
359
286
  }
360
287
 
361
- ca_rng_close(&source);
362
- ca_sync(ca);
363
- ca_detach(ca);
288
+ return Qnil;
289
+ }
290
+
291
+ /* CArray#random!([low,] [high], rng:) — fill self with uniform random
292
+ * numbers in-place, returning self. Range surface mirrors Numo/NumPy
293
+ * (half-open [low, high) as the default) plus Ruby idiom (`..` closed,
294
+ * `...` half-open):
295
+ *
296
+ * random! float [0.0, 1.0); integer -> ArgumentError
297
+ * random!(high) [0, high) (Ruby rand shorthand)
298
+ * random!(low, high) [low, high) (Numo positional)
299
+ * random!(a..b) [a, b] closed (integer: b included)
300
+ * random!(a...b) [a, b) half-open
301
+ * random!(..., rng: r) any of the above with a custom Random source
302
+ *
303
+ * For float, `..` and `...` return the same distribution (endpoint
304
+ * probability ~2^-53); the closed form is honored syntactically but not
305
+ * enforced at the mantissa level, matching NumPy/SciPy convention.
306
+ * Boolean fills 0/1 with 50% probability each and ignores the range
307
+ * argument. Rejects CA_OBJECT / CA_FIXLEN. */
308
+ static VALUE
309
+ rb_ca_random_bang(int argc, VALUE *argv, VALUE self)
310
+ {
311
+ CArray *ca;
312
+ VALUE arg1 = Qnil, arg2 = Qnil, opts = Qnil;
313
+ VALUE low_val = Qnil, high_val = Qnil;
314
+ int is_default = 1;
315
+ int high_is_closed = 0;
316
+ double low_dbl = 0.0, high_dbl = 0.0;
317
+ long low_long = 0, high_long = 0;
318
+ unsigned long limit = 0;
319
+ VALUE rng = Qnil;
320
+
321
+ rb_scan_args(argc, argv, "02:", &arg1, &arg2, &opts);
322
+ rb_scan_options(opts, "rng", &rng);
323
+
324
+ /* Parse positional args into (low_val, high_val) + high_is_closed. */
325
+ if (rb_obj_is_kind_of(arg1, rb_cRange)) {
326
+ if (!NIL_P(arg2)) {
327
+ rb_raise(rb_eArgError,
328
+ "random: cannot combine a Range with a second positional arg");
329
+ }
330
+ low_val = rb_funcall(arg1, rb_intern("begin"), 0);
331
+ high_val = rb_funcall(arg1, rb_intern("end"), 0);
332
+ if (NIL_P(low_val) || NIL_P(high_val)) {
333
+ rb_raise(rb_eArgError,
334
+ "random: Range must have finite begin and end");
335
+ }
336
+ high_is_closed = ! RTEST(rb_funcall(arg1, rb_intern("exclude_end?"), 0));
337
+ is_default = 0;
338
+ } else if (!NIL_P(arg1) && !NIL_P(arg2)) {
339
+ low_val = arg1;
340
+ high_val = arg2;
341
+ is_default = 0;
342
+ } else if (!NIL_P(arg1)) {
343
+ low_val = INT2FIX(0);
344
+ high_val = arg1;
345
+ is_default = 0;
346
+ }
347
+
348
+ rb_ca_modify(self);
349
+ TypedData_Get_Struct(self, CArray, &carray_data_type, ca);
350
+
351
+ if (ca->data_type == CA_OBJECT || ca->data_type == CA_FIXLEN) {
352
+ rb_raise(rb_eCADataTypeError,
353
+ "random! is not supported for object/fixlen arrays");
354
+ }
355
+
356
+ /* Materialize (low, high) as the correct scalar type and validate.
357
+ * For integer data types, `..` closed adds 1 to high (turns into half-open
358
+ * for the sampler); for float data types, closed and half-open are
359
+ * equivalent so no adjustment. */
360
+ if (!is_default) {
361
+ int is_integer_type = (ca->data_type >= CA_INT8
362
+ && ca->data_type <= CA_UINT64);
363
+ if (is_integer_type) {
364
+ /* The bounds stay Ruby Integers until they are known to fit: the
365
+ last value drawn is high (closed) or high - 1, and every value
366
+ from low to it must be one of the type's. The draw is low plus
367
+ an offset up to last - low, which holds even the whole int64 or
368
+ uint64 range. */
369
+ VALUE last, type_min, type_max;
370
+ if (!RB_INTEGER_TYPE_P(low_val) || !RB_INTEGER_TYPE_P(high_val)) {
371
+ rb_raise(rb_eTypeError,
372
+ "random: an integer array takes Integer bounds "
373
+ "(got %"PRIsVALUE", %"PRIsVALUE")",
374
+ rb_inspect(low_val), rb_inspect(high_val));
375
+ }
376
+ last = high_is_closed ? high_val
377
+ : rb_funcall(high_val, '-', 1, INT2FIX(1));
378
+ if (RTEST(rb_funcall(low_val, '>', 1, last))) {
379
+ rb_raise(rb_eArgError,
380
+ "random: low must be less than high "
381
+ "(got low=%"PRIsVALUE", high=%"PRIsVALUE")",
382
+ low_val, high_val);
383
+ }
384
+ switch (ca->data_type) {
385
+ case CA_INT8: type_min = INT2FIX(INT8_MIN); type_max = INT2FIX(INT8_MAX); break;
386
+ case CA_UINT8: type_min = INT2FIX(0); type_max = INT2FIX(UINT8_MAX); break;
387
+ case CA_INT16: type_min = INT2FIX(INT16_MIN); type_max = INT2FIX(INT16_MAX); break;
388
+ case CA_UINT16: type_min = INT2FIX(0); type_max = INT2FIX(UINT16_MAX); break;
389
+ case CA_INT32: type_min = LL2NUM(INT32_MIN); type_max = LL2NUM(INT32_MAX); break;
390
+ case CA_UINT32: type_min = INT2FIX(0); type_max = ULL2NUM(UINT32_MAX); break;
391
+ case CA_INT64: type_min = LL2NUM(INT64_MIN); type_max = LL2NUM(INT64_MAX); break;
392
+ default: type_min = INT2FIX(0); type_max = ULL2NUM(UINT64_MAX); break;
393
+ }
394
+ if (RTEST(rb_funcall(low_val, '<', 1, type_min)) ||
395
+ RTEST(rb_funcall(last, '>', 1, type_max))) {
396
+ rb_raise(rb_eRangeError,
397
+ "random: the values %"PRIsVALUE"..%"PRIsVALUE" are not all %s "
398
+ "(%"PRIsVALUE"..%"PRIsVALUE")",
399
+ low_val, last, ca_type_name[ca->data_type], type_min, type_max);
400
+ }
401
+ low_long = RTEST(rb_funcall(low_val, '<', 1, INT2FIX(0)))
402
+ ? (long) NUM2LL(low_val) : (long) NUM2ULL(low_val);
403
+ limit = (unsigned long) NUM2ULL(rb_funcall(last, '-', 1, low_val));
404
+ (void) high_long;
405
+ } else {
406
+ low_dbl = NUM2DBL(low_val);
407
+ high_dbl = NUM2DBL(high_val);
408
+ if (!isfinite(low_dbl) || !isfinite(high_dbl)) {
409
+ rb_raise(rb_eArgError,
410
+ "random: the bounds are finite numbers "
411
+ "(got low=%g, high=%g)", low_dbl, high_dbl);
412
+ }
413
+ if (low_dbl >= high_dbl) {
414
+ rb_raise(rb_eArgError,
415
+ "random: low must be less than high "
416
+ "(got low=%g, high=%g)",
417
+ low_dbl, high_dbl);
418
+ }
419
+ }
420
+ }
421
+
422
+ {
423
+ ca_random_ctx_t c = { 0 };
424
+ c.ca = ca;
425
+ c.n = ca->elements;
426
+ c.is_default = is_default;
427
+ c.low_dbl = low_dbl;
428
+ c.high_dbl = high_dbl;
429
+ c.low_long = low_long;
430
+ c.limit = limit;
431
+ ca_random_run(&c, rng, ca_random_bang_fill);
432
+ }
433
+ /* Every cell now holds a value, as after seq! or fill. */
434
+ ca_clear_mask(ca);
364
435
  return self;
365
436
  }
366
437
 
@@ -396,53 +467,31 @@ box_muller_pair(ca_rng_t *source, double *r1, double *r2)
396
467
  *r2 = r * sin(theta);
397
468
  }
398
469
 
399
- /* CArray#randomn!(rng:) — fill self with standard normal N(0, 1)
400
- * samples in-place via Box-Muller, returning self.
401
- *
402
- * Restricted to float / complex data types. Complex fills real + imag as
403
- * two independent normals per cell. */
404
470
  static VALUE
405
- rb_ca_randomn_bang(int argc, VALUE *argv, VALUE self)
471
+ ca_randomn_bang_fill (VALUE arg)
406
472
  {
407
- CArray *ca;
408
- VALUE opts = Qnil;
409
- ca_size_t i, n;
410
-
411
- rb_scan_args(argc, argv, "0:", &opts);
412
- VALUE rng = Qnil;
413
- ca_rng_t source;
414
- rb_scan_options(opts, "rng", &rng);
415
-
416
- rb_ca_modify(self);
417
- TypedData_Get_Struct(self, CArray, &carray_data_type, ca);
418
-
419
- if (ca->data_type != CA_FLOAT64 && ca->data_type != CA_FLOAT32 &&
420
- ca->data_type != CA_CMPLX128 && ca->data_type != CA_CMPLX64) {
421
- rb_raise(rb_eCADataTypeError,
422
- "randomn! requires float or complex array");
423
- }
424
-
425
- n = ca->elements;
426
- ca_attach(ca);
427
- ca_rng_open(rng, &source);
473
+ ca_random_ctx_t *c = (ca_random_ctx_t *) arg;
474
+ CArray *ca = c->ca;
475
+ ca_size_t i, n = c->n;
428
476
 
477
+ ca_rng_bind(&c->source);
429
478
  /* A CArray::Rng fills one cell per call rather than two, so that this
430
479
  and a kernel drawing afterwards are the one sequence: both are
431
480
  `ca_random_normal` run once per cell. It costs a draw a cell over
432
481
  the paired form, and what it buys is that where the generator
433
482
  stands can be worked out rather than run. */
434
- if (source.kind == CA_RNG_OWN) {
483
+ if (c->source.kind == CA_RNG_OWN) {
435
484
  switch (ca->data_type) {
436
485
  case CA_FLOAT64: {
437
486
  double *p = (double *)ca->ptr;
438
487
  for (i = 0; i < n; i++)
439
- p[i] = ca_random_normal(&source);
488
+ p[i] = ca_random_normal(&c->source);
440
489
  break;
441
490
  }
442
491
  case CA_FLOAT32: {
443
492
  float *p = (float *)ca->ptr;
444
493
  for (i = 0; i < n; i++)
445
- p[i] = (float) ca_random_normal(&source);
494
+ p[i] = (float) ca_random_normal(&c->source);
446
495
  break;
447
496
  }
448
497
  case CA_CMPLX128: {
@@ -450,8 +499,8 @@ rb_ca_randomn_bang(int argc, VALUE *argv, VALUE self)
450
499
  for (i = 0; i < n; i++) {
451
500
  /* Into locals first: C does not say which order a call's
452
501
  arguments are evaluated in, and these two advance a state. */
453
- double re = ca_random_normal(&source);
454
- double im = ca_random_normal(&source);
502
+ double re = ca_random_normal(&c->source);
503
+ double im = ca_random_normal(&c->source);
455
504
  p[i] = re + im * I;
456
505
  }
457
506
  break;
@@ -459,8 +508,8 @@ rb_ca_randomn_bang(int argc, VALUE *argv, VALUE self)
459
508
  case CA_CMPLX64: {
460
509
  float complex *p = (float complex *)ca->ptr;
461
510
  for (i = 0; i < n; i++) {
462
- double re = ca_random_normal(&source);
463
- double im = ca_random_normal(&source);
511
+ double re = ca_random_normal(&c->source);
512
+ double im = ca_random_normal(&c->source);
464
513
  p[i] = (float) re + (float) im * I;
465
514
  }
466
515
  break;
@@ -468,10 +517,7 @@ rb_ca_randomn_bang(int argc, VALUE *argv, VALUE self)
468
517
  default:
469
518
  break;
470
519
  }
471
- ca_rng_close(&source);
472
- ca_sync(ca);
473
- ca_detach(ca);
474
- return self;
520
+ return Qnil;
475
521
  }
476
522
 
477
523
  switch (ca->data_type) {
@@ -479,11 +525,11 @@ rb_ca_randomn_bang(int argc, VALUE *argv, VALUE self)
479
525
  double *p = (double *)ca->ptr;
480
526
  ca_size_t pairs = n / 2;
481
527
  for (i = 0; i < pairs; i++) {
482
- box_muller_pair(&source, &p[2*i], &p[2*i+1]);
528
+ box_muller_pair(&c->source, &p[2*i], &p[2*i+1]);
483
529
  }
484
530
  if (n % 2 == 1) {
485
531
  double r1, r2;
486
- box_muller_pair(&source, &r1, &r2);
532
+ box_muller_pair(&c->source, &r1, &r2);
487
533
  p[n-1] = r1;
488
534
  }
489
535
  break;
@@ -493,13 +539,13 @@ rb_ca_randomn_bang(int argc, VALUE *argv, VALUE self)
493
539
  ca_size_t pairs = n / 2;
494
540
  for (i = 0; i < pairs; i++) {
495
541
  double r1, r2;
496
- box_muller_pair(&source, &r1, &r2);
542
+ box_muller_pair(&c->source, &r1, &r2);
497
543
  p[2*i] = (float)r1;
498
544
  p[2*i+1] = (float)r2;
499
545
  }
500
546
  if (n % 2 == 1) {
501
547
  double r1, r2;
502
- box_muller_pair(&source, &r1, &r2);
548
+ box_muller_pair(&c->source, &r1, &r2);
503
549
  p[n-1] = (float)r1;
504
550
  }
505
551
  break;
@@ -508,7 +554,7 @@ rb_ca_randomn_bang(int argc, VALUE *argv, VALUE self)
508
554
  double complex *p = (double complex *)ca->ptr;
509
555
  for (i = 0; i < n; i++) {
510
556
  double r1, r2;
511
- box_muller_pair(&source, &r1, &r2);
557
+ box_muller_pair(&c->source, &r1, &r2);
512
558
  p[i] = r1 + r2 * I;
513
559
  }
514
560
  break;
@@ -517,7 +563,7 @@ rb_ca_randomn_bang(int argc, VALUE *argv, VALUE self)
517
563
  float complex *p = (float complex *)ca->ptr;
518
564
  for (i = 0; i < n; i++) {
519
565
  double r1, r2;
520
- box_muller_pair(&source, &r1, &r2);
566
+ box_muller_pair(&c->source, &r1, &r2);
521
567
  p[i] = (float)r1 + (float)r2 * I;
522
568
  }
523
569
  break;
@@ -526,9 +572,41 @@ rb_ca_randomn_bang(int argc, VALUE *argv, VALUE self)
526
572
  break;
527
573
  }
528
574
 
529
- ca_rng_close(&source);
530
- ca_sync(ca);
531
- ca_detach(ca);
575
+ return Qnil;
576
+ }
577
+
578
+ /* CArray#randomn!(rng:) — fill self with standard normal N(0, 1)
579
+ * samples in-place via Box-Muller, returning self.
580
+ *
581
+ * Restricted to float / complex data types. Complex fills real + imag as
582
+ * two independent normals per cell. */
583
+ static VALUE
584
+ rb_ca_randomn_bang(int argc, VALUE *argv, VALUE self)
585
+ {
586
+ CArray *ca;
587
+ VALUE opts = Qnil;
588
+
589
+ rb_scan_args(argc, argv, "0:", &opts);
590
+ VALUE rng = Qnil;
591
+ rb_scan_options(opts, "rng", &rng);
592
+
593
+ rb_ca_modify(self);
594
+ TypedData_Get_Struct(self, CArray, &carray_data_type, ca);
595
+
596
+ if (ca->data_type != CA_FLOAT64 && ca->data_type != CA_FLOAT32 &&
597
+ ca->data_type != CA_CMPLX128 && ca->data_type != CA_CMPLX64) {
598
+ rb_raise(rb_eCADataTypeError,
599
+ "randomn! requires float or complex array");
600
+ }
601
+
602
+ {
603
+ ca_random_ctx_t c = { 0 };
604
+ c.ca = ca;
605
+ c.n = ca->elements;
606
+ ca_random_run(&c, rng, ca_randomn_bang_fill);
607
+ }
608
+ /* Every cell now holds a value, as after seq! or fill. */
609
+ ca_clear_mask(ca);
532
610
  return self;
533
611
  }
534
612
 
@@ -550,59 +628,39 @@ swap_chunks(char *a, char *b, ca_size_t chunk_bytes, char *tmp)
550
628
  * as a byte chunk and swapped whole, so multi-dim slices move
551
629
  * together). */
552
630
  static VALUE
553
- rb_ca_shuffle_bang(int argc, VALUE *argv, VALUE self)
631
+ ca_shuffle_bang_fill (VALUE arg)
554
632
  {
555
- CArray *ca;
556
- VALUE opts = Qnil, v_axis = Qnil;
633
+ ca_random_ctx_t *c = (ca_random_ctx_t *) arg;
634
+ CArray *ca = c->ca;
557
635
  ca_size_t n;
558
- int axis = -1;
559
636
 
560
- rb_scan_args(argc, argv, "0:", &opts);
561
- VALUE rng = Qnil;
562
- ca_rng_t source;
563
- rb_scan_options(opts, "rng,axis", &rng, &v_axis);
637
+ ca_rng_bind(&c->source);
564
638
 
565
- rb_ca_modify(self);
566
- TypedData_Get_Struct(self, CArray, &carray_data_type, ca);
567
-
568
- if (ca->elements <= 1) return self;
569
-
570
- ca_attach(ca);
571
- ca_rng_open(rng, &source);
572
-
573
- if (NIL_P(v_axis)) {
639
+ if (c->axis < 0) {
574
640
  /* shuffle all elements */
575
- n = ca->elements;
641
+ volatile VALUE tmp_holder;
576
642
  ca_size_t elem_bytes = ca->bytes;
577
- char *tmp = (char *)xmalloc(elem_bytes);
643
+ char *tmp = ALLOCV_N(char, tmp_holder, elem_bytes);
578
644
  char *p = ca->ptr;
645
+ char *m = c->mask ? c->mask->ptr : NULL;
579
646
 
647
+ n = ca->elements;
580
648
  for (ca_size_t i = n - 1; i > 0; i--) {
581
- unsigned long j = ca_random_ulong_limited(&source, (unsigned long)i);
649
+ unsigned long j = ca_random_ulong_limited(&c->source, (unsigned long)i);
582
650
  if ((ca_size_t)j != i) {
583
651
  swap_chunks(p + i * elem_bytes, p + j * elem_bytes, elem_bytes, tmp);
652
+ if ( m ) {
653
+ char b = m[i]; m[i] = m[j]; m[j] = b;
654
+ }
584
655
  }
585
656
  }
586
- xfree(tmp);
657
+ ALLOCV_END(tmp_holder);
587
658
  }
588
659
  else {
589
- axis = NUM2INT(v_axis);
590
- if (axis < 0) axis += ca->ndim;
591
- if (axis < 0 || axis >= ca->ndim) {
592
- ca_rng_close(&source);
593
- ca_sync(ca);
594
- ca_detach(ca);
595
- rb_raise(rb_eArgError,
596
- "axis %d is out of range for ndim %d", axis, ca->ndim);
597
- }
660
+ int axis = c->axis;
661
+ volatile VALUE tmp_holder;
598
662
 
599
663
  n = ca->dim[axis];
600
- if (n <= 1) {
601
- ca_rng_close(&source);
602
- ca_sync(ca);
603
- ca_detach(ca);
604
- return self;
605
- }
606
664
 
607
665
  ca_size_t outer = 1;
608
666
  for (int d = 0; d < axis; d++)
@@ -614,25 +672,59 @@ rb_ca_shuffle_bang(int argc, VALUE *argv, VALUE self)
614
672
 
615
673
  ca_size_t chunk_bytes = inner * ca->bytes;
616
674
  ca_size_t stride = n * chunk_bytes;
617
- char *tmp = (char *)xmalloc(chunk_bytes);
675
+ char *tmp = ALLOCV_N(char, tmp_holder, chunk_bytes);
618
676
 
619
677
  for (ca_size_t o = 0; o < outer; o++) {
620
678
  char *base = ca->ptr + o * stride;
679
+ char *mbase = c->mask ? c->mask->ptr + o * n * inner : NULL;
621
680
  for (ca_size_t i = n - 1; i > 0; i--) {
622
- unsigned long j = ca_random_ulong_limited(&source, (unsigned long)i);
681
+ unsigned long j = ca_random_ulong_limited(&c->source, (unsigned long)i);
623
682
  if ((ca_size_t)j != i) {
624
683
  swap_chunks(base + i * chunk_bytes,
625
684
  base + j * chunk_bytes,
626
685
  chunk_bytes, tmp);
686
+ if ( mbase ) {
687
+ swap_chunks(mbase + i * inner, mbase + j * inner, inner, tmp);
688
+ }
627
689
  }
628
690
  }
629
691
  }
630
- xfree(tmp);
692
+ ALLOCV_END(tmp_holder);
693
+ }
694
+ return Qnil;
695
+ }
696
+
697
+ static VALUE
698
+ rb_ca_shuffle_bang(int argc, VALUE *argv, VALUE self)
699
+ {
700
+ CArray *ca;
701
+ VALUE opts = Qnil, v_axis = Qnil;
702
+ int axis = -1;
703
+ ca_random_ctx_t c = { 0 };
704
+
705
+ rb_scan_args(argc, argv, "0:", &opts);
706
+ VALUE rng = Qnil;
707
+ rb_scan_options(opts, "rng,axis", &rng, &v_axis);
708
+
709
+ rb_ca_modify(self);
710
+ TypedData_Get_Struct(self, CArray, &carray_data_type, ca);
711
+
712
+ if (ca->elements <= 1) return self;
713
+
714
+ if (!NIL_P(v_axis)) {
715
+ axis = ca_normalize_axis_arg(v_axis, ca->ndim, NULL);
716
+ if (ca->dim[axis] <= 1) {
717
+ return self;
718
+ }
631
719
  }
632
720
 
633
- ca_rng_close(&source);
634
- ca_sync(ca);
635
- ca_detach(ca);
721
+ c.ca = ca;
722
+ c.axis = axis;
723
+ /* The mask moves with the values: a masked cell stays masked wherever
724
+ it lands. */
725
+ ca_update_mask(ca);
726
+ c.mask = ca->mask;
727
+ ca_random_run(&c, rng, ca_shuffle_bang_fill);
636
728
  return self;
637
729
  }
638
730
 
@@ -698,7 +790,7 @@ ca_rng_cells (VALUE self, int64_t **cells)
698
790
  CArray *ca;
699
791
  VALUE state = rb_ivar_get(self, rb_intern("@state"));
700
792
  TypedData_Get_Struct(state, CArray, &carray_data_type, ca);
701
- ca_attach(ca);
793
+ ca_attach(ca); /* window: the state is an entity, whose attach holds nothing */
702
794
  *cells = (int64_t *) ca->ptr;
703
795
  return ca;
704
796
  }
@@ -752,8 +844,7 @@ rb_ca_rng_reset (int argc, VALUE *argv, VALUE self)
752
844
 
753
845
  ca = ca_rng_cells(self, &cells);
754
846
  ca_xoshiro256pp_seed(cells, (uint64_t) NUM2ULL(masked));
755
- ca_sync(ca);
756
- ca_detach(ca);
847
+ ca_sync_detach(ca);
757
848
 
758
849
  rb_ivar_set(self, rb_intern("@seed"), seed);
759
850
  return self;
@@ -779,8 +870,7 @@ rb_ca_rng_random (VALUE self)
779
870
 
780
871
  ca = ca_rng_cells(self, &cells);
781
872
  value = ca_xoshiro256pp_next_real(cells);
782
- ca_sync(ca);
783
- ca_detach(ca);
873
+ ca_sync_detach(ca);
784
874
  return rb_float_new(value);
785
875
  }
786
876
 
@@ -799,8 +889,7 @@ rb_ca_rng_bits (VALUE self)
799
889
 
800
890
  ca = ca_rng_cells(self, &cells);
801
891
  value = ca_xoshiro256pp_next(cells);
802
- ca_sync(ca);
803
- ca_detach(ca);
892
+ ca_sync_detach(ca);
804
893
  return ULL2NUM(value);
805
894
  }
806
895
 
@@ -823,8 +912,7 @@ rb_ca_rng_randomn (VALUE self)
823
912
  source.state = NULL;
824
913
  source.cells = cells;
825
914
  value = ca_random_normal(&source);
826
- ca_sync(ca);
827
- ca_detach(ca);
915
+ ca_sync_detach(ca);
828
916
  return rb_float_new(value);
829
917
  }
830
918