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
data/ext/ca_obj_stride.c CHANGED
@@ -20,7 +20,7 @@
20
20
  ---------------------------------------------------------------------------- */
21
21
 
22
22
  #include "carray.h"
23
- #include "carray_internal.h" /* ca_axis_integer */
23
+ #include "carray_internal.h" /* ca_normalize_axis_arg */
24
24
  #include "ca_iter_substrate.h"
25
25
  #include "ca_obj_face.h" /* ca_is_face, used by the compose-fold walk */
26
26
 
@@ -94,17 +94,15 @@ int8_t CA_OBJ_STRIDE; /* assigned at Init time via ca_install_obj_type */
94
94
  override pattern and makes the C-level dispatch wire up correctly
95
95
  on the first try. */
96
96
  int
97
- ca_stride_setup (CAStride *ca, int8_t obj_type, CArray *parent,
98
- int8_t data_type, ca_size_t bytes,
99
- int8_t ndim, ca_size_t *dim,
100
- ca_size_t *strides, ca_size_t base_offset)
97
+ ca_stride_setup_view (CAStride *ca, int8_t obj_type, CArray *parent,
98
+ int8_t data_type, ca_size_t bytes,
99
+ int8_t ndim, ca_size_t *dim,
100
+ ca_size_t *strides, ca_size_t base_offset)
101
101
  {
102
102
  ca_size_t elements;
103
103
  int i;
104
104
 
105
- if (ndim < 0 || ndim > CA_RANK_MAX) {
106
- rb_raise(rb_eArgError, "invalid ndim %d", (int) ndim);
107
- }
105
+ ca_check_rank_fits(ndim, NULL);
108
106
  for (i = 0; i < ndim; i++) {
109
107
  if (dim[i] < 0) {
110
108
  rb_raise(rb_eIndexError,
@@ -144,10 +142,23 @@ ca_stride_setup (CAStride *ca, int8_t obj_type, CArray *parent,
144
142
  ca->strides[i] = strides[i];
145
143
  }
146
144
 
147
- if (parent && ca_has_mask(parent)) {
148
- ca_create_mask(ca);
149
- }
145
+ return 0;
146
+ }
150
147
 
148
+ /* The public form also takes the parent's mask. A struct that no Ruby
149
+ object owns yet is lost if the parent's answer raises; the in-tree
150
+ constructors use ca_stride_setup_view and ca_view_inherit_mask. */
151
+ int
152
+ ca_stride_setup (CAStride *ca, int8_t obj_type, CArray *parent,
153
+ int8_t data_type, ca_size_t bytes,
154
+ int8_t ndim, ca_size_t *dim,
155
+ ca_size_t *strides, ca_size_t base_offset)
156
+ {
157
+ ca_stride_setup_view(ca, obj_type, parent, data_type, bytes,
158
+ ndim, dim, strides, base_offset);
159
+ if ( ca != NULL ) {
160
+ ca_view_inherit_mask((VALUE) ca);
161
+ }
151
162
  return 0;
152
163
  }
153
164
 
@@ -158,11 +169,12 @@ ca_stride_new (int8_t obj_type, CArray *parent,
158
169
  ca_size_t *strides, ca_size_t base_offset)
159
170
  {
160
171
  CAStride *ca;
161
- ca_stride_setup(NULL, obj_type, parent,
162
- data_type, bytes, ndim, dim, strides, base_offset);
172
+ ca_stride_setup_view(NULL, obj_type, parent,
173
+ data_type, bytes, ndim, dim, strides, base_offset);
163
174
  ca = (CAStride *) ca_array_alloc(obj_type, ndim);
164
- ca_stride_setup(ca, obj_type, parent,
165
- data_type, bytes, ndim, dim, strides, base_offset);
175
+ ca_stride_setup_view(ca, obj_type, parent,
176
+ data_type, bytes, ndim, dim, strides, base_offset);
177
+ ca_fill_or_free((CArray *) ca, ca_view_inherit_mask, (VALUE) ca);
166
178
  return ca;
167
179
  }
168
180
 
@@ -235,6 +247,10 @@ ca_stride_compose_through (CAStride *leaf, CAStride *parent,
235
247
  for (k = parent->ndim - 1; k >= 0; k--)
236
248
  prod[k] = prod[k + 1] * parent->dim[k];
237
249
 
250
+ /* A parent with a zero-length axis has no cell for the leaf to start at
251
+ (and prod[] below would divide by zero). */
252
+ if (prod[0] == 0) return 0;
253
+
238
254
  /* Compose base offset first. We need the per-dim base position
239
255
  to validate stride composition against parent dim bounds (a small
240
256
  forward stride starting near the end of a parent dim wraps into
@@ -443,6 +459,8 @@ ca_stride_compose_to_root (CAStride *leaf,
443
459
 
444
460
  "Identity compose-fold" semantics:
445
461
  - cand->bytes == root->bytes (no byte reinterpret)
462
+ - cand->data_type == root->data_type (no type reinterpret: a caller
463
+ may read the root's cells as typed values)
446
464
  - composed_base == 0
447
465
  - composed_strides[k] match row-major over cand->dim with cand->bytes
448
466
 
@@ -471,6 +489,7 @@ ca_resolve_attached_root_via_identity (CArray *cand)
471
489
  ca_stride_compose_to_root(cs, &root, composed_strides, &composed_base);
472
490
  if ( !root->ptr ) return cand;
473
491
  if ( cs->bytes != root->bytes ) return cand;
492
+ if ( cs->data_type != root->data_type ) return cand;
474
493
  if ( composed_base != 0 ) return cand;
475
494
 
476
495
  s = root->bytes;
@@ -610,7 +629,7 @@ ca_stride_func_xfer_addrs (void *ap, ca_size_t n, ca_size_t *addrs,
610
629
  ca_size_t composed_base;
611
630
  ca_size_t rbytes;
612
631
  ca_size_t *paddrs;
613
- ca_size_t i, base;
632
+ ca_size_t i;
614
633
  int8_t k;
615
634
  int all_aligned = 1;
616
635
  volatile VALUE holder;
@@ -619,18 +638,12 @@ ca_stride_func_xfer_addrs (void *ap, ca_size_t n, ca_size_t *addrs,
619
638
  ca_stride_compose_to_root(ca, &root, composed_strides, &composed_base);
620
639
  rbytes = root->bytes;
621
640
 
622
- /* Fast path: identity transform detection -- when the addr remap composed_base +
623
- Σ idx[k]*composed_strides[k] reduces to an identity mapping over the
624
- root's flat byte space (= simple reshape, same bytes, row-major
625
- composed_strides), forward addrs as-is to root without the ALLOCV +
626
- per-cell remap loop. Cascades through chain a[idx_2d] (= outer
627
- reshape -> CAGrid -> inner reshape -> entity): outer CARefer is a
628
- simple reshape so addrs pass through, then CAGrid Y.1.b fast path
629
- triggers on the recursive call, then inner CARefer simple reshape
630
- pass-through to entity. Detection is O(ndim) + O(n). */
631
- if ( n == ca->elements
632
- && ca->bytes == rbytes && composed_base == 0
633
- && ca_xfer_addrs_is_sequential_run(n, addrs, &base) && base == 0 ) {
641
+ /* Identity: same cell size, no offset, row-major composed strides (a
642
+ simple reshape / flatten of the root). A view address is then the
643
+ root address, whatever the addresses are, so the list goes through
644
+ as-is. Cascades through chains such as a[idx_2d] (outer reshape ->
645
+ CAGrid -> inner reshape -> entity). */
646
+ if ( ca->bytes == rbytes && composed_base == 0 ) {
634
647
  ca_size_t s = rbytes;
635
648
  int is_identity = 1;
636
649
  for ( k = ca->ndim - 1; k >= 0; k-- ) {
@@ -643,36 +656,51 @@ ca_stride_func_xfer_addrs (void *ap, ca_size_t n, ca_size_t *addrs,
643
656
  }
644
657
  }
645
658
 
646
- paddrs = ALLOCV_N(ca_size_t, holder, n);
647
- for ( i = 0; i < n; i++ ) {
648
- ca_size_t idx[CA_RANK_MAX];
649
- ca_size_t off = composed_base;
650
- ca_addr2index((CArray *) ca, addrs[i], idx);
659
+ /* Whole-cell strides: translate in cell units, with no per-cell division. */
660
+ if ( ca->bytes == rbytes && composed_base % rbytes == 0 ) {
651
661
  for ( k = 0; k < ca->ndim; k++ ) {
652
- off += idx[k] * composed_strides[k];
653
- }
654
- if ( ca->bytes == rbytes && off % rbytes == 0 ) {
655
- paddrs[i] = off / rbytes;
656
- }
657
- else {
658
- all_aligned = 0;
659
- break;
662
+ if ( composed_strides[k] % rbytes != 0 ) { all_aligned = 0; break; }
660
663
  }
661
664
  }
665
+ else {
666
+ all_aligned = 0;
667
+ }
662
668
 
663
669
  if ( all_aligned ) {
670
+ ca_size_t esteps[CA_RANK_MAX];
671
+ ca_size_t ebase = composed_base / rbytes;
672
+ for ( k = 0; k < ca->ndim; k++ ) {
673
+ esteps[k] = composed_strides[k] / rbytes;
674
+ }
675
+ paddrs = ALLOCV_N(ca_size_t, holder, n);
676
+ if ( ca->ndim == 1 ) {
677
+ for ( i = 0; i < n; i++ ) {
678
+ paddrs[i] = ebase + addrs[i] * esteps[0];
679
+ }
680
+ }
681
+ else {
682
+ for ( i = 0; i < n; i++ ) {
683
+ ca_size_t idx[CA_RANK_MAX];
684
+ ca_size_t off = ebase;
685
+ ca_addr2index((CArray *) ca, addrs[i], idx);
686
+ for ( k = 0; k < ca->ndim; k++ ) {
687
+ off += idx[k] * esteps[k];
688
+ }
689
+ paddrs[i] = off;
690
+ }
691
+ }
664
692
  ca_xfer_addrs(root, n, paddrs, data, dir);
693
+ ALLOCV_END(holder);
694
+ return;
665
695
  }
666
- else {
667
- /* byte-mismatched reinterpret: deliver cell by cell via xfer_index
668
- (still no whole-view attach -- delegates one cell at a time). */
669
- for ( i = 0; i < n; i++ ) {
670
- ca_size_t idx[CA_RANK_MAX];
671
- ca_addr2index((CArray *) ca, addrs[i], idx);
672
- ca_stride_func_xfer_index(ca, idx, d + i * ca->bytes, dir);
673
- }
696
+
697
+ /* Byte-mismatched reinterpret: deliver cell by cell via xfer_index
698
+ (still no whole-view attach -- delegates one cell at a time). */
699
+ for ( i = 0; i < n; i++ ) {
700
+ ca_size_t idx[CA_RANK_MAX];
701
+ ca_addr2index((CArray *) ca, addrs[i], idx);
702
+ ca_stride_func_xfer_index(ca, idx, d + i * ca->bytes, dir);
674
703
  }
675
- ALLOCV_END(holder);
676
704
  }
677
705
 
678
706
  /* Optimised region delivery. Reached when ca->ptr == NULL (the central dispatcher handles the alias / attached case).
@@ -833,6 +861,7 @@ ca_stride_func_xfer_stride (void *ap, ca_size_t *starts, ca_size_t *counts,
833
861
  if (!aligned || !root->ptr) {
834
862
  ca_size_t idx[CA_RANK_MAX], doff = 0;
835
863
  for (k = 0; k < ndim; k++) idx[k] = 0;
864
+ /* empty: the slot returns on an empty region at its entry */
836
865
  while (1) {
837
866
  ca_size_t off = base_addr, vmidx[CA_RANK_MAX];
838
867
  for (k = 0; k < ndim; k++) off += idx[k] * strides[k];
@@ -1085,8 +1114,9 @@ static int ca_layout_is_contiguous (int8_t ndim, const ca_size_t *dim,
1085
1114
  composes a source down to its root.
1086
1115
 
1087
1116
  Entities hold their own buffer. A CAStride-family root is one the fold
1088
- declined to walk through (a byte reinterpret, say); its own attach folds
1089
- onward as it always has, so leave that chain alone. What is left is the
1117
+ declined to walk through (a reshape over a transpose); its own attach
1118
+ folds onward from itself, so the answer is the one for the root it folds
1119
+ to. What is left is the
1090
1120
  boundary views that compute or fetch their contents -- CAObject, the lazy
1091
1121
  per-element transforms, whatever a companion gem installed -- and of those,
1092
1122
  only the ones with an xfer_stride slot can answer a region request at all.
@@ -1098,7 +1128,17 @@ ca_root_lends_no_memory (void *ap)
1098
1128
  CArray *root = (CArray *) ap;
1099
1129
 
1100
1130
  if ( ca_is_entity(root) ) return 0;
1101
- if ( ca_func[root->obj_type].attach == ca_stride_func.attach ) return 0;
1131
+ if ( ca_func[root->obj_type].attach == ca_stride_func.attach ) {
1132
+ /* The fold stopped at a CAStride (a reshape over a transpose). Its
1133
+ own attach folds onward from itself, so it lends memory exactly when
1134
+ the root it folds to does: over a computed root, attaching it
1135
+ produces all of it. */
1136
+ CArray *below;
1137
+ ca_size_t cs[CA_RANK_MAX];
1138
+ ca_size_t base;
1139
+ ca_stride_compose_to_root((CAStride *) root, &below, cs, &base);
1140
+ return ca_root_lends_no_memory(below);
1141
+ }
1102
1142
  return ca_func[root->obj_type].xfer_stride != NULL;
1103
1143
  }
1104
1144
 
@@ -1164,14 +1204,14 @@ ca_stride_func_allocate (void *ap)
1164
1204
 
1165
1205
  ca_stride_compose_to_root(ca, &root, composed_strides, &composed_base);
1166
1206
  if (ca_root_lends_no_memory(root)) {
1167
- ca->ptr = xmalloc(ca_length(ca));
1207
+ ca->ptr = ca_alloc_cells((CArray *) ca);
1168
1208
  return;
1169
1209
  }
1170
1210
  ca_attach(root);
1171
1211
  if (ca_layout_is_contiguous(ca->ndim, ca->dim, composed_strides, ca->bytes)) {
1172
1212
  ca->ptr = root->ptr + composed_base;
1173
1213
  } else {
1174
- ca->ptr = xmalloc(ca_length(ca));
1214
+ ca->ptr = ca_alloc_cells((CArray *) ca);
1175
1215
  }
1176
1216
  }
1177
1217
 
@@ -1200,14 +1240,9 @@ ca_stride_func_attach (void *ap)
1200
1240
  transfer slots. */
1201
1241
  char *buf = xmalloc(ca_length(ca));
1202
1242
  void *args[2];
1203
- int tag = 0;
1204
1243
  args[0] = ca;
1205
1244
  args[1] = buf;
1206
- rb_protect(ca_stride_cold_fill, (VALUE) args, &tag); /* root stays cold */
1207
- if (tag) {
1208
- xfree(buf);
1209
- rb_jump_tag(tag);
1210
- }
1245
+ ca_fill_or_xfree(buf, ca_stride_cold_fill, (VALUE) args); /* root stays cold */
1211
1246
  ca->ptr = buf;
1212
1247
  return;
1213
1248
  }
@@ -1215,7 +1250,7 @@ ca_stride_func_attach (void *ap)
1215
1250
  if (ca_layout_is_contiguous(ca->ndim, ca->dim, composed_strides, ca->bytes)) {
1216
1251
  ca->ptr = root->ptr + composed_base;
1217
1252
  } else {
1218
- ca->ptr = xmalloc(ca_length(ca));
1253
+ ca->ptr = ca_alloc_cells((CArray *) ca);
1219
1254
  ca_stride_xfer_with_layout(ca, 0, root->ptr + composed_base, composed_strides);
1220
1255
  }
1221
1256
  }
@@ -1342,6 +1377,8 @@ ca_stride_func_xfer_all (void *ap, void *data, int dir)
1342
1377
  char *ptr0 = ca->ptr;
1343
1378
  char *ptr = (char *) data;
1344
1379
 
1380
+ if ( ca->elements == 0 ) return;
1381
+
1345
1382
  ca_stride_compose_to_root(ca, &root, composed_strides, &composed_base);
1346
1383
 
1347
1384
  /* Partial materialise: when the fold stops at a cold boundary view that knows how to deliver a region (has an
@@ -1458,6 +1495,7 @@ ca_stride_func_xfer_all (void *ap, void *data, int dir)
1458
1495
  ca_size_t idx[CA_RANK_MAX], n = 0;
1459
1496
  int8_t k;
1460
1497
  for (k = 0; k < ca->ndim; k++) idx[k] = 0;
1498
+ /* empty: the slot returns on an empty view at its entry */
1461
1499
  while (1) {
1462
1500
  ca_size_t off = composed_base;
1463
1501
  for (k = 0; k < ca->ndim; k++) off += idx[k] * composed_strides[k];
@@ -1471,6 +1509,52 @@ ca_stride_func_xfer_all (void *ap, void *data, int dir)
1471
1509
  return;
1472
1510
  }
1473
1511
 
1512
+ /* A read through a byte reinterpret (CAField, a refer to another width)
1513
+ over a cold root: the root's addresses are not this view's cells, but
1514
+ each cell lies in a run of whole root elements. Ask the root for those
1515
+ elements and cut the cell's bytes out of them. A write keeps the
1516
+ whole-root round trip below: two cells can share a root element, and
1517
+ writing back each one's copy of it would drop the other's bytes. */
1518
+ if ( dir == CA_XFER_GET && ca->elements > 0 && ca->bytes != root->bytes
1519
+ && ca_func[root->obj_type].xfer_stride ) {
1520
+ volatile VALUE ah = 0, sh = 0;
1521
+ ca_size_t rb = root->bytes, cb = ca->bytes;
1522
+ ca_size_t per = (cb + rb - 1) / rb + 1;
1523
+ ca_size_t *addrs = ALLOCV_N(ca_size_t, ah, ca->elements * per);
1524
+ char *scratch;
1525
+ ca_size_t idx[CA_RANK_MAX], n = 0, i;
1526
+ ca_size_t *first = ALLOCV_N(ca_size_t, sh, ca->elements);
1527
+ int8_t k;
1528
+ int ok = 1;
1529
+ for (k = 0; k < ca->ndim; k++) idx[k] = 0;
1530
+ for (i = 0; ok; i++) {
1531
+ ca_size_t off = composed_base, e, e_end;
1532
+ for (k = 0; k < ca->ndim; k++) off += idx[k] * composed_strides[k];
1533
+ if ( off < 0 || off + cb > root->elements * rb ) { ok = 0; break; }
1534
+ first[i] = n * rb + off % rb;
1535
+ for (e = off / rb, e_end = (off + cb - 1) / rb; e <= e_end; e++) {
1536
+ addrs[n++] = e;
1537
+ }
1538
+ k = ca->ndim - 1;
1539
+ while (k >= 0) { if (++idx[k] < ca->dim[k]) break; idx[k] = 0; k--; }
1540
+ if (k < 0) break;
1541
+ }
1542
+ if ( ok && n < root->elements ) {
1543
+ volatile VALUE dh = 0;
1544
+ scratch = ALLOCV_N(char, dh, n * rb);
1545
+ ca_xfer_addrs(root, n, addrs, scratch, CA_XFER_GET);
1546
+ for (i = 0; i < ca->elements; i++) {
1547
+ memcpy(ptr + i * cb, scratch + first[i], cb);
1548
+ }
1549
+ ALLOCV_END(dh);
1550
+ ALLOCV_END(sh);
1551
+ ALLOCV_END(ah);
1552
+ return;
1553
+ }
1554
+ ALLOCV_END(sh);
1555
+ ALLOCV_END(ah);
1556
+ }
1557
+
1474
1558
  /* Cold root without an xfer_stride slot, or an ndim mismatch the bridges
1475
1559
  above could not express: materialise the root into scratch via
1476
1560
  ca_xfer_all and run the direct strided gather/scatter against that.
@@ -1606,19 +1690,19 @@ ca_stride_func_fill_data (void *ap, void *ptr)
1606
1690
  }
1607
1691
  case 2: {
1608
1692
  int16_t v; memcpy(&v, ptr, 2);
1609
- int16_t *d = (int16_t *) dst;
1693
+ ca_u_int16_t *d = (ca_u_int16_t *) dst;
1610
1694
  for (i = 0; i < inner_count; i++) d[i] = v;
1611
1695
  break;
1612
1696
  }
1613
1697
  case 4: {
1614
1698
  int32_t v; memcpy(&v, ptr, 4);
1615
- int32_t *d = (int32_t *) dst;
1699
+ ca_u_int32_t *d = (ca_u_int32_t *) dst;
1616
1700
  for (i = 0; i < inner_count; i++) d[i] = v;
1617
1701
  break;
1618
1702
  }
1619
1703
  case 8: {
1620
1704
  int64_t v; memcpy(&v, ptr, 8);
1621
- int64_t *d = (int64_t *) dst;
1705
+ ca_u_int64_t *d = (ca_u_int64_t *) dst;
1622
1706
  for (i = 0; i < inner_count; i++) d[i] = v;
1623
1707
  break;
1624
1708
  }
@@ -2078,16 +2162,17 @@ rb_ca_as_strided (int argc, VALUE *argv, VALUE self)
2078
2162
  "shape (%ld) and strides (%ld) length mismatch",
2079
2163
  len, RARRAY_LEN(rstrides));
2080
2164
  }
2081
- if (len <= 0 || len > CA_RANK_MAX) {
2165
+ if (len <= 0) {
2082
2166
  rb_raise(rb_eArgError, "invalid ndim %ld", len);
2083
2167
  }
2168
+ ca_check_rank_fits(len, NULL);
2084
2169
  ndim = (int8_t) len;
2085
2170
  for (i = 0; i < len; i++) {
2086
- shape[i] = NUM2SIZE(RARRAY_AREF(rshape, i));
2087
- strides[i] = NUM2SIZE(RARRAY_AREF(rstrides, i));
2171
+ shape[i] = ca_integer_arg(RARRAY_AREF(rshape, i), "a length", NULL);
2172
+ strides[i] = ca_integer_arg(RARRAY_AREF(rstrides, i), "a stride", NULL);
2088
2173
  }
2089
2174
  if (! NIL_P(roffset)) {
2090
- base_offset = NUM2SIZE(roffset);
2175
+ base_offset = ca_integer_arg(roffset, "offset", NULL);
2091
2176
  }
2092
2177
 
2093
2178
  VALUE obj = rb_ca_stride_new(self, parent->data_type, parent->bytes,
@@ -2118,11 +2203,7 @@ rb_ca_sliding_windows (int argc, VALUE *argv, VALUE self)
2118
2203
  TypedData_Get_Struct(self, CArray, &carray_data_type, parent);
2119
2204
 
2120
2205
  ndim = parent->ndim;
2121
- if (2 * (int) ndim > CA_RANK_MAX) {
2122
- rb_raise(rb_eArgError,
2123
- "sliding_windows: result rank %d exceeds CA_RANK_MAX (%d)",
2124
- 2 * (int) ndim, CA_RANK_MAX);
2125
- }
2206
+ ca_check_rank_fits(2 * (long) ndim, "sliding_windows");
2126
2207
 
2127
2208
  rb_scan_args(argc, argv, "*:", (VALUE *) &rposary, (VALUE *) &ropt);
2128
2209
  if (! NIL_P(ropt)) {
@@ -2138,12 +2219,12 @@ rb_ca_sliding_windows (int argc, VALUE *argv, VALUE self)
2138
2219
  RARRAY_LEN(wary), (int) ndim);
2139
2220
  }
2140
2221
  for (i = 0; i < ndim; i++) {
2141
- window[i] = NUM2SIZE(RARRAY_AREF(wary, i));
2222
+ window[i] = ca_integer_arg(RARRAY_AREF(wary, i), "a window size", NULL);
2142
2223
  }
2143
2224
  }
2144
2225
  else if (nargs == ndim) {
2145
2226
  for (i = 0; i < ndim; i++) {
2146
- window[i] = NUM2SIZE(RARRAY_AREF(rposary, i));
2227
+ window[i] = ca_integer_arg(RARRAY_AREF(rposary, i), "a window size", NULL);
2147
2228
  }
2148
2229
  }
2149
2230
  else {
@@ -2162,11 +2243,11 @@ rb_ca_sliding_windows (int argc, VALUE *argv, VALUE self)
2162
2243
  RARRAY_LEN(rstep), (int) ndim);
2163
2244
  }
2164
2245
  for (i = 0; i < ndim; i++) {
2165
- step[i] = NUM2SIZE(RARRAY_AREF(rstep, i));
2246
+ step[i] = ca_integer_arg(RARRAY_AREF(rstep, i), "a step", NULL);
2166
2247
  }
2167
2248
  }
2168
2249
  else {
2169
- ca_size_t s = NUM2SIZE(rstep);
2250
+ ca_size_t s = ca_integer_arg(rstep, "step", NULL);
2170
2251
  for (i = 0; i < ndim; i++) step[i] = s;
2171
2252
  }
2172
2253
 
@@ -2249,13 +2330,13 @@ rb_ca_unfold (int argc, VALUE *argv, VALUE self)
2249
2330
  }
2250
2331
  nspatial = (int8_t) RARRAY_LEN(wary);
2251
2332
  for (i = 0; i < nspatial; i++) {
2252
- window[i] = NUM2SIZE(RARRAY_AREF(wary, i));
2333
+ window[i] = ca_integer_arg(RARRAY_AREF(wary, i), "a window size", NULL);
2253
2334
  }
2254
2335
  }
2255
2336
  else if (nargs >= 1 && nargs <= ndim) {
2256
2337
  nspatial = (int8_t) nargs;
2257
2338
  for (i = 0; i < nspatial; i++) {
2258
- window[i] = NUM2SIZE(RARRAY_AREF(rposary, i));
2339
+ window[i] = ca_integer_arg(RARRAY_AREF(rposary, i), "a window size", NULL);
2259
2340
  }
2260
2341
  }
2261
2342
  else {
@@ -2266,11 +2347,7 @@ rb_ca_unfold (int argc, VALUE *argv, VALUE self)
2266
2347
 
2267
2348
  ntrail = ndim - nspatial;
2268
2349
  outrank = (int) ndim + (int) nspatial;
2269
- if (outrank > CA_RANK_MAX) {
2270
- rb_raise(rb_eArgError,
2271
- "unfold: result rank %d exceeds CA_RANK_MAX (%d)",
2272
- outrank, CA_RANK_MAX);
2273
- }
2350
+ ca_check_rank_fits(outrank, "unfold");
2274
2351
 
2275
2352
  if (NIL_P(rstep)) {
2276
2353
  for (i = 0; i < nspatial; i++) step[i] = 1;
@@ -2282,11 +2359,11 @@ rb_ca_unfold (int argc, VALUE *argv, VALUE self)
2282
2359
  RARRAY_LEN(rstep), (int) nspatial);
2283
2360
  }
2284
2361
  for (i = 0; i < nspatial; i++) {
2285
- step[i] = NUM2SIZE(RARRAY_AREF(rstep, i));
2362
+ step[i] = ca_integer_arg(RARRAY_AREF(rstep, i), "a step", NULL);
2286
2363
  }
2287
2364
  }
2288
2365
  else {
2289
- ca_size_t s = NUM2SIZE(rstep);
2366
+ ca_size_t s = ca_integer_arg(rstep, "step", NULL);
2290
2367
  for (i = 0; i < nspatial; i++) step[i] = s;
2291
2368
  }
2292
2369
 
@@ -2354,11 +2431,7 @@ rb_ca_block_view (int argc, VALUE *argv, VALUE self)
2354
2431
  TypedData_Get_Struct(self, CArray, &carray_data_type, parent);
2355
2432
 
2356
2433
  ndim = parent->ndim;
2357
- if (2 * (int) ndim > CA_RANK_MAX) {
2358
- rb_raise(rb_eArgError,
2359
- "block_view: result rank %d exceeds CA_RANK_MAX (%d)",
2360
- 2 * (int) ndim, CA_RANK_MAX);
2361
- }
2434
+ ca_check_rank_fits(2 * (long) ndim, "block_view");
2362
2435
 
2363
2436
  rb_scan_args(argc, argv, "*:", (VALUE *) &rposary, (VALUE *) &ropt);
2364
2437
  rb_reject_options(ropt);
@@ -2372,12 +2445,12 @@ rb_ca_block_view (int argc, VALUE *argv, VALUE self)
2372
2445
  RARRAY_LEN(bary), (int) ndim);
2373
2446
  }
2374
2447
  for (i = 0; i < ndim; i++) {
2375
- block[i] = NUM2SIZE(RARRAY_AREF(bary, i));
2448
+ block[i] = ca_integer_arg(RARRAY_AREF(bary, i), "a block size", NULL);
2376
2449
  }
2377
2450
  }
2378
2451
  else if (nargs == ndim) {
2379
2452
  for (i = 0; i < ndim; i++) {
2380
- block[i] = NUM2SIZE(RARRAY_AREF(rposary, i));
2453
+ block[i] = ca_integer_arg(RARRAY_AREF(rposary, i), "a block size", NULL);
2381
2454
  }
2382
2455
  }
2383
2456
  else {
@@ -2461,7 +2534,7 @@ rb_ca_dim_view (int argc, VALUE *argv, VALUE self)
2461
2534
  (int) n_iter, (int) ndim);
2462
2535
  }
2463
2536
  for (i = 0; i < n_iter; i++) {
2464
- iter_axes[i] = ca_axis_integer(RARRAY_AREF(aary, i), NULL);
2537
+ iter_axes[i] = ca_normalize_axis_arg(RARRAY_AREF(aary, i), ndim, NULL);
2465
2538
  }
2466
2539
  }
2467
2540
  else if (nargs >= 1) {
@@ -2472,7 +2545,7 @@ rb_ca_dim_view (int argc, VALUE *argv, VALUE self)
2472
2545
  }
2473
2546
  n_iter = (int8_t) nargs;
2474
2547
  for (i = 0; i < n_iter; i++) {
2475
- iter_axes[i] = ca_axis_integer(RARRAY_AREF(rposary, i), NULL);
2548
+ iter_axes[i] = ca_normalize_axis_arg(RARRAY_AREF(rposary, i), ndim, NULL);
2476
2549
  }
2477
2550
  }
2478
2551
  else {
@@ -2480,23 +2553,16 @@ rb_ca_dim_view (int argc, VALUE *argv, VALUE self)
2480
2553
  "dim_view: at least one iteration axis required");
2481
2554
  }
2482
2555
 
2483
- /* Normalize negative indices and validate range / distinctness. */
2556
+ /* The axes are normalized as they are read; check distinctness. */
2484
2557
  for (i = 0; i < ndim; i++) seen[i] = 0;
2485
2558
  for (i = 0; i < n_iter; i++) {
2486
2559
  ca_size_t a = iter_axes[i];
2487
- if (a < 0) a += ndim;
2488
- if (a < 0 || a >= ndim) {
2489
- rb_raise(rb_eArgError,
2490
- "dim_view: axis %lld out of range for ndim %d",
2491
- (long long) iter_axes[i], (int) ndim);
2492
- }
2493
2560
  if (seen[a]) {
2494
2561
  rb_raise(rb_eArgError,
2495
2562
  "dim_view: duplicate iteration axis %lld",
2496
2563
  (long long) a);
2497
2564
  }
2498
2565
  seen[a] = 1;
2499
- iter_axes[i] = a;
2500
2566
  }
2501
2567
 
2502
2568
  /* imap: iter axes first (in given order), then remaining axes
@@ -2509,7 +2575,12 @@ rb_ca_dim_view (int argc, VALUE *argv, VALUE self)
2509
2575
  if (! seen[i]) imap[k++] = i;
2510
2576
  }
2511
2577
 
2512
- return rb_ca_trans_new(self, imap);
2578
+ {
2579
+ /* A reordering of the axes, as transpose is: a Face stays its Face. */
2580
+ volatile VALUE obj = rb_ca_trans_new(self, imap);
2581
+ CA_WRAPPER_LIFT(obj, self, parent);
2582
+ return obj;
2583
+ }
2513
2584
  }
2514
2585
 
2515
2586
  /* flip(*axes) -- reverses the listed axes by negating their strides; with no
@@ -2568,12 +2639,8 @@ rb_ca_flip_axis (VALUE self, long axis)
2568
2639
  rb_check_carray_object(self);
2569
2640
  TypedData_Get_Struct(self, CArray, &carray_data_type, parent);
2570
2641
  int8_t ndim = parent->ndim;
2571
- long a = (axis < 0) ? (axis + ndim) : axis;
2572
- if (a < 0 || a >= ndim) {
2573
- rb_raise(rb_eArgError,
2574
- "flip_axis: axis %ld out of range for ndim %d",
2575
- axis, (int) ndim);
2576
- }
2642
+ long a = rb_ca_normalize_axis_for_ndim(axis, ndim, /* axis-check: a long from a C caller */
2643
+ "flip_axis");
2577
2644
  int8_t flip[CA_RANK_MAX] = {0};
2578
2645
  flip[a] = 1;
2579
2646
  return rb_ca_flip_build_view(self, parent, flip);
@@ -2615,7 +2682,7 @@ rb_ca_flip (int argc, VALUE *argv, VALUE self)
2615
2682
  (int) n_axes, (int) ndim);
2616
2683
  }
2617
2684
  for (i = 0; i < n_axes; i++) {
2618
- axes[i] = ca_axis_integer(RARRAY_AREF(aary, i), NULL);
2685
+ axes[i] = ca_normalize_axis_arg(RARRAY_AREF(aary, i), ndim, NULL);
2619
2686
  }
2620
2687
  }
2621
2688
  else {
@@ -2626,18 +2693,12 @@ rb_ca_flip (int argc, VALUE *argv, VALUE self)
2626
2693
  }
2627
2694
  n_axes = (int8_t) nargs;
2628
2695
  for (i = 0; i < n_axes; i++) {
2629
- axes[i] = ca_axis_integer(RARRAY_AREF(rposary, i), NULL);
2696
+ axes[i] = ca_normalize_axis_arg(RARRAY_AREF(rposary, i), ndim, NULL);
2630
2697
  }
2631
2698
  }
2632
2699
 
2633
2700
  for (i = 0; i < n_axes; i++) {
2634
2701
  ca_size_t a = axes[i];
2635
- if (a < 0) a += ndim;
2636
- if (a < 0 || a >= ndim) {
2637
- rb_raise(rb_eArgError,
2638
- "flip: axis %lld out of range for ndim %d",
2639
- (long long) axes[i], (int) ndim);
2640
- }
2641
2702
  if (flip[a]) {
2642
2703
  rb_raise(rb_eArgError,
2643
2704
  "flip: duplicate axis %lld",
@@ -2688,14 +2749,14 @@ rb_ca_diagonal (int argc, VALUE *argv, VALUE self)
2688
2749
 
2689
2750
  /* offset from positional or keyword (not both) */
2690
2751
  if (nargs == 0) {
2691
- if (! NIL_P(roffset)) offset = NUM2SIZE(roffset);
2752
+ if (! NIL_P(roffset)) offset = ca_integer_arg(roffset, "offset", NULL);
2692
2753
  }
2693
2754
  else if (nargs == 1) {
2694
2755
  if (! NIL_P(roffset)) {
2695
2756
  rb_raise(rb_eArgError,
2696
2757
  "diagonal: give offset positionally OR as keyword, not both");
2697
2758
  }
2698
- offset = NUM2SIZE(RARRAY_AREF(rposary, 0));
2759
+ offset = ca_integer_arg(RARRAY_AREF(rposary, 0), "offset", NULL);
2699
2760
  }
2700
2761
  else {
2701
2762
  rb_raise(rb_eArgError,
@@ -2711,18 +2772,8 @@ rb_ca_diagonal (int argc, VALUE *argv, VALUE self)
2711
2772
  rb_raise(rb_eArgError,
2712
2773
  "diagonal: axis: must be an Array of 2 integers");
2713
2774
  }
2714
- ai = ca_axis_integer(RARRAY_AREF(raxis, 0), NULL);
2715
- aj = ca_axis_integer(RARRAY_AREF(raxis, 1), NULL);
2716
- if (ai < 0) ai += ndim;
2717
- if (aj < 0) aj += ndim;
2718
- if (ai < 0 || ai >= ndim) {
2719
- rb_raise(rb_eArgError, "diagonal: axis[0] out of range for ndim %d",
2720
- (int) ndim);
2721
- }
2722
- if (aj < 0 || aj >= ndim) {
2723
- rb_raise(rb_eArgError, "diagonal: axis[1] out of range for ndim %d",
2724
- (int) ndim);
2725
- }
2775
+ ai = ca_normalize_axis_arg(RARRAY_AREF(raxis, 0), ndim, NULL);
2776
+ aj = ca_normalize_axis_arg(RARRAY_AREF(raxis, 1), ndim, NULL);
2726
2777
  if (ai == aj) {
2727
2778
  rb_raise(rb_eArgError, "diagonal: axis[0] and axis[1] must be distinct");
2728
2779
  }
@@ -2750,7 +2801,9 @@ rb_ca_diagonal (int argc, VALUE *argv, VALUE self)
2750
2801
  base_offset = offset * parent_byte_stride[aj];
2751
2802
  }
2752
2803
  else {
2753
- ca_size_t neg = -offset;
2804
+ /* compare before negating: -offset overflows for the most negative
2805
+ offset */
2806
+ ca_size_t neg = ( offset <= -parent->dim[ai] ) ? parent->dim[ai] : -offset;
2754
2807
  if (neg >= parent->dim[ai]) {
2755
2808
  diag_len = 0;
2756
2809
  }
@@ -2761,6 +2814,9 @@ rb_ca_diagonal (int argc, VALUE *argv, VALUE self)
2761
2814
  }
2762
2815
  base_offset = neg * parent_byte_stride[ai];
2763
2816
  }
2817
+ if (diag_len == 0) {
2818
+ base_offset = 0; /* an offset past the edge can overflow the product */
2819
+ }
2764
2820
 
2765
2821
  /* build output: kept axes (in original order), then diagonal axis */
2766
2822
  out_k = 0;