carray 3.0.0 → 3.0.1

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 (180) hide show
  1. checksums.yaml +4 -4
  2. data/.yardopts +2 -1
  3. data/CHANGELOG.md +325 -3
  4. data/{NEWS.md → CHANGELOG.v1.md} +1 -1
  5. data/README.md +32 -32
  6. data/carray.gemspec +12 -8
  7. data/ext/ca_axis_group.c +55 -19
  8. data/ext/ca_binop_dispatch.c +5 -6
  9. data/ext/ca_binop_dispatch.h +0 -7
  10. data/ext/ca_categorical_iterator.c +13 -13
  11. data/ext/ca_for_each_element.h +6 -8
  12. data/ext/ca_group_iter.c +1 -1
  13. data/ext/ca_kernel_iterator.c +69 -23
  14. data/ext/ca_kernel_iterator.h +3 -10
  15. data/ext/ca_obj_bincmp.c +57 -27
  16. data/ext/ca_obj_binop.c +86 -30
  17. data/ext/ca_obj_const_string.c +3 -1
  18. data/ext/ca_obj_face.c +80 -0
  19. data/ext/ca_obj_face.h +33 -1
  20. data/ext/ca_obj_fake.c +11 -0
  21. data/ext/ca_obj_fixlen_string.c +3 -1
  22. data/ext/ca_obj_grid.c +11 -3
  23. data/ext/ca_obj_meld.c +13 -8
  24. data/ext/ca_obj_moncmp.c +33 -14
  25. data/ext/ca_obj_monop.c +19 -7
  26. data/ext/ca_obj_object.c +99 -4
  27. data/ext/ca_obj_record.c +3 -1
  28. data/ext/ca_obj_refer.c +30 -6
  29. data/ext/ca_obj_roll.c +13 -5
  30. data/ext/ca_obj_select_axis.c +16 -6
  31. data/ext/ca_obj_shift.c +3 -3
  32. data/ext/ca_obj_stride.c +106 -23
  33. data/ext/ca_obj_string.c +3 -1
  34. data/ext/ca_obj_tile.c +13 -5
  35. data/ext/ca_obj_time.c +3 -1
  36. data/ext/ca_obj_timedelta.c +3 -1
  37. data/ext/ca_obj_transpose.c +1 -1
  38. data/ext/ca_obj_triop.c +62 -23
  39. data/ext/ca_obj_window.c +86 -17
  40. data/ext/ca_op_cmplx64.h +123 -0
  41. data/ext/ca_op_ipower.c +0 -3
  42. data/ext/ca_sort_kernels.h +5 -5
  43. data/ext/ca_sweep_engine.c +78 -35
  44. data/ext/ca_sweep_engine.h +13 -4
  45. data/ext/ca_transform_common.c +7 -0
  46. data/ext/carray.h +127 -130
  47. data/ext/carray_access.c +56 -34
  48. data/ext/carray_bincount.c +8 -8
  49. data/ext/carray_broadcast.c +99 -6
  50. data/ext/carray_build_flags.h +3 -0
  51. data/ext/carray_call_cfunc.c +1491 -0
  52. data/ext/carray_call_cfunc.h +153 -0
  53. data/ext/carray_cast.c +69 -31
  54. data/ext/carray_conversion.c +22 -22
  55. data/ext/carray_copy.c +12 -2
  56. data/ext/carray_core.c +57 -10
  57. data/ext/carray_count.c +1 -1
  58. data/ext/carray_element.c +7 -7
  59. data/ext/carray_factorize.c +28 -28
  60. data/ext/carray_hold.c +1 -1
  61. data/ext/carray_index_classifier.c +7 -21
  62. data/ext/carray_internal.h +19 -3
  63. data/ext/carray_kernels_bincmp.c +1 -0
  64. data/ext/carray_kernels_binop.c +320 -298
  65. data/ext/carray_kernels_init.c +1095 -0
  66. data/ext/carray_kernels_map.c +1 -0
  67. data/ext/carray_kernels_moncmp.c +1 -0
  68. data/ext/carray_kernels_monop.c +412 -411
  69. data/ext/carray_kernels_reduce_aggregate.c +88 -87
  70. data/ext/carray_kernels_reduce_boolean.c +1 -0
  71. data/ext/carray_kernels_reduce_cumulative.c +54 -53
  72. data/ext/carray_kernels_reduce_extreme.c +51 -50
  73. data/ext/carray_kernels_reduce_variance.c +1 -0
  74. data/ext/carray_kernels_scan.c +1 -0
  75. data/ext/carray_kernels_search.c +93 -92
  76. data/ext/carray_kernels_sort.c +1 -0
  77. data/ext/carray_kernels_triop.c +1 -0
  78. data/ext/carray_lazy.c +175 -5
  79. data/ext/carray_mask.c +6 -5
  80. data/ext/carray_median_percentile.c +1 -1
  81. data/ext/carray_memory_view.c +46 -21
  82. data/ext/carray_operator.c +46 -49
  83. data/ext/carray_partition.c +4 -4
  84. data/ext/carray_random.c +7 -7
  85. data/ext/carray_scatter.c +1 -1
  86. data/ext/carray_sort.c +3 -3
  87. data/ext/carray_sort_kernel.c +10 -10
  88. data/ext/carray_test.c +1 -1
  89. data/ext/extconf.rb +18 -0
  90. data/ext/mk_call_cfunc.rb +243 -52
  91. data/ext/mkkernel.rb +354 -100
  92. data/ext/ruby_carray.c +18 -16
  93. data/ext/version.h +4 -4
  94. data/lib/carray/autoload_carray.rb +19 -0
  95. data/lib/carray/autoload_method_extension.rb +2 -1
  96. data/lib/carray/axis_group.rb +7 -7
  97. data/lib/carray/basics.rb +59 -59
  98. data/lib/carray/bincount_nd.rb +12 -12
  99. data/lib/carray/block_iterator.rb +24 -14
  100. data/lib/carray/categorical.rb +4 -4
  101. data/lib/carray/categorical_iterator.rb +98 -76
  102. data/lib/carray/conditional.rb +14 -14
  103. data/lib/carray/construct.rb +21 -0
  104. data/lib/carray/core_extensions.rb +62 -5
  105. data/lib/carray/data_type_extension.rb +18 -1
  106. data/lib/carray/frame/concat.rb +3 -3
  107. data/lib/carray/frame/convert.rb +1 -1
  108. data/lib/carray/frame/frame.rb +4 -11
  109. data/lib/carray/frame/io.rb +159 -2
  110. data/lib/carray/frame/sort.rb +1 -1
  111. data/lib/carray/frame/verbs.rb +18 -1
  112. data/lib/carray/fuse_source.rb +123 -0
  113. data/lib/carray/fusion.rb +218 -0
  114. data/lib/carray/histogram.rb +16 -16
  115. data/lib/carray/inspect.rb +1 -7
  116. data/lib/carray/iterator.rb +4 -3
  117. data/lib/carray/lazy.rb +125 -73
  118. data/lib/carray/meld_reduce.rb +2 -2
  119. data/lib/carray/methods/align_addr.rb +1 -1
  120. data/lib/carray/methods/composition.rb +1 -1
  121. data/lib/carray/methods/is_in.rb +12 -12
  122. data/lib/carray/methods/locate_addr.rb +6 -1
  123. data/lib/carray/methods/mask_duplicates.rb +1 -1
  124. data/lib/carray/methods/meshgrid.rb +4 -5
  125. data/lib/carray/methods/mode.rb +2 -2
  126. data/lib/carray/methods/nunique.rb +1 -1
  127. data/lib/carray/methods/snap.rb +7 -2
  128. data/lib/carray/methods/unique.rb +3 -3
  129. data/lib/carray/methods/value_counts.rb +2 -2
  130. data/lib/carray/runtime.rb +0 -19
  131. data/lib/carray/slab_iterator.rb +20 -7
  132. data/lib/carray/string_operation_extension.rb +5 -5
  133. data/lib/carray/time.rb +890 -491
  134. data/lib/carray/window_iterator.rb +285 -13
  135. data/lib/carray.rb +5 -5
  136. data/yard-stubs/ca_obj_array.rb +385 -0
  137. data/yard-stubs/ca_obj_bitarray.rb +38 -0
  138. data/yard-stubs/ca_obj_bitfield.rb +43 -0
  139. data/yard-stubs/ca_obj_block.rb +73 -0
  140. data/yard-stubs/ca_obj_byte_swap.rb +56 -0
  141. data/yard-stubs/ca_obj_fake.rb +31 -0
  142. data/yard-stubs/ca_obj_farray.rb +32 -0
  143. data/yard-stubs/ca_obj_field.rb +45 -0
  144. data/yard-stubs/ca_obj_grid.rb +35 -0
  145. data/yard-stubs/ca_obj_refer.rb +72 -0
  146. data/yard-stubs/ca_obj_roll.rb +45 -0
  147. data/yard-stubs/ca_obj_shift.rb +43 -0
  148. data/yard-stubs/ca_obj_stride.rb +181 -0
  149. data/yard-stubs/ca_obj_tile.rb +29 -0
  150. data/yard-stubs/ca_obj_transpose.rb +40 -0
  151. data/yard-stubs/ca_obj_window.rb +49 -0
  152. data/yard-stubs/carray_access.rb +131 -0
  153. data/yard-stubs/carray_attribute.rb +246 -0
  154. data/yard-stubs/carray_broadcast.rb +37 -0
  155. data/yard-stubs/carray_cast.rb +489 -0
  156. data/yard-stubs/carray_class.rb +65 -0
  157. data/yard-stubs/carray_conversion.rb +76 -0
  158. data/yard-stubs/carray_copy.rb +79 -0
  159. data/yard-stubs/carray_core.rb +114 -0
  160. data/yard-stubs/carray_count.rb +79 -0
  161. data/yard-stubs/carray_element.rb +108 -0
  162. data/yard-stubs/carray_generate.rb +66 -0
  163. data/yard-stubs/carray_lazy.rb +23 -0
  164. data/yard-stubs/carray_loop.rb +140 -0
  165. data/yard-stubs/carray_mask.rb +259 -0
  166. data/yard-stubs/carray_math.rb +132 -0
  167. data/yard-stubs/carray_mathfunc.rb +45 -0
  168. data/yard-stubs/carray_median_percentile.rb +89 -0
  169. data/yard-stubs/carray_memory_view.rb +163 -0
  170. data/yard-stubs/carray_order.rb +312 -0
  171. data/yard-stubs/carray_random.rb +89 -0
  172. data/yard-stubs/carray_scatter.rb +106 -0
  173. data/yard-stubs/carray_slab.rb +57 -0
  174. data/yard-stubs/carray_sort.rb +163 -0
  175. data/yard-stubs/carray_test.rb +85 -0
  176. data/yard-stubs/carray_undef.rb +64 -0
  177. data/yard-stubs/carray_utils.rb +97 -0
  178. data/yard-stubs/ruby_carray.rb +193 -0
  179. metadata +59 -10
  180. data/ext/ca_obj_unbound_repeat.c +0 -496
data/ext/ca_obj_string.c CHANGED
@@ -192,7 +192,9 @@ ca_operation_function_t ca_string_func = {
192
192
  ca_face_xfer_addrs,
193
193
  NULL, /* fold_stride: identity Face is not foldable */
194
194
  ca_face_xfer_stride,
195
- ca_face_xfer_all
195
+ ca_face_xfer_all,
196
+ .fill_addrs = ca_face_fill_addrs,
197
+ .fill_stride = ca_face_fill_stride,
196
198
  };
197
199
 
198
200
  /* ------------------------------------------------------------------- */
data/ext/ca_obj_tile.c CHANGED
@@ -214,10 +214,18 @@ ca_tile_func_xfer_stride (void *ap, ca_size_t *starts, ca_size_t *counts,
214
214
  s = ca->bytes;
215
215
  for (k = ndim - 1; k >= 0; k--) { dstride[k] = s; s *= counts[k]; }
216
216
 
217
- for (k = 0; k < ndim; k++) {
218
- if (strides[k] % tnative[k] != 0 || strides[k] / tnative[k] != 1) {
219
- structural = 0;
220
- break;
217
+ /* The request is over the view's addresses, so a transposed / flat request
218
+ is legal and must not be composed axis-by-axis; see
219
+ ca_xfer_stride_request_is_axis_box (carray.h). */
220
+ if ( ! ca_xfer_stride_request_is_axis_box(ca, starts, counts, strides) ) {
221
+ structural = 0;
222
+ }
223
+ else {
224
+ for (k = 0; k < ndim; k++) {
225
+ if (strides[k] % tnative[k] != 0 || strides[k] / tnative[k] != 1) {
226
+ structural = 0;
227
+ break;
228
+ }
221
229
  }
222
230
  }
223
231
 
@@ -546,7 +554,7 @@ rb_ca_tile (int argc, VALUE *argv, VALUE self)
546
554
 
547
555
  {
548
556
  VALUE obj = rb_ca_tile_new(self, reps);
549
- CA_FACE_LIFT_IF_FACE(obj, self, ca);
557
+ CA_WRAPPER_LIFT(obj, self, ca);
550
558
  return obj;
551
559
  }
552
560
  }
data/ext/ca_obj_time.c CHANGED
@@ -293,7 +293,9 @@ ca_operation_function_t ca_time_func = {
293
293
  ca_face_xfer_addrs,
294
294
  NULL, /* fold_stride: identity Face is not foldable */
295
295
  ca_face_xfer_stride,
296
- ca_face_xfer_all
296
+ ca_face_xfer_all,
297
+ .fill_addrs = ca_face_fill_addrs,
298
+ .fill_stride = ca_face_fill_stride,
297
299
  };
298
300
 
299
301
  /* ------------------------------------------------------------------- */
@@ -273,7 +273,9 @@ ca_operation_function_t ca_timedelta_func = {
273
273
  ca_face_xfer_addrs,
274
274
  NULL, /* fold_stride */
275
275
  ca_face_xfer_stride,
276
- ca_face_xfer_all
276
+ ca_face_xfer_all,
277
+ .fill_addrs = ca_face_fill_addrs,
278
+ .fill_stride = ca_face_fill_stride,
277
279
  };
278
280
 
279
281
  /* ------------------------------------------------------------------- */
@@ -117,7 +117,7 @@ rb_ca_trans (int argc, VALUE *argv, VALUE self)
117
117
  }
118
118
 
119
119
  obj = rb_ca_trans_new(self, imap);
120
- CA_FACE_LIFT_IF_FACE(obj, self, ca);
120
+ CA_WRAPPER_LIFT(obj, self, ca);
121
121
  return obj;
122
122
  }
123
123
 
data/ext/ca_obj_triop.c CHANGED
@@ -61,12 +61,19 @@ typedef struct CATriOp {
61
61
  CArray *parent; /* = op1 */
62
62
  uint32_t attach;
63
63
  uint8_t nosync;
64
+ /* ---- CAMultiParent conformance (CA_FLAG_MULTI_PARENTS): n_parents and
65
+ parents[] sit immediately after the CAView header, as carray.h's
66
+ layout convention requires, so ca_has_mask can fold over both
67
+ operands and build the mask on demand instead of at setup. ---- */
68
+ int32_t n_parents; /* always 3 */
69
+ CArray **parents; /* = &operands[0]; no separate allocation */
64
70
  /* CATriOp-specific tail */
65
71
  CArray *op2;
66
72
  CArray *op3;
67
73
  uint16_t op_id;
68
74
  uint8_t op2_is_scalar;
69
75
  uint8_t op3_is_scalar;
76
+ CArray *operands[3]; /* {op1, op2, op3}; what parents points at */
70
77
  } CATriOp;
71
78
 
72
79
  static size_t
@@ -117,7 +124,7 @@ ca_triop_setup (CATriOp *ca, CArray *op1, CArray *op2, CArray *op3,
117
124
 
118
125
  ca->obj_type = CA_OBJ_TRIOP;
119
126
  ca->data_type = out_dt;
120
- ca->flags = CA_FLAG_READ_ONLY;
127
+ ca->flags = CA_FLAG_READ_ONLY | CA_FLAG_MULTI_PARENTS;
121
128
  ca->ndim = op1->ndim;
122
129
  ca->bytes = out_bytes;
123
130
  ca->elements = op1->elements;
@@ -134,12 +141,17 @@ ca_triop_setup (CATriOp *ca, CArray *op1, CArray *op2, CArray *op3,
134
141
  ca->op_id = op_id;
135
142
  ca->op2_is_scalar = ( op2->elements == 1 && op1->elements > 1 ) ? 1 : 0;
136
143
  ca->op3_is_scalar = ( op3->elements == 1 && op1->elements > 1 ) ? 1 : 0;
144
+ ca->operands[0] = op1;
145
+ ca->operands[1] = op2;
146
+ ca->operands[2] = op3;
147
+ ca->parents = ca->operands;
148
+ ca->n_parents = 3;
137
149
 
138
150
  memcpy(ca->dim, op1->dim, op1->ndim * sizeof(ca_size_t));
139
151
 
140
- if ( ca_has_mask(op1) || ca_has_mask(op2) || ca_has_mask(op3) ) {
141
- ca_create_mask(ca);
142
- }
152
+ /* The mask is NOT built here. ca_has_mask folds over parents[] for a
153
+ multi-parent view and creates it on demand, so an expression whose mask
154
+ nobody reads never allocates one. */
143
155
 
144
156
  if ( ca_is_scalar(op1) && ca_is_scalar(op2) && ca_is_scalar(op3) ) {
145
157
  ca_set_flag(ca, CA_FLAG_SCALAR);
@@ -253,7 +265,9 @@ pull_operand (CArray *op, int is_scalar, ca_size_t *starts,
253
265
  one_counts[k] = 1;
254
266
  one_strides[k] = bytes;
255
267
  }
256
- scratch = ca_lazy_arena_acquire(bytes);
268
+ scratch = ( op->data_type == CA_OBJECT )
269
+ ? ca_lazy_arena_acquire_object(1)
270
+ : ca_lazy_arena_acquire(bytes);
257
271
  ca_triop_scratch_acquire_count++;
258
272
  ca_xfer_stride(op, one_starts, one_counts, one_strides, scratch,
259
273
  CA_XFER_GET);
@@ -266,7 +280,9 @@ pull_operand (CArray *op, int is_scalar, ca_size_t *starts,
266
280
  op_strides[k] = s;
267
281
  s *= counts[k];
268
282
  }
269
- scratch = ca_lazy_arena_acquire(slab_n * bytes);
283
+ scratch = ( op->data_type == CA_OBJECT )
284
+ ? ca_lazy_arena_acquire_object(slab_n)
285
+ : ca_lazy_arena_acquire(slab_n * bytes);
270
286
  ca_triop_scratch_acquire_count++;
271
287
  ca_xfer_stride(op, starts, counts, op_strides, scratch, CA_XFER_GET);
272
288
  *step_out = 1;
@@ -343,6 +359,14 @@ ca_triop_func_allocate (void *ap)
343
359
  {
344
360
  CATriOp *ca = (CATriOp *) ap;
345
361
  ca->ptr = xmalloc(ca_length(ca));
362
+ /* CA_OBJECT cells are VALUEs and this buffer is about to be marked as
363
+ soon as the view is, so it must not be handed to the GC as raw
364
+ xmalloc garbage. */
365
+ if ( ca->data_type == CA_OBJECT ) {
366
+ VALUE *p = (VALUE *) ca->ptr;
367
+ ca_size_t i;
368
+ for ( i = 0; i < ca->elements; i++ ) *p++ = Qnil;
369
+ }
346
370
  }
347
371
 
348
372
  static void
@@ -362,6 +386,14 @@ ca_triop_func_attach (void *ap)
362
386
  s = ca->bytes;
363
387
  for ( k = ca->ndim - 1; k >= 0; k-- ) { native[k] = s; s *= ca->dim[k]; }
364
388
  for ( k = 0; k < ca->ndim; k++ ) starts[k] = 0;
389
+ /* CA_OBJECT cells are VALUEs and this buffer is about to be marked as
390
+ soon as the view is, so it must not be handed to the GC as raw
391
+ xmalloc garbage. */
392
+ if ( ca->data_type == CA_OBJECT ) {
393
+ VALUE *p = (VALUE *) ca->ptr;
394
+ ca_size_t i;
395
+ for ( i = 0; i < ca->elements; i++ ) *p++ = Qnil;
396
+ }
365
397
  ca_triop_func_xfer_stride(ca, starts, ca->dim, native, ca->ptr, CA_XFER_GET);
366
398
  }
367
399
 
@@ -405,13 +437,26 @@ ca_triop_func_create_mask (void *ap)
405
437
  has3 = ca_has_mask(op3);
406
438
  if ( ! has1 && ! has2 && ! has3 ) return;
407
439
 
440
+ /* Exactly one masked operand: the answer is that operand's mask, cell
441
+ for cell. Share it rather than allocating a copy per node. */
442
+ if ( has1 + has2 + has3 == 1 ) {
443
+ CArray *src = has1 ? op1 : ( has2 ? op2 : op3 );
444
+ if ( src->elements == to->elements ) {
445
+ to->mask = (CArray *) ca_refer_new(src->mask, CA_BOOLEAN,
446
+ to->ndim, to->dim, 0, 0);
447
+ return;
448
+ }
449
+ }
450
+
408
451
  to->mask = (CArray *) carray_new(CA_BOOLEAN, to->ndim, to->dim, 0, NULL);
409
452
  dst = (boolean8_t *) to->mask->ptr;
410
453
  n = to->elements;
411
454
 
412
- if ( has1 ) ca_attach(op1);
413
- if ( has2 ) ca_attach(op2);
414
- if ( has3 ) ca_attach(op3);
455
+ /* The masks are what is read here; attaching the operand instead
456
+ materialises the whole subexpression under it. */
457
+ if ( has1 ) ca_attach(op1->mask);
458
+ if ( has2 ) ca_attach(op2->mask);
459
+ if ( has3 ) ca_attach(op3->mask);
415
460
 
416
461
  m1 = has1 ? (boolean8_t *) op1->mask->ptr : NULL;
417
462
  m2 = has2 ? (boolean8_t *) op2->mask->ptr : NULL;
@@ -426,9 +471,9 @@ ca_triop_func_create_mask (void *ap)
426
471
  dst[i] = (boolean8_t) ( a | b | c );
427
472
  }
428
473
 
429
- if ( has3 ) ca_detach(op3);
430
- if ( has2 ) ca_detach(op2);
431
- if ( has1 ) ca_detach(op1);
474
+ if ( has3 ) ca_detach(op3->mask);
475
+ if ( has2 ) ca_detach(op2->mask);
476
+ if ( has1 ) ca_detach(op1->mask);
432
477
  }
433
478
 
434
479
  ca_operation_function_t ca_triop_func = {
@@ -597,23 +642,17 @@ rb_ca_triop_build (VALUE cary1, VALUE cary2, VALUE cary3, uint16_t op_id)
597
642
  TypedData_Get_Struct(r1, CArray, &carray_data_type, op1);
598
643
  TypedData_Get_Struct(r3, CArray, &carray_data_type, op3);
599
644
 
600
- /* Step 3: shape sanity — each of op2 / op3 must match op1's element
601
- count OR be a 1-element CScalar (kernel walks with element_step
602
- = 0 in that case). */
645
+ /* Step 3: each of op2 / op3 must match op1's element count OR be a
646
+ 1-element CScalar (kernel walks with element_step = 0 in that case).
647
+ Unreachable from Ruby; see the note in ca_obj_binop.c. */
603
648
  if ( op2->elements != op1->elements && op2->elements != 1 ) {
604
649
  rb_raise(rb_eArgError,
605
- "CATriOp: shape mismatch on op2 (%lld vs %lld) — only "
606
- "same-ndim size-1 broadcast is supported; cross-ndim "
607
- "promotion is not adopted in CArray "
608
- "(reshape explicitly)",
650
+ "CATriOp: element count mismatch on op2 (%lld vs %lld)",
609
651
  (long long) op2->elements, (long long) op1->elements);
610
652
  }
611
653
  if ( op3->elements != op1->elements && op3->elements != 1 ) {
612
654
  rb_raise(rb_eArgError,
613
- "CATriOp: shape mismatch on op3 (%lld vs %lld) — only "
614
- "same-ndim size-1 broadcast is supported; cross-ndim "
615
- "promotion is not adopted in CArray "
616
- "(reshape explicitly)",
655
+ "CATriOp: element count mismatch on op3 (%lld vs %lld)",
617
656
  (long long) op3->elements, (long long) op1->elements);
618
657
  }
619
658
 
data/ext/ca_obj_window.c CHANGED
@@ -334,12 +334,20 @@ ca_window_func_xfer_stride (void *ap, ca_size_t *starts, ca_size_t *counts,
334
334
  for (k = ndim - 1; k >= 0; k--) { dstride[k] = s; s *= counts[k]; }
335
335
  for (k = 0; k < ndim; k++) n *= counts[k];
336
336
 
337
- for (k = 0; k < ndim; k++) {
338
- if ( (w->bounds[k] != CA_BOUNDS_FILL && w->bounds[k] != CA_BOUNDS_MASK)
339
- || strides[k] % wnative[k] != 0
340
- || strides[k] / wnative[k] != 1 ) {
341
- structural = 0;
342
- break;
337
+ /* The request is over the view's addresses, so a transposed / flat request
338
+ is legal and must not be composed axis-by-axis; see
339
+ ca_xfer_stride_request_is_axis_box (carray.h). */
340
+ if ( ! ca_xfer_stride_request_is_axis_box(w, starts, counts, strides) ) {
341
+ structural = 0;
342
+ }
343
+ else {
344
+ for (k = 0; k < ndim; k++) {
345
+ if ( (w->bounds[k] != CA_BOUNDS_FILL && w->bounds[k] != CA_BOUNDS_MASK)
346
+ || strides[k] % wnative[k] != 0
347
+ || strides[k] / wnative[k] != 1 ) {
348
+ structural = 0;
349
+ break;
350
+ }
343
351
  }
344
352
  }
345
353
 
@@ -374,12 +382,31 @@ ca_window_func_xfer_stride (void *ap, ca_size_t *starts, ca_size_t *counts,
374
382
  lo[k] = l; hi[k] = h;
375
383
  }
376
384
 
385
+ /* An unattached parent that ca_attach_is_alias accepts has memory to lend
386
+ and the attach neither allocates nor copies, so borrow it and re-enter
387
+ on the batched branch below. The recursion runs at most once: the
388
+ second entry sees parent->ptr != NULL. A parent with nothing to lend is
389
+ left alone — the per-row loop is the region protocol and must not be
390
+ traded for a whole-parent attach. */
391
+ if ( ! parent->ptr && ca_attach_is_alias(parent) ) {
392
+ ca_attach(parent);
393
+ if ( parent->ptr ) {
394
+ ca_window_func_xfer_stride(ap, starts, counts, strides, data, dir);
395
+ if ( dir == CA_XFER_PUT ) {
396
+ ca_sync(parent);
397
+ }
398
+ ca_detach(parent);
399
+ return;
400
+ }
401
+ ca_detach(parent);
402
+ }
403
+
377
404
  /* Fast path: when the parent is attached (ptr != NULL), drive
378
405
  ca_composite_region_* + fill_complement directly with the sub-region
379
406
  intersection geometry. This collapses the outer per-row dispatch loop
380
407
  (one ca_xfer_stride per inner row) into a single batched routine. An
381
- unattached parent falls through to the per-row loop below, which is
382
- equivalent but slower. */
408
+ unattached parent with nothing to lend falls through to the per-row loop
409
+ below, which is equivalent but slower. */
383
410
  if ( parent->ptr ) {
384
411
  ca_size_t any_empty = 0;
385
412
  ca_size_t alias_parent_start[CA_RANK_MAX];
@@ -1011,10 +1038,16 @@ ca_window_func_detach (void *ap)
1011
1038
  so the caller must have made it available.
1012
1039
 
1013
1040
  CAREFUL: neither this nor ca_window_func_xfer_all may call
1014
- ca_attach(parent). A transfer slot that silently attaches its parent
1015
- materialises the whole parent behind the caller's back the cold case
1016
- below instead materialises a parent-shaped scratch through ca_xfer_all,
1017
- which recurses under the same rule. */
1041
+ ca_attach(parent) on a parent that would have to materialise to answer.
1042
+ A transfer slot that silently does that duplicates the whole parent
1043
+ behind the caller's back — the cold case below instead materialises a
1044
+ parent-shaped scratch through ca_xfer_all, which recurses under the
1045
+ same rule.
1046
+
1047
+ The exception is a parent that ca_attach_is_alias accepts: there the
1048
+ attach hands back parent (or root) memory that already exists, so
1049
+ nothing is allocated or copied and the rule has nothing to protect.
1050
+ ca_window_func_fill_data draws the same line. */
1018
1051
  static void
1019
1052
  ca_window_func_run_fast_path (CAWindow *ca, char *data, int dir)
1020
1053
  {
@@ -1049,8 +1082,44 @@ ca_window_func_xfer_all (void *ap, void *data, int dir)
1049
1082
  ca_window_func_run_fast_path(ca, (char *) data, dir);
1050
1083
  return;
1051
1084
  }
1052
- /* Cold parent: materialise it into a scratch buffer via ca_xfer_all, then
1053
- run the normal fast path with the scratch standing in for parent->ptr. */
1085
+ /* Cold parent that has memory to lend (entity, or a CAStride-family view
1086
+ whose composed strides alias a ptr-bearing root): borrow it. The attach
1087
+ costs no allocation and no copy, and on the alias path ca->parent->ptr
1088
+ points into the root, so a PUT lands where the scratch path would have
1089
+ had to copy it back. Without this the window duplicates the entire
1090
+ parent on every transfer, which is what an ordinary `a[nil, nil]`,
1091
+ `a.refer` or `a.transpose.transpose` parent used to pay. */
1092
+ if ( ca_attach_is_alias(ca->parent) ) {
1093
+ ca_attach(ca->parent);
1094
+ ca_window_func_run_fast_path(ca, (char *) data, dir);
1095
+ if ( dir == CA_XFER_PUT ) {
1096
+ ca_sync(ca->parent);
1097
+ }
1098
+ ca_detach(ca->parent);
1099
+ return;
1100
+ }
1101
+ /* Second chance: ca_attach_is_alias reads the leaf view's own strides, so
1102
+ it declines a chain that is only contiguous once composed (a.transpose
1103
+ .transpose is the plain case). ca_resolve_attached_root_via_identity
1104
+ folds the chain and answers the same question about the root, and when
1105
+ it succeeds the parent's flat byte addressing IS the root's — so the
1106
+ root ptr can stand in for the parent's exactly as the scratch does
1107
+ below, with no copy in either direction. ca_window_func_xfer_addrs
1108
+ already resolves its parent this way. */
1109
+ {
1110
+ CArray *root = ca_resolve_attached_root_via_identity(ca->parent);
1111
+ if ( root != ca->parent && root->ptr ) {
1112
+ CArray *parent = ca->parent;
1113
+ parent->ptr = root->ptr;
1114
+ ca_window_func_run_fast_path(ca, (char *) data, dir);
1115
+ parent->ptr = NULL;
1116
+ return;
1117
+ }
1118
+ }
1119
+
1120
+ /* Cold parent with nothing to lend: materialise it into a scratch buffer
1121
+ via ca_xfer_all, then run the normal fast path with the scratch standing
1122
+ in for parent->ptr. */
1054
1123
  {
1055
1124
  volatile VALUE holder;
1056
1125
  CArray *parent = ca->parent;
@@ -1296,7 +1365,7 @@ rb_ca_window_new (VALUE cary,
1296
1365
  VALUE
1297
1366
  rb_ca_window (int argc, VALUE *argv, VALUE self)
1298
1367
  {
1299
- volatile VALUE obj, ropt, rfval = CA_NIL, rbounds = Qnil, rcs;
1368
+ volatile VALUE obj, ropt, rfval = CA_UNSPECIFIED, rbounds = Qnil, rcs;
1300
1369
  CArray *ca;
1301
1370
  CScalar *cs;
1302
1371
  ca_size_t start[CA_RANK_MAX];
@@ -1334,7 +1403,7 @@ rb_ca_window (int argc, VALUE *argv, VALUE self)
1334
1403
  "use fill_value: kwarg (e.g. window(-1..1, fill_value: UNDEF))");
1335
1404
  }
1336
1405
 
1337
- if ( rfval == CA_NIL ) {
1406
+ if ( rfval == CA_UNSPECIFIED ) {
1338
1407
  ;
1339
1408
  }
1340
1409
  else if ( rfval == CA_UNDEF ) {
@@ -1397,7 +1466,7 @@ rb_ca_window (int argc, VALUE *argv, VALUE self)
1397
1466
 
1398
1467
  obj = rb_ca_window_new(self, start, count, bounds, fill);
1399
1468
 
1400
- CA_FACE_LIFT_IF_FACE(obj, self, ca);
1469
+ CA_WRAPPER_LIFT(obj, self, ca);
1401
1470
  return obj;
1402
1471
  }
1403
1472
 
@@ -0,0 +1,123 @@
1
+ /* ---------------------------------------------------------------------------
2
+
3
+ ca_op_cmplx64.h -- the cmplx64 products and quotients, computed in
4
+ double and rounded once
5
+
6
+ Used by:
7
+ - ext/carray_kernels_binop.c and ext/carray_kernels_monop.c
8
+ (generated from ext/mkkernel.rb): the `*`, `/`, `rcp` and
9
+ `rcp_mul` kernels for CA_CMPLX64.
10
+
11
+ Why these are not just `x * y` and `x / y`. Both the product and the
12
+ quotient of two complex numbers subtract two products of the parts, so
13
+ the result cancels whenever those two are close. Done at the operands'
14
+ own width there are no bits left underneath to absorb the cancellation.
15
+ Divide has a second cost: the compiler turns a `float _Complex` divide
16
+ into a call to `__divsc3`, which is Smith's algorithm (scale by the
17
+ larger component so the intermediate squares cannot overflow) plus the
18
+ C99 Annex G recovery for infinities -- branches and a call per cell.
19
+
20
+ For a cmplx64 the scaling is not needed at all: the operands are
21
+ floats, so a product of two parts reaches about 1.2e77 where a double
22
+ reaches 1.8e308. Computing the textbook formulas in double therefore
23
+ cannot overflow, and the double carries 29 extra mantissa bits, which
24
+ is what the cancellation needs. Measured against an exact rational
25
+ reference, the double route was correctly rounded on every sample
26
+ where the width-native route was off by up to 1806 ulp (divide) and
27
+ 1679 ulp (multiply).
28
+
29
+ The two are not the same trade. Divide gets faster as well, because
30
+ the call and the scaling both go away. Multiply gets slower, because
31
+ the compiler already inlines a naive float product and only calls
32
+ `__mulsc3` when a NaN appears -- so the wider arithmetic is pure cost
33
+ there, and is paid for the accuracy alone.
34
+
35
+ Annex G is preserved by falling back rather than by reimplementing it.
36
+ The fallback is chosen from the *result*, not from a classification of
37
+ the inputs: if the quick answer is not an ordinary number, hand the
38
+ pair to the compiler's helper and return whatever it says. Writing an
39
+ input classification here would mean transcribing the Annex G table,
40
+ and a transcription drifts from what the runtime actually does.
41
+
42
+ cmplx128 has no wider type to borrow (`long double` is a double on
43
+ Apple ARM64 and a slow 80-bit on x86-64), so it keeps `__mulsc3` /
44
+ `__divdc3`.
45
+
46
+ --------------------------------------------------------------------------- */
47
+
48
+ #ifndef CA_OP_CMPLX64_H
49
+ #define CA_OP_CMPLX64_H
50
+
51
+ #include "carray.h"
52
+ #include <math.h>
53
+
54
+ #ifdef HAVE_COMPLEX_H
55
+
56
+ /* CMPLXF is the float sibling of the CMPLX defined in carray.h, and is
57
+ here for the same reason: `re + I * im` evaluates `I * im` first, so an
58
+ im of +0.0 loses the sign of a -0.0 real part. CMPLXF is C11; provide
59
+ it when the toolchain predates that. */
60
+ #ifndef CMPLXF
61
+ # if defined(__clang__) || (defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 7)))
62
+ # define CMPLXF(re, im) __builtin_complex((float)(re), (float)(im))
63
+ # else
64
+ # define CMPLXF(re, im) \
65
+ (((union { float _parts[2]; float complex _value; }) \
66
+ { { (float)(re), (float)(im) } })._value)
67
+ # endif
68
+ #endif
69
+
70
+ /* The real part is `xr*yr - xi*yi`, which cancels when the two products
71
+ are close; the double keeps the bits that cancellation eats. Only a
72
+ NaN can appear here that the naive form gets wrong, since a product of
73
+ two floats cannot overflow a double -- an infinite part in the answer
74
+ is the true answer. */
75
+ static inline cmplx64_t
76
+ op_cmul_cmplx64 (cmplx64_t x, cmplx64_t y)
77
+ {
78
+ double xr = crealf(x), xi = cimagf(x);
79
+ double yr = crealf(y), yi = cimagf(y);
80
+ float rr = (float) (xr * yr - xi * yi);
81
+ float ri = (float) (xr * yi + xi * yr);
82
+ if ( isnan(rr) || isnan(ri) ) {
83
+ return x * y; /* __mulsc3: an infinity met a zero (Annex G) */
84
+ }
85
+ return CMPLXF(rr, ri);
86
+ }
87
+
88
+ static inline cmplx64_t
89
+ op_cdiv_cmplx64 (cmplx64_t x, cmplx64_t y)
90
+ {
91
+ double yr = crealf(y), yi = cimagf(y);
92
+ double d = yr * yr + yi * yi;
93
+ if ( d > 0.0 && d < INFINITY ) {
94
+ double xr = crealf(x), xi = cimagf(x);
95
+ float rr = (float) ((xr * yr + xi * yi) / d);
96
+ float ri = (float) ((xi * yr - xr * yi) / d);
97
+ if ( isfinite(rr) && isfinite(ri) ) {
98
+ return CMPLXF(rr, ri);
99
+ }
100
+ }
101
+ return x / y; /* __divsc3: zero, infinite or NaN operands (Annex G) */
102
+ }
103
+
104
+ /* 1 / y. Same shape with the numerator's parts folded in, so that
105
+ `rcp` and `1 / z` keep giving the same answer. */
106
+ static inline cmplx64_t
107
+ op_crcp_cmplx64 (cmplx64_t y)
108
+ {
109
+ double yr = crealf(y), yi = cimagf(y);
110
+ double d = yr * yr + yi * yi;
111
+ if ( d > 0.0 && d < INFINITY ) {
112
+ float rr = (float) (yr / d);
113
+ float ri = (float) (-yi / d);
114
+ if ( isfinite(rr) && isfinite(ri) ) {
115
+ return CMPLXF(rr, ri);
116
+ }
117
+ }
118
+ return 1 / y;
119
+ }
120
+
121
+ #endif /* HAVE_COMPLEX_H */
122
+
123
+ #endif /* CA_OP_CMPLX64_H */
data/ext/ca_op_ipower.c CHANGED
@@ -233,8 +233,6 @@ rb_ca_ipower (VALUE self, VALUE other)
233
233
  ALLOCV_END(h1);
234
234
  }
235
235
 
236
- obj = rb_ca_rewrap_unbound_repeat(self, obj);
237
-
238
236
  return obj;
239
237
  }
240
238
 
@@ -289,7 +287,6 @@ static VALUE rb_ca_pow (VALUE self, VALUE other)
289
287
  }
290
288
  else {
291
289
  obj = rb_ca_power(self, other);
292
- obj = rb_ca_rewrap_unbound_repeat(self, obj);
293
290
  return obj;
294
291
  }
295
292
  }
@@ -4,7 +4,7 @@
4
4
 
5
5
  Portable textbook sort kernels (PROPOSAL_PORTABLE_TEXTBOOK_SORT).
6
6
 
7
- P.1 / P.2 : quicksort + mergesort over the 10 numeric dtypes
7
+ P.1 / P.2 : quicksort + mergesort over the 10 numeric data types
8
8
  (i8 / u8 / i16 / u16 / i32 / u32 / i64 / u64 / f32 / f64)
9
9
  P.3 : NaN pre-partition for f32 / f64
10
10
  P.4 : pair (value + index) variants for argsort kernels —
@@ -33,7 +33,7 @@
33
33
  flight. */
34
34
  #include "carray.h"
35
35
 
36
- /* P.1 / P.2: quicksort over 10 numeric dtypes. */
36
+ /* P.1 / P.2: quicksort over 10 numeric data types. */
37
37
  void ca_sort_quick_i8 (int8_t *a, ca_size_t n);
38
38
  void ca_sort_quick_u8 (uint8_t *a, ca_size_t n);
39
39
  void ca_sort_quick_i16 (int16_t *a, ca_size_t n);
@@ -45,8 +45,8 @@ void ca_sort_quick_u64 (uint64_t *a, ca_size_t n);
45
45
  void ca_sort_quick_f32 (float32_t *a, ca_size_t n);
46
46
  void ca_sort_quick_f64 (double *a, ca_size_t n);
47
47
 
48
- /* P.1 / P.2: mergesort over 10 numeric dtypes (`aux` is caller-supplied
49
- scratch buffer of the same length / dtype as `a`). */
48
+ /* P.1 / P.2: mergesort over 10 numeric data types (`aux` is caller-supplied
49
+ scratch buffer of the same length / data type as `a`). */
50
50
  void ca_sort_merge_i8 (int8_t *a, int8_t *aux, ca_size_t n);
51
51
  void ca_sort_merge_u8 (uint8_t *a, uint8_t *aux, ca_size_t n);
52
52
  void ca_sort_merge_i16 (int16_t *a, int16_t *aux, ca_size_t n);
@@ -58,7 +58,7 @@ void ca_sort_merge_u64 (uint64_t *a, uint64_t *aux, ca_size_t n);
58
58
  void ca_sort_merge_f32 (float32_t *a, float32_t *aux, ca_size_t n);
59
59
  void ca_sort_merge_f64 (double *a, double *aux, ca_size_t n);
60
60
 
61
- /* P.3: NaN pre-partition for float dtypes (Hoare 1-pass, returns finite count). */
61
+ /* P.3: NaN pre-partition for float data types (Hoare 1-pass, returns finite count). */
62
62
  ca_size_t ca_partition_nan_f32 (float32_t *a, ca_size_t n);
63
63
  ca_size_t ca_partition_nan_f64 (double *a, ca_size_t n);
64
64