carray 3.0.3 → 3.0.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (221) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +469 -0
  3. data/README.md +3 -2
  4. data/carray.gemspec +1 -1
  5. data/ext/ca_array_pool.c +25 -1
  6. data/ext/ca_axis_descriptor.h +27 -24
  7. data/ext/ca_axis_dispatch.c +14 -26
  8. data/ext/ca_axis_group.c +27 -27
  9. data/ext/ca_binop_dispatch.c +1 -29
  10. data/ext/ca_binop_dispatch.h +0 -1
  11. data/ext/ca_categorical_iterator.c +231 -153
  12. data/ext/ca_compare.c +9 -20
  13. data/ext/ca_composite_dispatch.c +9 -9
  14. data/ext/ca_group_iter.c +16 -17
  15. data/ext/ca_kernel_iterator.c +1240 -2000
  16. data/ext/ca_kernel_iterator.h +502 -519
  17. data/ext/ca_monop_dispatch.c +82 -59
  18. data/ext/ca_monop_dispatch.h +1 -0
  19. data/ext/ca_obj_array.c +152 -45
  20. data/ext/ca_obj_bincmp.c +25 -21
  21. data/ext/ca_obj_binop.c +16 -26
  22. data/ext/ca_obj_bitarray.c +8 -3
  23. data/ext/ca_obj_bitfield.c +63 -49
  24. data/ext/ca_obj_block.c +18 -9
  25. data/ext/ca_obj_byte_swap.c +7 -7
  26. data/ext/ca_obj_const_string.c +188 -22
  27. data/ext/ca_obj_face.c +280 -58
  28. data/ext/ca_obj_face.h +19 -0
  29. data/ext/ca_obj_fake.c +15 -29
  30. data/ext/ca_obj_farray.c +6 -3
  31. data/ext/ca_obj_field.c +30 -8
  32. data/ext/ca_obj_fixlen_string.c +2 -3
  33. data/ext/ca_obj_grid.c +455 -134
  34. data/ext/ca_obj_meld.c +172 -34
  35. data/ext/ca_obj_moncmp.c +23 -8
  36. data/ext/ca_obj_monop.c +113 -67
  37. data/ext/ca_obj_object.c +210 -52
  38. data/ext/ca_obj_record.c +15 -7
  39. data/ext/ca_obj_reduce.c +17 -8
  40. data/ext/ca_obj_refer.c +42 -35
  41. data/ext/ca_obj_remap.c +30 -9
  42. data/ext/ca_obj_repeat.c +9 -10
  43. data/ext/ca_obj_roll.c +31 -10
  44. data/ext/ca_obj_select.c +48 -14
  45. data/ext/ca_obj_select_axis.c +15 -47
  46. data/ext/ca_obj_shift.c +41 -7
  47. data/ext/ca_obj_stack.c +180 -34
  48. data/ext/ca_obj_stride.c +190 -134
  49. data/ext/ca_obj_string.c +80 -4
  50. data/ext/ca_obj_tile.c +32 -11
  51. data/ext/ca_obj_time.c +22 -6
  52. data/ext/ca_obj_timedelta.c +22 -6
  53. data/ext/ca_obj_transpose.c +13 -7
  54. data/ext/ca_obj_triop.c +26 -32
  55. data/ext/ca_obj_window.c +267 -74
  56. data/ext/ca_op_ipower.c +4 -4
  57. data/ext/ca_sort_kernels.h +6 -3
  58. data/ext/ca_sweep_engine.c +2 -2
  59. data/ext/caframe_csv_reader.c +80 -1
  60. data/ext/carray.h +214 -75
  61. data/ext/carray_access.c +200 -99
  62. data/ext/carray_address_basis.c +343 -87
  63. data/ext/carray_attribute.c +113 -6
  64. data/ext/carray_bincount.c +10 -8
  65. data/ext/carray_broadcast.c +13 -8
  66. data/ext/carray_build_flags.h +3 -0
  67. data/ext/carray_call_cfunc.c +108 -0
  68. data/ext/carray_cast.c +375 -96
  69. data/ext/carray_cast_func.rb +90 -20
  70. data/ext/carray_class.c +4 -3
  71. data/ext/carray_config.h +28 -0
  72. data/ext/carray_conversion.c +18 -9
  73. data/ext/carray_copy.c +51 -14
  74. data/ext/carray_core.c +150 -55
  75. data/ext/carray_count.c +56 -20
  76. data/ext/carray_element.c +81 -33
  77. data/ext/carray_factorize.c +138 -84
  78. data/ext/carray_generate.c +199 -52
  79. data/ext/carray_histogram.c +63 -18
  80. data/ext/carray_hold.c +12 -15
  81. data/ext/carray_index_classifier.c +388 -176
  82. data/ext/carray_internal.h +348 -29
  83. data/ext/carray_kernels_bincmp.c +4453 -0
  84. data/ext/carray_kernels_binop.c +11368 -0
  85. data/ext/carray_kernels_init.c +1734 -0
  86. data/ext/carray_kernels_moncmp.c +2104 -0
  87. data/ext/carray_kernels_monop.c +18369 -0
  88. data/ext/carray_kernels_reduce_aggregate.c +23818 -0
  89. data/ext/carray_kernels_reduce_boolean.c +406 -0
  90. data/ext/carray_kernels_reduce_cumulative.c +10086 -0
  91. data/ext/carray_kernels_reduce_extreme.c +17634 -0
  92. data/ext/carray_kernels_reduce_variance.c +3849 -0
  93. data/ext/carray_kernels_scan.c +4302 -0
  94. data/ext/carray_kernels_search.c +33829 -0
  95. data/ext/carray_kernels_sort.c +10897 -0
  96. data/ext/carray_kernels_triop.c +1864 -0
  97. data/ext/carray_key_windows.c +220 -0
  98. data/ext/carray_lazy.c +170 -111
  99. data/ext/carray_mask.c +337 -94
  100. data/ext/carray_median_percentile.c +82 -28
  101. data/ext/carray_memory_view.c +669 -561
  102. data/ext/carray_operator.c +129 -52
  103. data/ext/carray_order.c +604 -77
  104. data/ext/carray_partition.c +28 -27
  105. data/ext/carray_random.c +85 -31
  106. data/ext/carray_round.c +273 -0
  107. data/ext/carray_scatter.c +87 -45
  108. data/ext/carray_segment.c +3 -3
  109. data/ext/carray_slab.c +123 -37
  110. data/ext/carray_sort.c +259 -51
  111. data/ext/carray_sort_kernel.c +5 -7
  112. data/ext/carray_struct.c +54 -61
  113. data/ext/carray_test.c +26 -5
  114. data/ext/carray_time_text.c +4 -2
  115. data/ext/carray_undef.c +4 -0
  116. data/ext/carray_utils.c +309 -66
  117. data/ext/mk_call_cfunc.rb +19 -2
  118. data/ext/mkkernel.rb +403 -350
  119. data/ext/ruby_carray.c +10 -4
  120. data/ext/version.h +4 -4
  121. data/lib/carray/attribute.rb +139 -22
  122. data/lib/carray/autoload_carray.rb +8 -31
  123. data/lib/carray/autoload_method_extension.rb +14 -5
  124. data/lib/carray/axis_group.rb +255 -106
  125. data/lib/carray/basics.rb +105 -17
  126. data/lib/carray/bincount_nd.rb +12 -2
  127. data/lib/carray/block_iterator.rb +126 -23
  128. data/lib/carray/boolean_reduce.rb +17 -17
  129. data/lib/carray/categorical.rb +195 -59
  130. data/lib/carray/categorical_iterator.rb +132 -29
  131. data/lib/carray/complex.rb +5 -5
  132. data/lib/carray/conditional.rb +67 -18
  133. data/lib/carray/const_string.rb +24 -5
  134. data/lib/carray/construct.rb +15 -11
  135. data/lib/carray/data_type_extension.rb +9 -1
  136. data/lib/carray/distance_limit.rb +77 -0
  137. data/lib/carray/fixlen_string.rb +48 -3
  138. data/lib/carray/frame/column_verbs.rb +71 -0
  139. data/lib/carray/frame/concat.rb +17 -3
  140. data/lib/carray/frame/convert.rb +69 -0
  141. data/lib/carray/frame/csv_parser.rb +111 -17
  142. data/lib/carray/frame/describe.rb +3 -2
  143. data/lib/carray/frame/frame.rb +252 -28
  144. data/lib/carray/frame/group.rb +603 -101
  145. data/lib/carray/frame/io.rb +117 -28
  146. data/lib/carray/frame/join.rb +312 -51
  147. data/lib/carray/frame/records.rb +83 -7
  148. data/lib/carray/frame/reshape.rb +42 -35
  149. data/lib/carray/frame/sort.rb +7 -19
  150. data/lib/carray/frame/verbs.rb +188 -48
  151. data/lib/carray/frame/windows.rb +117 -0
  152. data/lib/carray/frame.rb +2 -0
  153. data/lib/carray/fuse_source.rb +42 -6
  154. data/lib/carray/fusion.rb +40 -16
  155. data/lib/carray/histogram.rb +6 -1
  156. data/lib/carray/inspect.rb +31 -8
  157. data/lib/carray/iterator.rb +85 -7
  158. data/lib/carray/key_windows.rb +620 -0
  159. data/lib/carray/lazy.rb +217 -183
  160. data/lib/carray/mask_gap_fill.rb +63 -57
  161. data/lib/carray/meld_reduce.rb +12 -14
  162. data/lib/carray/methods/bin.rb +26 -18
  163. data/lib/carray/methods/bincount.rb +16 -0
  164. data/lib/carray/methods/bit_string.rb +1 -1
  165. data/lib/carray/methods/choose.rb +16 -3
  166. data/lib/carray/methods/composition.rb +3 -1
  167. data/lib/carray/methods/gather_nd.rb +14 -5
  168. data/lib/carray/methods/insert_block.rb +2 -2
  169. data/lib/carray/methods/is_between.rb +59 -0
  170. data/lib/carray/methods/is_in.rb +61 -37
  171. data/lib/carray/methods/join.rb +1 -6
  172. data/lib/carray/methods/lag.rb +49 -0
  173. data/lib/carray/methods/locate_addr.rb +47 -51
  174. data/lib/carray/methods/mode.rb +1 -1
  175. data/lib/carray/methods/nunique.rb +11 -6
  176. data/lib/carray/methods/resize.rb +3 -3
  177. data/lib/carray/methods/value_counts.rb +4 -11
  178. data/lib/carray/record_order.rb +3 -3
  179. data/lib/carray/rng.rb +3 -0
  180. data/lib/carray/runtime.rb +53 -8
  181. data/lib/carray/segment_iterator.rb +323 -88
  182. data/lib/carray/serialize.rb +45 -14
  183. data/lib/carray/slab_iterator.rb +50 -0
  184. data/lib/carray/stack.rb +15 -12
  185. data/lib/carray/string.rb +113 -5
  186. data/lib/carray/string_operation_extension.rb +21 -6
  187. data/lib/carray/struct.rb +9 -21
  188. data/lib/carray/struct_builder.rb +24 -11
  189. data/lib/carray/time.rb +819 -158
  190. data/lib/carray/window_iterator.rb +122 -16
  191. data/lib/carray.rb +7 -0
  192. data/yard-stubs/ca_obj_bitfield.rb +1 -1
  193. data/yard-stubs/ca_obj_field.rb +4 -0
  194. data/yard-stubs/ca_obj_grid.rb +21 -10
  195. data/yard-stubs/ca_obj_refer.rb +2 -1
  196. data/yard-stubs/ca_obj_roll.rb +2 -2
  197. data/yard-stubs/ca_obj_shift.rb +5 -1
  198. data/yard-stubs/ca_obj_tile.rb +1 -1
  199. data/yard-stubs/ca_obj_transpose.rb +5 -4
  200. data/yard-stubs/ca_obj_window.rb +8 -3
  201. data/yard-stubs/carray_access.rb +6 -1
  202. data/yard-stubs/carray_attribute.rb +3 -6
  203. data/yard-stubs/carray_cast.rb +12 -0
  204. data/yard-stubs/carray_conversion.rb +7 -2
  205. data/yard-stubs/carray_copy.rb +4 -2
  206. data/yard-stubs/carray_count.rb +4 -4
  207. data/yard-stubs/carray_element.rb +17 -10
  208. data/yard-stubs/carray_generate.rb +21 -1
  209. data/yard-stubs/carray_mask.rb +32 -3
  210. data/yard-stubs/carray_math.rb +41 -5
  211. data/yard-stubs/carray_median_percentile.rb +5 -2
  212. data/yard-stubs/carray_memory_view.rb +40 -23
  213. data/yard-stubs/carray_order.rb +129 -22
  214. data/yard-stubs/carray_random.rb +13 -4
  215. data/yard-stubs/carray_scatter.rb +17 -10
  216. data/yard-stubs/carray_slab.rb +9 -2
  217. data/yard-stubs/carray_sort.rb +6 -5
  218. data/yard-stubs/carray_test.rb +5 -3
  219. metadata +25 -3
  220. data/lib/carray/arrow_tensor.rb +0 -401
  221. data/lib/carray/table.rb +0 -108
@@ -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,19 +103,29 @@ 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
  { \
86
- type *p = (type *)ca->ptr; \
120
+ CA_U(type) *p = (CA_U(type) *)ca->ptr; \
87
121
  ca_size_t i; \
88
122
  if ( NIL_P(roffset) && NIL_P(rstep) ) { \
89
123
  for (i=0; i<ca->elements; i++) { \
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); \
@@ -116,11 +149,10 @@ rb_ca_where (VALUE self)
116
149
  `outer_size` come from the enclosing scope (ca_seq_geometry). */
117
150
  #define proc_seq_bang_axis(type, from, to) \
118
151
  { \
119
- type *p = (type *)ca->ptr; \
152
+ CA_U(type) *p = (CA_U(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,28 @@ 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
+ if ( ca->elements == 0 ) { /* no first cell to write */
293
+ return Qnil;
294
+ }
295
+ *p++ = ca_seq_cell(c, rval = roffset);
256
296
  for (i=1; i<ca->elements; i++) {
257
- *p++ = rval = rb_funcall(rval, id_method, 0);
297
+ *p++ = ca_seq_cell(c, rval = rb_funcall(rval, id_method, 0));
258
298
  }
259
299
  }
260
300
  else { /* a.seq(obj, step) */
261
301
  ID id_plus = rb_intern("+");
262
302
  rstep = ( NIL_P(rstep) ) ? INT2NUM(1) : rstep;
263
- *p++ = rval = roffset;
303
+ if ( ca->elements == 0 ) { /* no first cell to write */
304
+ return Qnil;
305
+ }
306
+ *p++ = ca_seq_cell(c, rval = roffset);
264
307
  for (i=1; i<ca->elements; i++) {
265
- *p++ = rval = rb_funcall(rval, id_plus, 1, rstep);
308
+ *p++ = ca_seq_cell(c, rval = rb_funcall(rval, id_plus, 1, rstep));
266
309
  }
267
310
  }
268
311
  }
@@ -280,7 +323,7 @@ ca_seq_bang_object_body (VALUE arg)
280
323
  double offset = ( NIL_P(roffset) ) ? 0 : NUM2DBL(roffset);
281
324
  double step = ( NIL_P(rstep) ) ? 1 : NUM2DBL(rstep);
282
325
  for (k=0; k<len; k++) {
283
- rb_ary_push(vtbl, rb_float_new(step*k+offset));
326
+ rb_ary_push(vtbl, ca_seq_cell(c, rb_float_new(step*k+offset)));
284
327
  }
285
328
  }
286
329
  else if ( NIL_P(roffset) ) {
@@ -289,26 +332,26 @@ ca_seq_bang_object_body (VALUE arg)
289
332
  "nil is invalid as offset for seq([offset[,step])");
290
333
  }
291
334
  for (k=0; k<len; k++) {
292
- rb_ary_push(vtbl, LL2NUM(k));
335
+ rb_ary_push(vtbl, ca_seq_cell(c, LL2NUM(k)));
293
336
  }
294
337
  }
295
338
  else if ( ! NIL_P(rmethod) ) {
296
339
  ID id_method = SYM2ID(rmethod);
297
340
  rval = roffset;
298
- rb_ary_push(vtbl, rval);
341
+ rb_ary_push(vtbl, ca_seq_cell(c, rval));
299
342
  for (k=1; k<len; k++) {
300
343
  rval = rb_funcall(rval, id_method, 0);
301
- rb_ary_push(vtbl, rval);
344
+ rb_ary_push(vtbl, ca_seq_cell(c, rval));
302
345
  }
303
346
  }
304
347
  else {
305
348
  ID id_plus = rb_intern("+");
306
349
  rstep = ( NIL_P(rstep) ) ? INT2NUM(1) : rstep;
307
350
  rval = roffset;
308
- rb_ary_push(vtbl, rval);
351
+ rb_ary_push(vtbl, ca_seq_cell(c, rval));
309
352
  for (k=1; k<len; k++) {
310
353
  rval = rb_funcall(rval, id_plus, 1, rstep);
311
- rb_ary_push(vtbl, rval);
354
+ rb_ary_push(vtbl, ca_seq_cell(c, rval));
312
355
  }
313
356
  }
314
357
  p = (VALUE *)ca->ptr;
@@ -320,6 +363,10 @@ ca_seq_bang_object_body (VALUE arg)
320
363
  }
321
364
  }
322
365
 
366
+ /* Cleared last, so a fill that raises leaves the mask as it was. */
367
+ if ( ca_has_mask(ca) ) {
368
+ ca_clear_mask(ca);
369
+ }
323
370
  return Qnil;
324
371
  }
325
372
 
@@ -330,6 +377,7 @@ rb_ca_seq_bang_object (VALUE self, VALUE roffset, VALUE rstep, int axis)
330
377
 
331
378
  TypedData_Get_Struct(self, CArray, &carray_data_type, c.ca);
332
379
 
380
+ c.self = self;
333
381
  c.rmethod = Qnil;
334
382
  if ( TYPE(rstep) == T_SYMBOL ) { /* e.g. a.seq("a", :succ) */
335
383
  c.rmethod = rstep;
@@ -344,8 +392,9 @@ rb_ca_seq_bang_object (VALUE self, VALUE roffset, VALUE rstep, int axis)
344
392
  return self;
345
393
  }
346
394
 
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. */
395
+ /* The numeric fill runs in an allocate window, which clears an entity's
396
+ mask as it opens (an entity has no separate buffer), so nothing in it may
397
+ raise: offset and step are checked to be numbers before it opens. */
349
398
  static VALUE
350
399
  ca_seq_bang_numeric_body (VALUE arg)
351
400
  {
@@ -368,6 +417,76 @@ ca_seq_bang_numeric_body (VALUE arg)
368
417
  return Qnil;
369
418
  }
370
419
 
420
+ /* An integer array refuses a first value it cannot hold, as a store does,
421
+ and an Integer step whose size it cannot hold. An unsigned array takes
422
+ a negative step (a descending run). A run that passes the end of the
423
+ type wraps, as integer arithmetic does. */
424
+ static void
425
+ ca_seq_check_integer_args (CArray *ca, VALUE roffset, VALUE rstep)
426
+ {
427
+ int8_t dt = ca->data_type;
428
+ /* Only a number is checked here; anything else is left to the fill's
429
+ own conversion, which refuses it with TypeError. */
430
+ if ( RB_INTEGER_TYPE_P(roffset) || RB_FLOAT_TYPE_P(roffset) ) {
431
+ VALUE first = RB_INTEGER_TYPE_P(roffset) ? roffset : rb_Integer(roffset);
432
+ if ( ca_integer_range_side(first, dt) != 0 ) {
433
+ rb_raise(rb_eRangeError, "%s: first value %"PRIsVALUE" is out of range for %s",
434
+ ca_calling_method_name(), rb_inspect(roffset), ca_type_name[dt]);
435
+ }
436
+ }
437
+ if ( RB_INTEGER_TYPE_P(rstep) ) {
438
+ int is_unsigned = ( dt == CA_UINT8 || dt == CA_UINT16 ||
439
+ dt == CA_UINT32 || dt == CA_UINT64 );
440
+ VALUE size = rstep;
441
+ if ( is_unsigned && RTEST(rb_funcall(rstep, rb_intern("negative?"), 0)) ) {
442
+ size = rb_funcall(rstep, rb_intern("-@"), 0);
443
+ }
444
+ if ( ca_integer_range_side(size, dt) != 0 ) {
445
+ rb_raise(rb_eRangeError, "%s: step %"PRIsVALUE" is out of range for %s",
446
+ ca_calling_method_name(), rb_inspect(rstep), ca_type_name[dt]);
447
+ }
448
+ }
449
+ }
450
+
451
+ /* A real progression on an integer array: each cell takes offset + step*i
452
+ cut toward zero, as a store takes a float, so the first and the last
453
+ value (the extremes of a linear run) must fit the type, and NaN raises
454
+ as it does in a store. Checked before the fill writes anything. */
455
+ static void
456
+ ca_seq_check_real_run (CArray *ca, VALUE roffset, VALUE rstep, ca_size_t n)
457
+ {
458
+ int8_t dt = ca->data_type;
459
+ double offset = NIL_P(roffset) ? 0.0 : NUM2DBL(roffset);
460
+ double step = NIL_P(rstep) ? 1.0 : NUM2DBL(rstep);
461
+ double ends[2];
462
+ double lo, hi_excl;
463
+ int k;
464
+ if ( n == 0 ) return;
465
+ ends[0] = offset;
466
+ ends[1] = step * (double) (n - 1) + offset;
467
+ switch ( dt ) {
468
+ case CA_INT8: lo = INT8_MIN; hi_excl = (double) INT8_MAX + 1; break;
469
+ case CA_UINT8: lo = 0; hi_excl = (double) UINT8_MAX + 1; break;
470
+ case CA_INT16: lo = INT16_MIN; hi_excl = (double) INT16_MAX + 1; break;
471
+ case CA_UINT16: lo = 0; hi_excl = (double) UINT16_MAX + 1; break;
472
+ case CA_INT32: lo = INT32_MIN; hi_excl = (double) INT32_MAX + 1; break;
473
+ case CA_UINT32: lo = 0; hi_excl = (double) UINT32_MAX + 1; break;
474
+ case CA_INT64: lo = -ldexp(1.0, 63); hi_excl = ldexp(1.0, 63); break;
475
+ default: lo = 0; hi_excl = ldexp(1.0, 64); break;
476
+ }
477
+ for (k = 0; k < 2; k++) {
478
+ double t = trunc(ends[k]);
479
+ if ( isnan(ends[k]) || ( step != 0.0 && isnan(step) ) ) {
480
+ rb_raise(rb_eFloatDomainError, "%s: the run has NaN",
481
+ ca_calling_method_name());
482
+ }
483
+ if ( ! ( t >= lo && t < hi_excl ) ) {
484
+ rb_raise(rb_eRangeError, "%s: value %g is out of range for %s",
485
+ ca_calling_method_name(), ends[k], ca_type_name[dt]);
486
+ }
487
+ }
488
+ }
489
+
371
490
  /* CArray#seq!(init_val=0, step=1, axis: nil) -- arithmetic-progression
372
491
  fill, in place. Without `axis:` the fill runs in row-major order
373
492
  over the whole array; with `axis: k` the progression runs along axis
@@ -397,6 +516,34 @@ rb_ca_seq_bang_method (int argc, VALUE *argv, VALUE self)
397
516
  return rb_ca_seq_bang_object(self, roffset, rstep, axis);
398
517
  }
399
518
 
519
+ {
520
+ VALUE args[2];
521
+ const char *names[2] = { "offset", "step" };
522
+ int k;
523
+ args[0] = roffset;
524
+ args[1] = rstep;
525
+ for (k = 0; k < 2; k++) {
526
+ VALUE v = args[k];
527
+ if ( NIL_P(v) ) continue;
528
+ if ( ! RTEST(rb_obj_is_kind_of(v, rb_cNumeric)) ||
529
+ ( RB_TYPE_P(v, T_COMPLEX) && ! ca_is_complex_type(ca) ) ) {
530
+ rb_raise(rb_eTypeError, "%s: %s must be a %snumber (got %"PRIsVALUE")",
531
+ ca_calling_method_name(), names[k],
532
+ ca_is_complex_type(ca) ? "" : "real ", rb_inspect(v));
533
+ }
534
+ }
535
+ }
536
+
537
+ if ( ca_is_integer_type(ca) ) {
538
+ if ( ca_seq_is_real(roffset) || ca_seq_is_real(rstep) ) {
539
+ ca_seq_check_real_run(ca, roffset, rstep,
540
+ axis < 0 ? ca->elements : ca->dim[axis]);
541
+ }
542
+ else {
543
+ ca_seq_check_integer_args(ca, roffset, rstep);
544
+ }
545
+ }
546
+
400
547
  {
401
548
  ca_seq_ctx_t c;
402
549
  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);
@@ -281,7 +312,7 @@ histogram_scatter_walk (VALUE arg)
281
312
  ca_size_t off = fiber_base;
282
313
  int masked = 0;
283
314
  for ( kk = 0; kk < (ca_size_t) M; kk++ ) {
284
- double v = *(const double *)(p + a * ss + kk * cs);
315
+ double v = *(const ca_u_float64_t *)(p + a * ss + kk * cs);
285
316
  if ( (m && m[a * sms + kk * cms]) || isnan(v) ) { masked = 1; break; }
286
317
  off += histbin_index(&axes[kk], v) * ext_stride[kk];
287
318
  }
@@ -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
  }