carray 3.0.3 → 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 (212) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +264 -0
  3. data/README.md +3 -2
  4. data/carray.gemspec +1 -1
  5. data/ext/ca_axis_descriptor.h +27 -24
  6. data/ext/ca_axis_dispatch.c +1 -6
  7. data/ext/ca_axis_group.c +27 -27
  8. data/ext/ca_binop_dispatch.c +1 -29
  9. data/ext/ca_binop_dispatch.h +0 -1
  10. data/ext/ca_categorical_iterator.c +205 -134
  11. data/ext/ca_compare.c +7 -18
  12. data/ext/ca_group_iter.c +16 -17
  13. data/ext/ca_kernel_iterator.c +1234 -1994
  14. data/ext/ca_kernel_iterator.h +391 -437
  15. data/ext/ca_monop_dispatch.c +82 -59
  16. data/ext/ca_monop_dispatch.h +1 -0
  17. data/ext/ca_obj_array.c +96 -39
  18. data/ext/ca_obj_bincmp.c +24 -21
  19. data/ext/ca_obj_binop.c +15 -26
  20. data/ext/ca_obj_bitarray.c +3 -2
  21. data/ext/ca_obj_bitfield.c +5 -6
  22. data/ext/ca_obj_block.c +18 -9
  23. data/ext/ca_obj_byte_swap.c +5 -6
  24. data/ext/ca_obj_const_string.c +173 -19
  25. data/ext/ca_obj_face.c +238 -57
  26. data/ext/ca_obj_face.h +19 -0
  27. data/ext/ca_obj_fake.c +15 -29
  28. data/ext/ca_obj_farray.c +6 -3
  29. data/ext/ca_obj_field.c +17 -5
  30. data/ext/ca_obj_fixlen_string.c +2 -3
  31. data/ext/ca_obj_grid.c +406 -132
  32. data/ext/ca_obj_meld.c +172 -34
  33. data/ext/ca_obj_moncmp.c +23 -8
  34. data/ext/ca_obj_monop.c +67 -57
  35. data/ext/ca_obj_object.c +157 -51
  36. data/ext/ca_obj_record.c +12 -7
  37. data/ext/ca_obj_reduce.c +13 -8
  38. data/ext/ca_obj_refer.c +25 -27
  39. data/ext/ca_obj_remap.c +30 -9
  40. data/ext/ca_obj_repeat.c +9 -10
  41. data/ext/ca_obj_roll.c +6 -5
  42. data/ext/ca_obj_select.c +14 -9
  43. data/ext/ca_obj_select_axis.c +15 -47
  44. data/ext/ca_obj_shift.c +11 -1
  45. data/ext/ca_obj_stack.c +179 -34
  46. data/ext/ca_obj_stride.c +162 -115
  47. data/ext/ca_obj_string.c +71 -4
  48. data/ext/ca_obj_tile.c +6 -5
  49. data/ext/ca_obj_time.c +22 -6
  50. data/ext/ca_obj_timedelta.c +22 -6
  51. data/ext/ca_obj_transpose.c +5 -3
  52. data/ext/ca_obj_triop.c +23 -32
  53. data/ext/ca_obj_window.c +190 -26
  54. data/ext/ca_sweep_engine.c +2 -2
  55. data/ext/caframe_csv_reader.c +80 -1
  56. data/ext/carray.h +181 -75
  57. data/ext/carray_access.c +184 -96
  58. data/ext/carray_address_basis.c +343 -87
  59. data/ext/carray_attribute.c +89 -6
  60. data/ext/carray_bincount.c +10 -8
  61. data/ext/carray_broadcast.c +13 -8
  62. data/ext/carray_build_flags.h +3 -0
  63. data/ext/carray_call_cfunc.c +108 -0
  64. data/ext/carray_cast.c +142 -24
  65. data/ext/carray_cast_func.rb +46 -6
  66. data/ext/carray_class.c +4 -3
  67. data/ext/carray_config.h +28 -0
  68. data/ext/carray_conversion.c +16 -7
  69. data/ext/carray_copy.c +51 -14
  70. data/ext/carray_core.c +147 -55
  71. data/ext/carray_count.c +50 -20
  72. data/ext/carray_element.c +76 -33
  73. data/ext/carray_factorize.c +118 -64
  74. data/ext/carray_generate.c +191 -50
  75. data/ext/carray_histogram.c +62 -17
  76. data/ext/carray_hold.c +12 -15
  77. data/ext/carray_index_classifier.c +388 -176
  78. data/ext/carray_internal.h +336 -29
  79. data/ext/carray_kernels_bincmp.c +4453 -0
  80. data/ext/carray_kernels_binop.c +11368 -0
  81. data/ext/carray_kernels_init.c +1734 -0
  82. data/ext/carray_kernels_moncmp.c +2104 -0
  83. data/ext/carray_kernels_monop.c +18369 -0
  84. data/ext/carray_kernels_reduce_aggregate.c +23818 -0
  85. data/ext/carray_kernels_reduce_boolean.c +406 -0
  86. data/ext/carray_kernels_reduce_cumulative.c +10086 -0
  87. data/ext/carray_kernels_reduce_extreme.c +17634 -0
  88. data/ext/carray_kernels_reduce_variance.c +3849 -0
  89. data/ext/carray_kernels_scan.c +4302 -0
  90. data/ext/carray_kernels_search.c +33829 -0
  91. data/ext/carray_kernels_sort.c +10906 -0
  92. data/ext/carray_kernels_triop.c +1864 -0
  93. data/ext/carray_key_windows.c +216 -0
  94. data/ext/carray_lazy.c +170 -111
  95. data/ext/carray_mask.c +292 -72
  96. data/ext/carray_median_percentile.c +82 -28
  97. data/ext/carray_memory_view.c +669 -561
  98. data/ext/carray_operator.c +124 -47
  99. data/ext/carray_order.c +384 -76
  100. data/ext/carray_partition.c +28 -27
  101. data/ext/carray_random.c +67 -13
  102. data/ext/carray_round.c +273 -0
  103. data/ext/carray_scatter.c +63 -38
  104. data/ext/carray_segment.c +3 -3
  105. data/ext/carray_slab.c +91 -28
  106. data/ext/carray_sort.c +35 -35
  107. data/ext/carray_struct.c +19 -18
  108. data/ext/carray_test.c +26 -5
  109. data/ext/carray_undef.c +4 -0
  110. data/ext/carray_utils.c +246 -45
  111. data/ext/mk_call_cfunc.rb +19 -2
  112. data/ext/mkkernel.rb +198 -240
  113. data/ext/ruby_carray.c +10 -4
  114. data/ext/version.h +4 -4
  115. data/lib/carray/attribute.rb +126 -22
  116. data/lib/carray/autoload_carray.rb +8 -31
  117. data/lib/carray/autoload_method_extension.rb +14 -5
  118. data/lib/carray/axis_group.rb +255 -106
  119. data/lib/carray/basics.rb +78 -10
  120. data/lib/carray/bincount_nd.rb +12 -2
  121. data/lib/carray/block_iterator.rb +126 -23
  122. data/lib/carray/boolean_reduce.rb +17 -17
  123. data/lib/carray/categorical.rb +165 -43
  124. data/lib/carray/categorical_iterator.rb +130 -27
  125. data/lib/carray/complex.rb +5 -5
  126. data/lib/carray/conditional.rb +67 -18
  127. data/lib/carray/const_string.rb +14 -5
  128. data/lib/carray/construct.rb +15 -11
  129. data/lib/carray/data_type_extension.rb +1 -0
  130. data/lib/carray/distance_limit.rb +77 -0
  131. data/lib/carray/fixlen_string.rb +7 -1
  132. data/lib/carray/frame/column_verbs.rb +70 -0
  133. data/lib/carray/frame/convert.rb +69 -0
  134. data/lib/carray/frame/csv_parser.rb +111 -17
  135. data/lib/carray/frame/describe.rb +3 -2
  136. data/lib/carray/frame/frame.rb +246 -21
  137. data/lib/carray/frame/group.rb +510 -95
  138. data/lib/carray/frame/io.rb +115 -28
  139. data/lib/carray/frame/join.rb +312 -51
  140. data/lib/carray/frame/records.rb +83 -7
  141. data/lib/carray/frame/reshape.rb +42 -35
  142. data/lib/carray/frame/sort.rb +7 -19
  143. data/lib/carray/frame/verbs.rb +188 -48
  144. data/lib/carray/frame/windows.rb +117 -0
  145. data/lib/carray/frame.rb +2 -0
  146. data/lib/carray/fuse_source.rb +11 -4
  147. data/lib/carray/fusion.rb +40 -16
  148. data/lib/carray/histogram.rb +6 -1
  149. data/lib/carray/inspect.rb +31 -8
  150. data/lib/carray/iterator.rb +85 -7
  151. data/lib/carray/key_windows.rb +620 -0
  152. data/lib/carray/lazy.rb +182 -183
  153. data/lib/carray/mask_gap_fill.rb +63 -57
  154. data/lib/carray/meld_reduce.rb +12 -14
  155. data/lib/carray/methods/bin.rb +26 -18
  156. data/lib/carray/methods/bincount.rb +16 -0
  157. data/lib/carray/methods/bit_string.rb +1 -1
  158. data/lib/carray/methods/choose.rb +16 -3
  159. data/lib/carray/methods/composition.rb +3 -1
  160. data/lib/carray/methods/gather_nd.rb +14 -5
  161. data/lib/carray/methods/insert_block.rb +2 -2
  162. data/lib/carray/methods/is_between.rb +59 -0
  163. data/lib/carray/methods/is_in.rb +53 -36
  164. data/lib/carray/methods/join.rb +1 -6
  165. data/lib/carray/methods/lag.rb +49 -0
  166. data/lib/carray/methods/locate_addr.rb +47 -51
  167. data/lib/carray/methods/mode.rb +1 -1
  168. data/lib/carray/methods/nunique.rb +11 -6
  169. data/lib/carray/methods/resize.rb +3 -3
  170. data/lib/carray/methods/value_counts.rb +4 -11
  171. data/lib/carray/record_order.rb +3 -3
  172. data/lib/carray/rng.rb +3 -0
  173. data/lib/carray/runtime.rb +53 -8
  174. data/lib/carray/segment_iterator.rb +313 -82
  175. data/lib/carray/serialize.rb +24 -10
  176. data/lib/carray/slab_iterator.rb +50 -0
  177. data/lib/carray/string.rb +105 -5
  178. data/lib/carray/string_operation_extension.rb +8 -1
  179. data/lib/carray/struct.rb +9 -21
  180. data/lib/carray/struct_builder.rb +19 -8
  181. data/lib/carray/time.rb +726 -104
  182. data/lib/carray/window_iterator.rb +122 -16
  183. data/lib/carray.rb +7 -0
  184. data/yard-stubs/ca_obj_bitfield.rb +1 -1
  185. data/yard-stubs/ca_obj_field.rb +4 -0
  186. data/yard-stubs/ca_obj_grid.rb +21 -10
  187. data/yard-stubs/ca_obj_refer.rb +2 -1
  188. data/yard-stubs/ca_obj_roll.rb +2 -2
  189. data/yard-stubs/ca_obj_shift.rb +5 -1
  190. data/yard-stubs/ca_obj_tile.rb +1 -1
  191. data/yard-stubs/ca_obj_window.rb +8 -3
  192. data/yard-stubs/carray_access.rb +6 -1
  193. data/yard-stubs/carray_attribute.rb +0 -5
  194. data/yard-stubs/carray_cast.rb +10 -0
  195. data/yard-stubs/carray_conversion.rb +2 -1
  196. data/yard-stubs/carray_copy.rb +4 -2
  197. data/yard-stubs/carray_count.rb +4 -4
  198. data/yard-stubs/carray_element.rb +10 -4
  199. data/yard-stubs/carray_generate.rb +21 -1
  200. data/yard-stubs/carray_mask.rb +26 -2
  201. data/yard-stubs/carray_math.rb +27 -1
  202. data/yard-stubs/carray_median_percentile.rb +5 -2
  203. data/yard-stubs/carray_memory_view.rb +40 -23
  204. data/yard-stubs/carray_order.rb +29 -18
  205. data/yard-stubs/carray_random.rb +13 -4
  206. data/yard-stubs/carray_scatter.rb +14 -9
  207. data/yard-stubs/carray_slab.rb +9 -2
  208. data/yard-stubs/carray_sort.rb +6 -5
  209. data/yard-stubs/carray_test.rb +5 -3
  210. metadata +25 -3
  211. data/lib/carray/arrow_tensor.rb +0 -401
  212. data/lib/carray/table.rb +0 -108
@@ -9,14 +9,16 @@
9
9
 
10
10
  #include "ruby.h"
11
11
  #include "carray.h"
12
- #include "carray_internal.h" /* ca_attach_window, ca_axis_integer */
12
+ #include "carray_internal.h" /* ca_attach_window, ca_normalize_axis_arg */
13
+ #include "ca_obj_face.h" /* ca_face_element_to_storage */
13
14
 
14
15
  /* ----------------------------------------------------------------- */
15
16
 
16
17
  /* CArray#where -- collect the flat addresses of all non-zero (or
17
- true) cells in self into a fresh 1-D CA_SIZE array. Non-boolean
18
- inputs are first coerced to boolean via rb_ca_to_boolean. Masked
19
- cells are excluded.
18
+ true) cells in self into a fresh 1-D CA_SIZE array. An integer,
19
+ float or complex array selects its non-zero cells (NaN is non-zero);
20
+ any other non-boolean array is cast to boolean, which takes only
21
+ 0 / 1 / true / false. Masked cells are excluded.
20
22
 
21
23
  Also used internally by ext/ca_obj_grid.c when it needs to extract
22
24
  the addresses of true cells from a boolean axis selector. */
@@ -27,28 +29,44 @@ rb_ca_where (VALUE self)
27
29
  CArray *ca, *co;
28
30
  boolean8_t *p, *m;
29
31
  ca_size_t *q;
30
- ca_size_t i, count;
32
+ ca_size_t i, j, n, last, count;
31
33
 
32
- bool0 = ( ! rb_ca_is_boolean_type(self) ) ? rb_ca_to_boolean(self) : self;
34
+ TypedData_Get_Struct(self, CArray, &carray_data_type, ca);
35
+ if ( ca_is_boolean_type(ca) ) {
36
+ bool0 = self;
37
+ }
38
+ else if ( ! ca_is_face(ca) &&
39
+ ( ca_is_integer_type(ca) || ca_is_float_type(ca) ||
40
+ ca_is_complex_type(ca) ) ) {
41
+ bool0 = rb_funcall(self, rb_intern("ne"), 1, INT2FIX(0));
42
+ }
43
+ else {
44
+ bool0 = rb_ca_to_boolean(self);
45
+ }
33
46
 
34
47
  TypedData_Get_Struct(bool0, CArray, &carray_data_type, ca);
35
48
 
36
49
  ca_attach(ca); /* window: nothing raises inside */
37
50
 
38
- /* calculate elements of output array */
51
+ /* calculate elements of output array, and the last selected cell */
39
52
  p = (boolean8_t *) ca->ptr;
40
53
  m = ca_mask_ptr(ca);
54
+ n = ca->elements;
41
55
  count = 0;
42
56
  if ( m ) {
43
- for (i=0; i<ca->elements; i++) {
44
- if ( ( ! *m ) && ( *p ) ) { count++; } /* not-masked && true */
45
- m++; p++;
57
+ for (i=0; i<n; i++) {
58
+ count += ( ! m[i] ) & ( p[i] != 0 ); /* not-masked && true */
46
59
  }
47
60
  }
48
61
  else {
49
- for (i=0; i<ca->elements; i++) {
50
- if ( *p ) { count++; } /* true */
51
- p++;
62
+ for (i=0; i<n; i++) {
63
+ count += ( p[i] != 0 ); /* true */
64
+ }
65
+ }
66
+ last = n - 1;
67
+ if ( count > 0 ) {
68
+ while ( m ? ( m[last] || ! p[last] ) : ! p[last] ) {
69
+ last--;
52
70
  }
53
71
  }
54
72
 
@@ -56,20 +74,25 @@ rb_ca_where (VALUE self)
56
74
  obj = rb_carray_new(CA_SIZE, 1, &count, 0, NULL);
57
75
  TypedData_Get_Struct(obj, CArray, &carray_data_type, co);
58
76
 
59
- /* store address which elements is true to output array */
60
- p = (boolean8_t *) ca->ptr;
77
+ /* Store the address of each selected cell. Every cell up to the last
78
+ selected one is written and the output position advances only on a
79
+ selected cell, so a selection with true and false mixed at random
80
+ does not mispredict a branch on most cells. Stopping at the last
81
+ selected cell keeps the writes inside the count slots. */
61
82
  q = (ca_size_t *) co->ptr;
62
- m = ca_mask_ptr(ca);
63
- if ( m ) {
64
- for (i=0; i<ca->elements; i++) { /* not-masked && true */
65
- if ( ( ! *m ) && ( *p ) ) { *q = i; q++; }
66
- m++; p++;
83
+ j = 0;
84
+ if ( count > 0 ) {
85
+ if ( m ) {
86
+ for (i=0; i<=last; i++) { /* not-masked && true */
87
+ q[j] = i;
88
+ j += ( ! m[i] ) & ( p[i] != 0 );
89
+ }
67
90
  }
68
- }
69
- else { /* true */
70
- for (i=0; i<ca->elements; i++) {
71
- if ( *p ) { *q = i; q++; }
72
- p++;
91
+ else { /* true */
92
+ for (i=0; i<=last; i++) {
93
+ q[j] = i;
94
+ j += ( p[i] != 0 );
95
+ }
73
96
  }
74
97
  }
75
98
 
@@ -80,6 +103,17 @@ rb_ca_where (VALUE self)
80
103
 
81
104
  /* ----------------------------------------------------------------- */
82
105
 
106
+ /* A number that is not an Integer (a Float, a Rational, a BigDecimal) makes
107
+ the progression a real one: every cell is offset + step*i computed in
108
+ double, and an integer array takes it as a store takes a float (cut
109
+ toward zero; the run is checked to fit before anything is written). */
110
+ static int
111
+ ca_seq_is_real (VALUE v)
112
+ {
113
+ return ! NIL_P(v) && ! RB_INTEGER_TYPE_P(v) && ! RB_TYPE_P(v, T_COMPLEX)
114
+ && RTEST(rb_obj_is_kind_of(v, rb_cNumeric));
115
+ }
116
+
83
117
  /* Fill the whole array in row-major order with `step*i + offset`. */
84
118
  #define proc_seq_bang(type, from, to) \
85
119
  { \
@@ -90,9 +124,8 @@ rb_ca_where (VALUE self)
90
124
  *p++ = (type) to(i); \
91
125
  } \
92
126
  } \
93
- else if ( rb_obj_is_kind_of(rstep, rb_cFloat) || \
94
- rb_obj_is_kind_of(roffset, rb_cFloat) ) { \
95
- type offset = (NIL_P(roffset)) ? (type) 0 : (type) from(roffset); \
127
+ else if ( ca_seq_is_real(rstep) || ca_seq_is_real(roffset) ) { \
128
+ double offset = (NIL_P(roffset)) ? 0 : NUM2DBL(roffset); \
96
129
  double step = (NIL_P(rstep)) ? 1 : NUM2DBL(rstep); \
97
130
  for (i=0; i<ca->elements; i++) { \
98
131
  *p++ = (type) to(step*i+offset); \
@@ -118,9 +151,8 @@ rb_ca_where (VALUE self)
118
151
  { \
119
152
  type *p = (type *)ca->ptr; \
120
153
  ca_size_t o, k, in; \
121
- if ( rb_obj_is_kind_of(rstep, rb_cFloat) || \
122
- rb_obj_is_kind_of(roffset, rb_cFloat) ) { \
123
- type offset = (NIL_P(roffset)) ? (type) 0 : (type) from(roffset); \
154
+ if ( ca_seq_is_real(rstep) || ca_seq_is_real(roffset) ) { \
155
+ double offset = (NIL_P(roffset)) ? 0 : NUM2DBL(roffset); \
124
156
  double step = (NIL_P(rstep)) ? 1 : NUM2DBL(rstep); \
125
157
  for (o=0; o<outer_size; o++) { \
126
158
  for (k=0; k<len; k++) { \
@@ -183,7 +215,7 @@ ca_seq_resolve_axis (CArray *ca, VALUE raxis)
183
215
  "%s: axis must be a single Integer (a seq over several axes is not supported)",
184
216
  ca_calling_method_name());
185
217
  }
186
- axis = rb_ca_normalize_axis_for_ndim(ca_axis_integer(raxis, NULL), ca->ndim, NULL);
218
+ axis = ca_normalize_axis_arg(raxis, ca->ndim, NULL);
187
219
  return axis;
188
220
  }
189
221
 
@@ -209,6 +241,7 @@ ca_seq_geometry (CArray *ca, int axis,
209
241
  left as they were, as they would for an entity. */
210
242
 
211
243
  typedef struct {
244
+ VALUE self;
212
245
  CArray *ca;
213
246
  VALUE roffset;
214
247
  VALUE rstep;
@@ -216,6 +249,14 @@ typedef struct {
216
249
  int axis;
217
250
  } ca_seq_ctx_t;
218
251
 
252
+ /* A value of the progression as it goes into a cell: a Face receiver (a
253
+ CAString) checks it as it checks any value stored into it. */
254
+ static VALUE
255
+ ca_seq_cell (ca_seq_ctx_t *c, VALUE v)
256
+ {
257
+ return ca_face_element_to_storage(c->self, c->ca, v);
258
+ }
259
+
219
260
  static VALUE
220
261
  ca_seq_bang_object_body (VALUE arg)
221
262
  {
@@ -227,10 +268,6 @@ ca_seq_bang_object_body (VALUE arg)
227
268
  VALUE *p;
228
269
  ca_size_t i;
229
270
 
230
- if ( ca_has_mask(ca) ) {
231
- ca_clear_mask(ca); /* clear all mask */
232
- }
233
-
234
271
  if ( axis < 0 ) { /* flat fill */
235
272
  p = (VALUE *)ca->ptr;
236
273
  if ( rb_obj_is_kind_of(roffset, rb_cFloat) ||
@@ -238,7 +275,7 @@ ca_seq_bang_object_body (VALUE arg)
238
275
  double offset = ( NIL_P(roffset) ) ? 0 : NUM2DBL(roffset);
239
276
  double step = ( NIL_P(rstep) ) ? 1 : NUM2DBL(rstep);
240
277
  for (i=0; i<ca->elements; i++) {
241
- *p++ = rb_float_new(step*i+offset);
278
+ *p++ = ca_seq_cell(c, rb_float_new(step*i+offset));
242
279
  }
243
280
  }
244
281
  else if ( NIL_P(roffset) ) {
@@ -247,22 +284,22 @@ ca_seq_bang_object_body (VALUE arg)
247
284
  "nil is invalid as offset for seq([offset[,step])");
248
285
  }
249
286
  for (i=0; i<ca->elements; i++) { /* a.seq() */
250
- *p++ = LL2NUM(i);
287
+ *p++ = ca_seq_cell(c, LL2NUM(i));
251
288
  }
252
289
  }
253
290
  else if ( ! NIL_P(rmethod) ) { /* a.seq(obj, :method) */
254
291
  ID id_method = SYM2ID(rmethod);
255
- *p++ = rval = roffset;
292
+ *p++ = ca_seq_cell(c, rval = roffset);
256
293
  for (i=1; i<ca->elements; i++) {
257
- *p++ = rval = rb_funcall(rval, id_method, 0);
294
+ *p++ = ca_seq_cell(c, rval = rb_funcall(rval, id_method, 0));
258
295
  }
259
296
  }
260
297
  else { /* a.seq(obj, step) */
261
298
  ID id_plus = rb_intern("+");
262
299
  rstep = ( NIL_P(rstep) ) ? INT2NUM(1) : rstep;
263
- *p++ = rval = roffset;
300
+ *p++ = ca_seq_cell(c, rval = roffset);
264
301
  for (i=1; i<ca->elements; i++) {
265
- *p++ = rval = rb_funcall(rval, id_plus, 1, rstep);
302
+ *p++ = ca_seq_cell(c, rval = rb_funcall(rval, id_plus, 1, rstep));
266
303
  }
267
304
  }
268
305
  }
@@ -280,7 +317,7 @@ ca_seq_bang_object_body (VALUE arg)
280
317
  double offset = ( NIL_P(roffset) ) ? 0 : NUM2DBL(roffset);
281
318
  double step = ( NIL_P(rstep) ) ? 1 : NUM2DBL(rstep);
282
319
  for (k=0; k<len; k++) {
283
- rb_ary_push(vtbl, rb_float_new(step*k+offset));
320
+ rb_ary_push(vtbl, ca_seq_cell(c, rb_float_new(step*k+offset)));
284
321
  }
285
322
  }
286
323
  else if ( NIL_P(roffset) ) {
@@ -289,26 +326,26 @@ ca_seq_bang_object_body (VALUE arg)
289
326
  "nil is invalid as offset for seq([offset[,step])");
290
327
  }
291
328
  for (k=0; k<len; k++) {
292
- rb_ary_push(vtbl, LL2NUM(k));
329
+ rb_ary_push(vtbl, ca_seq_cell(c, LL2NUM(k)));
293
330
  }
294
331
  }
295
332
  else if ( ! NIL_P(rmethod) ) {
296
333
  ID id_method = SYM2ID(rmethod);
297
334
  rval = roffset;
298
- rb_ary_push(vtbl, rval);
335
+ rb_ary_push(vtbl, ca_seq_cell(c, rval));
299
336
  for (k=1; k<len; k++) {
300
337
  rval = rb_funcall(rval, id_method, 0);
301
- rb_ary_push(vtbl, rval);
338
+ rb_ary_push(vtbl, ca_seq_cell(c, rval));
302
339
  }
303
340
  }
304
341
  else {
305
342
  ID id_plus = rb_intern("+");
306
343
  rstep = ( NIL_P(rstep) ) ? INT2NUM(1) : rstep;
307
344
  rval = roffset;
308
- rb_ary_push(vtbl, rval);
345
+ rb_ary_push(vtbl, ca_seq_cell(c, rval));
309
346
  for (k=1; k<len; k++) {
310
347
  rval = rb_funcall(rval, id_plus, 1, rstep);
311
- rb_ary_push(vtbl, rval);
348
+ rb_ary_push(vtbl, ca_seq_cell(c, rval));
312
349
  }
313
350
  }
314
351
  p = (VALUE *)ca->ptr;
@@ -320,6 +357,10 @@ ca_seq_bang_object_body (VALUE arg)
320
357
  }
321
358
  }
322
359
 
360
+ /* Cleared last, so a fill that raises leaves the mask as it was. */
361
+ if ( ca_has_mask(ca) ) {
362
+ ca_clear_mask(ca);
363
+ }
323
364
  return Qnil;
324
365
  }
325
366
 
@@ -330,6 +371,7 @@ rb_ca_seq_bang_object (VALUE self, VALUE roffset, VALUE rstep, int axis)
330
371
 
331
372
  TypedData_Get_Struct(self, CArray, &carray_data_type, c.ca);
332
373
 
374
+ c.self = self;
333
375
  c.rmethod = Qnil;
334
376
  if ( TYPE(rstep) == T_SYMBOL ) { /* e.g. a.seq("a", :succ) */
335
377
  c.rmethod = rstep;
@@ -344,8 +386,9 @@ rb_ca_seq_bang_object (VALUE self, VALUE roffset, VALUE rstep, int axis)
344
386
  return self;
345
387
  }
346
388
 
347
- /* The numeric fill converts offset and step before it writes a cell, so
348
- a raise there leaves nothing to push back: an allocate window. */
389
+ /* The numeric fill runs in an allocate window, which clears an entity's
390
+ mask as it opens (an entity has no separate buffer), so nothing in it may
391
+ raise: offset and step are checked to be numbers before it opens. */
349
392
  static VALUE
350
393
  ca_seq_bang_numeric_body (VALUE arg)
351
394
  {
@@ -368,6 +411,76 @@ ca_seq_bang_numeric_body (VALUE arg)
368
411
  return Qnil;
369
412
  }
370
413
 
414
+ /* An integer array refuses a first value it cannot hold, as a store does,
415
+ and an Integer step whose size it cannot hold. An unsigned array takes
416
+ a negative step (a descending run). A run that passes the end of the
417
+ type wraps, as integer arithmetic does. */
418
+ static void
419
+ ca_seq_check_integer_args (CArray *ca, VALUE roffset, VALUE rstep)
420
+ {
421
+ int8_t dt = ca->data_type;
422
+ /* Only a number is checked here; anything else is left to the fill's
423
+ own conversion, which refuses it with TypeError. */
424
+ if ( RB_INTEGER_TYPE_P(roffset) || RB_FLOAT_TYPE_P(roffset) ) {
425
+ VALUE first = RB_INTEGER_TYPE_P(roffset) ? roffset : rb_Integer(roffset);
426
+ if ( ca_integer_range_side(first, dt) != 0 ) {
427
+ rb_raise(rb_eRangeError, "%s: first value %"PRIsVALUE" is out of range for %s",
428
+ ca_calling_method_name(), rb_inspect(roffset), ca_type_name[dt]);
429
+ }
430
+ }
431
+ if ( RB_INTEGER_TYPE_P(rstep) ) {
432
+ int is_unsigned = ( dt == CA_UINT8 || dt == CA_UINT16 ||
433
+ dt == CA_UINT32 || dt == CA_UINT64 );
434
+ VALUE size = rstep;
435
+ if ( is_unsigned && RTEST(rb_funcall(rstep, rb_intern("negative?"), 0)) ) {
436
+ size = rb_funcall(rstep, rb_intern("-@"), 0);
437
+ }
438
+ if ( ca_integer_range_side(size, dt) != 0 ) {
439
+ rb_raise(rb_eRangeError, "%s: step %"PRIsVALUE" is out of range for %s",
440
+ ca_calling_method_name(), rb_inspect(rstep), ca_type_name[dt]);
441
+ }
442
+ }
443
+ }
444
+
445
+ /* A real progression on an integer array: each cell takes offset + step*i
446
+ cut toward zero, as a store takes a float, so the first and the last
447
+ value (the extremes of a linear run) must fit the type, and NaN raises
448
+ as it does in a store. Checked before the fill writes anything. */
449
+ static void
450
+ ca_seq_check_real_run (CArray *ca, VALUE roffset, VALUE rstep, ca_size_t n)
451
+ {
452
+ int8_t dt = ca->data_type;
453
+ double offset = NIL_P(roffset) ? 0.0 : NUM2DBL(roffset);
454
+ double step = NIL_P(rstep) ? 1.0 : NUM2DBL(rstep);
455
+ double ends[2];
456
+ double lo, hi_excl;
457
+ int k;
458
+ if ( n == 0 ) return;
459
+ ends[0] = offset;
460
+ ends[1] = step * (double) (n - 1) + offset;
461
+ switch ( dt ) {
462
+ case CA_INT8: lo = INT8_MIN; hi_excl = (double) INT8_MAX + 1; break;
463
+ case CA_UINT8: lo = 0; hi_excl = (double) UINT8_MAX + 1; break;
464
+ case CA_INT16: lo = INT16_MIN; hi_excl = (double) INT16_MAX + 1; break;
465
+ case CA_UINT16: lo = 0; hi_excl = (double) UINT16_MAX + 1; break;
466
+ case CA_INT32: lo = INT32_MIN; hi_excl = (double) INT32_MAX + 1; break;
467
+ case CA_UINT32: lo = 0; hi_excl = (double) UINT32_MAX + 1; break;
468
+ case CA_INT64: lo = -ldexp(1.0, 63); hi_excl = ldexp(1.0, 63); break;
469
+ default: lo = 0; hi_excl = ldexp(1.0, 64); break;
470
+ }
471
+ for (k = 0; k < 2; k++) {
472
+ double t = trunc(ends[k]);
473
+ if ( isnan(ends[k]) || ( step != 0.0 && isnan(step) ) ) {
474
+ rb_raise(rb_eFloatDomainError, "%s: the run has NaN",
475
+ ca_calling_method_name());
476
+ }
477
+ if ( ! ( t >= lo && t < hi_excl ) ) {
478
+ rb_raise(rb_eRangeError, "%s: value %g is out of range for %s",
479
+ ca_calling_method_name(), ends[k], ca_type_name[dt]);
480
+ }
481
+ }
482
+ }
483
+
371
484
  /* CArray#seq!(init_val=0, step=1, axis: nil) -- arithmetic-progression
372
485
  fill, in place. Without `axis:` the fill runs in row-major order
373
486
  over the whole array; with `axis: k` the progression runs along axis
@@ -397,6 +510,34 @@ rb_ca_seq_bang_method (int argc, VALUE *argv, VALUE self)
397
510
  return rb_ca_seq_bang_object(self, roffset, rstep, axis);
398
511
  }
399
512
 
513
+ {
514
+ VALUE args[2];
515
+ const char *names[2] = { "offset", "step" };
516
+ int k;
517
+ args[0] = roffset;
518
+ args[1] = rstep;
519
+ for (k = 0; k < 2; k++) {
520
+ VALUE v = args[k];
521
+ if ( NIL_P(v) ) continue;
522
+ if ( ! RTEST(rb_obj_is_kind_of(v, rb_cNumeric)) ||
523
+ ( RB_TYPE_P(v, T_COMPLEX) && ! ca_is_complex_type(ca) ) ) {
524
+ rb_raise(rb_eTypeError, "%s: %s must be a %snumber (got %"PRIsVALUE")",
525
+ ca_calling_method_name(), names[k],
526
+ ca_is_complex_type(ca) ? "" : "real ", rb_inspect(v));
527
+ }
528
+ }
529
+ }
530
+
531
+ if ( ca_is_integer_type(ca) ) {
532
+ if ( ca_seq_is_real(roffset) || ca_seq_is_real(rstep) ) {
533
+ ca_seq_check_real_run(ca, roffset, rstep,
534
+ axis < 0 ? ca->elements : ca->dim[axis]);
535
+ }
536
+ else {
537
+ ca_seq_check_integer_args(ca, roffset, rstep);
538
+ }
539
+ }
540
+
400
541
  {
401
542
  ca_seq_ctx_t c;
402
543
  c.ca = ca;
@@ -26,11 +26,38 @@
26
26
  #include "carray_internal.h" /* ca_attach_window */
27
27
  #include <math.h>
28
28
 
29
- /* Resolve `redges` to a 1-D float64 CArray of at least two edges. The
30
- caller copies the edges out (ca_xfer_all into its own buffer) rather
31
- than attaching them: the walk that follows reads the caller's array,
32
- and a raise there must not leave the edges attached. */
33
- static CArray *
29
+ /* Edges are the bin boundaries and must read as such: no masked edge (its
30
+ stored value is no boundary), no NaN, and no edge below the one before
31
+ it. Equal edges (a bin of zero width) and infinite ones are kept. */
32
+ static void
33
+ histbin_check_edge_mask (CArray *ce, const char *name)
34
+ {
35
+ if ( ca_has_mask(ce) && ca_is_any_masked(ce) ) {
36
+ rb_raise(rb_eArgError, "%s: edges has a masked value", name);
37
+ }
38
+ }
39
+
40
+ static void
41
+ histbin_check_edge_order (const double *ep, ca_size_t n, const char *name)
42
+ {
43
+ ca_size_t i;
44
+ for (i = 0; i < n; i++) {
45
+ if ( isnan(ep[i]) ) {
46
+ rb_raise(rb_eArgError, "%s: edges has NaN at %lld", name, (long long) i);
47
+ }
48
+ if ( i > 0 && ep[i] < ep[i-1] ) {
49
+ rb_raise(rb_eArgError, "%s: edges must not decrease "
50
+ "(%g at %lld after %g)", name, ep[i], (long long) i, ep[i-1]);
51
+ }
52
+ }
53
+ }
54
+
55
+ /* Resolve `redges` to a 1-D float64 CArray of at least two edges, and
56
+ return it: it may be a view made here, which only the caller can keep
57
+ alive. The caller copies the edges out (ca_xfer_all into its own
58
+ buffer) rather than attaching them: the walk that follows reads the
59
+ caller's array, and a raise there must not leave the edges attached. */
60
+ static VALUE
34
61
  histbin_resolve_edges (VALUE self, VALUE redges)
35
62
  {
36
63
  CArray *ce;
@@ -50,8 +77,9 @@ histbin_resolve_edges (VALUE self, VALUE redges)
50
77
  if ( ce->elements < 2 ) {
51
78
  rb_raise(rb_eArgError, "histbin_ki: edges needs at least 2 values");
52
79
  }
80
+ histbin_check_edge_mask(ce, "bin_to");
53
81
 
54
- return ce;
82
+ return redges;
55
83
  }
56
84
 
57
85
  /* ---- shared binning helpers (used by histbin_ki + the fused kernel) ---- */
@@ -144,10 +172,13 @@ rb_ca_histbin_ki (VALUE self, VALUE redges, VALUE rinclude_max)
144
172
  ca_type_name[ca->data_type]);
145
173
  }
146
174
 
147
- ce = histbin_resolve_edges(self, redges);
175
+ redges = histbin_resolve_edges(self, redges);
176
+ GetCArray(redges, ce);
148
177
  ep = ALLOCV_N(double, hedges, ce->elements);
149
178
  ca_xfer_all(ce, ep, CA_XFER_GET);
179
+ histbin_check_edge_order(ep, ce->elements, "bin_to");
150
180
  histbin_axis_setup(&ax, ep, ce->elements, RTEST(rinclude_max));
181
+ RB_GC_GUARD(redges); /* a view made by histbin_resolve_edges is read through ce */
151
182
 
152
183
  /* output: fresh contiguous int64, same shape as self. */
153
184
  vout = rb_carray_new(CA_INT64, ca->ndim, ca->dim, 0, NULL);
@@ -314,6 +345,10 @@ histogram_scatter_edges (VALUE arg)
314
345
  {
315
346
  histogram_scatter_t *c = (histogram_scatter_t *) arg;
316
347
  long k;
348
+ for ( k = 0; k < c->M; k++ ) {
349
+ histbin_check_edge_order((const double *) c->edge_ca[k]->ptr,
350
+ c->edge_ca[k]->elements, "histogram");
351
+ }
317
352
  for ( k = 0; k < c->M; k++ ) {
318
353
  histbin_axis_setup(&c->axes[k], (const double *) c->edge_ca[k]->ptr,
319
354
  c->edge_ca[k]->elements,
@@ -382,6 +417,7 @@ rb_ca_histogram_scatter_ki (VALUE self, VALUE rcounts, VALUE redges,
382
417
  for ( k = 0; k < M; k++ ) {
383
418
  VALUE e = RARRAY_AREF(redges, k);
384
419
  GetCArray(e, edge_ca[k]);
420
+ histbin_check_edge_mask(edge_ca[k], "histogram");
385
421
  }
386
422
 
387
423
  /* ext strides (row-major within the trailing M bin axes of counts). */
@@ -428,7 +464,16 @@ rb_ca_histogram_scatter_ki (VALUE self, VALUE rcounts, VALUE redges,
428
464
  weights = nil OR float64 [fiber..., A] (second iterator in lockstep).
429
465
  =========================================================================== */
430
466
 
431
- #define BINCOUNT_ND_BODY(LABEL_T) \
467
+ /* A label at or above the last bin lands in the overflow cell. An unsigned
468
+ 64-bit label above INT64_MAX must be clamped before it becomes an int64,
469
+ or it turns negative and indexes before the counts. */
470
+ static inline int64_t
471
+ bincount_nd_label_u64 (uint64_t x)
472
+ {
473
+ return ( x > (uint64_t) INT64_MAX ) ? INT64_MAX : (int64_t) x;
474
+ }
475
+
476
+ #define BINCOUNT_ND_BODY(LABEL_T, TO_INT64) \
432
477
  do { \
433
478
  while ( ca_iter_state_next_slab_axes(&st, &p, &m) ) { \
434
479
  ca_size_t fiber_base = 0, A, a, kk, ss, cs, sms, cms, ws = 0, wms = 0; \
@@ -441,7 +486,7 @@ rb_ca_histogram_scatter_ki (VALUE self, VALUE rcounts, VALUE redges,
441
486
  for ( a = 0; a < A; a++ ) { \
442
487
  ca_size_t off = fiber_base; int masked = 0; \
443
488
  for ( kk = 0; kk < (ca_size_t) M; kk++ ) { \
444
- int64_t v = (int64_t) *(const LABEL_T *)(p + a * ss + kk * cs); \
489
+ int64_t v = TO_INT64(*(const LABEL_T *)(p + a * ss + kk * cs)); \
445
490
  if ( m && m[a * sms + kk * cms] ) { masked = 1; break; } \
446
491
  if ( v >= ext_dim[kk] - 1 ) v = ext_dim[kk] - 1; \
447
492
  off += (ca_size_t) v * ext_stride[kk]; \
@@ -515,14 +560,14 @@ rb_ca_bincount_nd_count_ki (VALUE self, VALUE rcounts, VALUE rweights)
515
560
  for ( mm = 0; mm < st.outer_ndim; mm++ ) cur_outer_idx[mm] = 0;
516
561
 
517
562
  switch ( ca->data_type ) {
518
- case CA_INT8: BINCOUNT_ND_BODY(int8_t); break;
519
- case CA_UINT8: BINCOUNT_ND_BODY(uint8_t); break;
520
- case CA_INT16: BINCOUNT_ND_BODY(int16_t); break;
521
- case CA_UINT16: BINCOUNT_ND_BODY(uint16_t); break;
522
- case CA_INT32: BINCOUNT_ND_BODY(int32_t); break;
523
- case CA_UINT32: BINCOUNT_ND_BODY(uint32_t); break;
524
- case CA_INT64: BINCOUNT_ND_BODY(int64_t); break;
525
- case CA_UINT64: BINCOUNT_ND_BODY(uint64_t); break;
563
+ case CA_INT8: BINCOUNT_ND_BODY(int8_t, (int64_t)); break;
564
+ case CA_UINT8: BINCOUNT_ND_BODY(uint8_t, (int64_t)); break;
565
+ case CA_INT16: BINCOUNT_ND_BODY(int16_t, (int64_t)); break;
566
+ case CA_UINT16: BINCOUNT_ND_BODY(uint16_t, (int64_t)); break;
567
+ case CA_INT32: BINCOUNT_ND_BODY(int32_t, (int64_t)); break;
568
+ case CA_UINT32: BINCOUNT_ND_BODY(uint32_t, (int64_t)); break;
569
+ case CA_INT64: BINCOUNT_ND_BODY(int64_t, (int64_t)); break;
570
+ case CA_UINT64: BINCOUNT_ND_BODY(uint64_t, bincount_nd_label_u64); break;
526
571
  default:
527
572
  ca_iter_state_finish(&st);
528
573
  if ( weighted ) ca_iter_state_finish(&stw);
data/ext/carray_hold.c CHANGED
@@ -20,6 +20,7 @@
20
20
  #include "carray.h"
21
21
  #include "ca_kernel_iterator.h"
22
22
  #include "ca_obj_face.h"
23
+ #include "carray_internal.h" /* ca_normalize_axis_arg */
23
24
 
24
25
  /* Forward hold along `axis`. Per fiber along the scan axis:
25
26
  - present cell -> output = cell value, becomes the held value;
@@ -47,22 +48,24 @@ rb_ca_hold_forward (VALUE self, int axis)
47
48
  ca_iter_state st_in, st_out;
48
49
  int rc = ca_iter_state_init_l2(&st_in, ca, CA_SLAB_AXES, slab_axes, 1, 0);
49
50
  if ( rc != CA_ITER_OK ) {
50
- rb_raise(rb_eRuntimeError, "__hold__: input init failed rc=%d", rc);
51
+ rb_raise(rb_eRuntimeError, "hold_forward: input init failed rc=%d", rc);
51
52
  }
52
53
  rc = ca_iter_state_init_l2_paired(&st_out, &st_in, co, CA_SLAB_AXES,
53
54
  slab_axes, 1,
54
55
  CA_KERNEL_WRITE);
55
56
  if ( rc != CA_ITER_OK ) {
56
57
  ca_iter_state_finish(&st_in);
57
- rb_raise(rb_eRuntimeError, "__hold__: output init failed rc=%d", rc);
58
+ rb_raise(rb_eRuntimeError, "hold_forward: output init failed rc=%d", rc);
58
59
  }
59
60
 
60
61
  /* Output mask allocated lazily on the first leading-masked (UNDEF) cell:
61
62
  a fiber can only go UNDEF on a leading masked run, so a fully filled
62
- result carries no mask at all. op_mask parallels the fresh contiguous
63
- output value buffer co->ptr. */
63
+ result carries no mask at all. The output is a fresh row-major entity
64
+ and the walk cuts its slabs from a buffer laid out the same way (the
65
+ output's memory, or the walk's scratch when it delivers by transfer),
66
+ so a slab's offset from st_out.alias_ptr is its offset in co, which
67
+ op_mask parallels. */
64
68
  boolean8_t *op_mask = NULL;
65
- char *co_root = (char *) co->ptr;
66
69
 
67
70
  char *pi, *po;
68
71
  boolean8_t *mi, *mo;
@@ -73,7 +76,7 @@ rb_ca_hold_forward (VALUE self, int axis)
73
76
  ca_size_t oS = st_out.slab_strides[0]; /* output data byte stride */
74
77
  ca_size_t iMS = st_in.slab_mask_strides[0]; /* input mask element stride */
75
78
  ca_size_t oMStep = oS / K; /* output mask element step */
76
- ca_size_t oMBase = (ca_size_t)(po - co_root) / K;
79
+ ca_size_t oMBase = (ca_size_t)(po - st_out.alias_ptr) / K;
77
80
  const char *held = NULL; /* last valid cell (points into input slab) */
78
81
 
79
82
  for ( ca_size_t j = 0; j < N; j++ ) {
@@ -104,7 +107,7 @@ rb_ca_hold_forward (VALUE self, int axis)
104
107
  return vout;
105
108
  }
106
109
 
107
- /* __hold__(axis): private forward-hold primitive. axis is a required
110
+ /* hold_forward(axis): private forward-hold primitive. axis is a required
108
111
  Integer (negative counts from the end). The Ruby wrapper composes
109
112
  backward / flatten / linear on top of this. */
110
113
  static VALUE
@@ -112,13 +115,7 @@ rb_ca_hold_method (VALUE self, VALUE raxis)
112
115
  {
113
116
  CArray *ca;
114
117
  GetCArray(self, ca);
115
- int axis = NUM2INT(raxis);
116
- if ( axis < 0 ) axis += ca->ndim;
117
- if ( axis < 0 || axis >= ca->ndim ) {
118
- rb_raise(rb_eArgError, "__hold__: axis %d out of range for ndim %d",
119
- NUM2INT(raxis), ca->ndim);
120
- }
121
- return rb_ca_hold_forward(self, axis);
118
+ return rb_ca_hold_forward(self, ca_normalize_axis_arg(raxis, ca->ndim, NULL));
122
119
  }
123
120
 
124
121
  /* first / last: the reduction sibling of hold. Reduce each fiber to its
@@ -223,7 +220,7 @@ rb_ca_last (int argc, VALUE *argv, VALUE self)
223
220
  void
224
221
  Init_carray_hold (void)
225
222
  {
226
- rb_define_private_method(rb_cCArray, "__hold__", rb_ca_hold_method, 1);
223
+ rb_define_private_method(rb_cCArray, "hold_forward", rb_ca_hold_method, 1);
227
224
  rb_define_method(rb_cCArray, "first", rb_ca_first, -1);
228
225
  rb_define_method(rb_cCArray, "last", rb_ca_last, -1);
229
226
  }