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/carray_access.c CHANGED
@@ -492,6 +492,14 @@ rb_ca_ref_all (VALUE self, CAIndexInfo *info)
492
492
  return rb_funcall(self, rb_intern("refer"), 0);
493
493
  }
494
494
 
495
+ static ID
496
+ ca_id_expression_evaluator (void)
497
+ {
498
+ static ID id = 0;
499
+ if ( ! id ) id = rb_intern("@expression_evaluator");
500
+ return id;
501
+ }
502
+
495
503
  VALUE
496
504
  rb_ca_store_all (VALUE self, VALUE rval)
497
505
  {
@@ -515,16 +523,34 @@ rb_ca_store_all (VALUE self, VALUE rval)
515
523
  CArray *cv;
516
524
  TypedData_Get_Struct(rval, CArray, &carray_data_type, cv);
517
525
 
518
- if ( cv->obj_type == CA_OBJ_UNBOUND_REPEAT ) {
519
- rval = ca_ubrep_bind_with(rval, self);
520
- TypedData_Get_Struct(rval, CArray, &carray_data_type, cv);
526
+ /* Where something is registered to compute an expression, and this is
527
+ one, it is asked before the walk below -- it fills the destination
528
+ directly, which is the whole of the saving; making an array and
529
+ copying it over is most of the work once the expression itself is
530
+ fast. It may decline, and then the walk does it. Narrow on purpose:
531
+ the same shape and data type, so nothing here has to broadcast or
532
+ cast on the way. */
533
+ if ( ca_is_lazy_view(cv)
534
+ && ca->data_type == cv->data_type
535
+ && ca->ndim == cv->ndim
536
+ && ca->elements == cv->elements
537
+ && RTEST(rb_attr_get(rb_cCArray, ca_id_expression_evaluator())) ) {
538
+ int8_t k;
539
+ int same = 1;
540
+ for ( k = 0; k < ca->ndim; k++ ) {
541
+ if ( ca->dim[k] != cv->dim[k] ) { same = 0; break; }
542
+ }
543
+ if ( same ) {
544
+ VALUE fusion = rb_const_get(rb_cCArray, rb_intern("Fusion"));
545
+ if ( RTEST(rb_funcall(fusion, rb_intern("evaluate_into"), 2, rval, self)) ) {
546
+ return rval;
547
+ }
548
+ }
521
549
  }
522
550
 
523
- if ( ca->elements != cv->elements ) {
524
- rb_raise(rb_eRuntimeError,
525
- "mismatch in data size (%" PRId64 " <-> %" PRId64 ") for storing to carray",
526
- (ca_size_t) ca->elements, (ca_size_t) cv->elements);
527
- }
551
+ /* The destination owns the shape; see ca_broadcast_to_destination. */
552
+ ca_broadcast_to_destination(self, &rval);
553
+ TypedData_Get_Struct(rval, CArray, &carray_data_type, cv);
528
554
 
529
555
  /* Source delivery via ca_xfer_all into a local scratch instead of
530
556
  ca_attach(cv). This routes through ca_*_func_xfer_all's per-region
@@ -985,7 +1011,16 @@ rb_ca_fetch_method (int argc, VALUE *argv, VALUE self)
985
1011
 
986
1012
  switch ( info.type ) {
987
1013
  case CA_REG_ADDRESS_COMPLEX:
1014
+ /* Re-enter against a flattened view of self. rb_ca_refer_new_flatten
1015
+ is an internal builder and hands back a bare refer, so a marker
1016
+ receiver would be dropped here and the result would never be lifted
1017
+ at the tail. Put it back on before the re-entry. (A Face survives
1018
+ on its own -- the internal builder still lifts those.) */
988
1019
  self = rb_ca_refer_new_flatten(self);
1020
+ if ( ca_is_lazy_marker(ca) ) {
1021
+ extern VALUE rb_ca_lazy_marker_new (VALUE cary);
1022
+ self = rb_ca_lazy_marker_new(self);
1023
+ }
989
1024
  goto retry;
990
1025
  case CA_REG_ADDRESS:
991
1026
  obj = rb_ca_ref_address(self, &info);
@@ -1011,9 +1046,6 @@ rb_ca_fetch_method (int argc, VALUE *argv, VALUE self)
1011
1046
  case CA_REG_REPEAT:
1012
1047
  obj = rb_ca_repeat(argc, argv, self);
1013
1048
  break;
1014
- case CA_REG_UNBOUND_REPEAT:
1015
- obj = rb_funcall2(self, rb_intern("unbound_repeat"), (int) argc, argv);
1016
- break;
1017
1049
  case CA_REG_MAPPING:
1018
1050
  obj = rb_ca_fancy_index_chain(self, argv[0]);
1019
1051
  break;
@@ -1050,19 +1082,19 @@ rb_ca_fetch_method (int argc, VALUE *argv, VALUE self)
1050
1082
  rb_raise(rb_eIndexError, "invalid index specified");
1051
1083
  }
1052
1084
 
1053
- /* Face lift at the read touch point: if `self` is a Face, re-wrap the
1054
- view result as a Face (ca_face_lift). Guards: lift only when the result
1055
- is a CArray instance (excludes Hash / iterator / scalar), and only when
1056
- it is not ALREADY a Face -- some builders (select / repeat / ...) lift
1057
- their own result, so re-lifting here would double-wrap and leave `.parent`
1058
- pointing at a Face instead of the storage. */
1059
- if ( ca_is_face(ca) && rb_obj_is_kind_of(obj, rb_cCArray) ) {
1060
- CArray *obj_ca;
1061
- TypedData_Get_Struct(obj, CArray, &carray_data_type, obj_ca);
1062
- if ( ! ca_is_face(obj_ca) ) {
1063
- obj = ca_face_lift(obj, self);
1064
- }
1065
- }
1085
+ /* Wrapper lift at the read touch point: if `self` is a Face or a
1086
+ CALazyMarker, re-wrap the view result so the wrapper stays on top.
1087
+ Guarded to CArray results, which drops the scalar / CASlabIterator /
1088
+ CAGroupIterator / Hash cases the switch above can produce. The
1089
+ already-a-Face case (some builders lift their own result) is handled
1090
+ inside ca_wrapper_lift.
1091
+
1092
+ Every CA_REG_* branch converges here, so this one line covers all of
1093
+ them -- but not every index form: `a[:*, nil]` builds a CAUnboundRepeat,
1094
+ whose shape stays open until an operand binds it, and a marker copies
1095
+ shape at construction. ca_wrapper_lift refuses that one, in one place,
1096
+ because `unbound_repeat` reaches it by a second route. */
1097
+ CA_WRAPPER_LIFT(obj, self, ca);
1066
1098
 
1067
1099
  return obj;
1068
1100
  }
@@ -1305,9 +1337,6 @@ rb_cs_fetch_method (int argc, VALUE *argv, VALUE self)
1305
1337
  case CA_REG_REPEAT:
1306
1338
  obj = rb_ca_repeat(argc, argv, self);
1307
1339
  break;
1308
- case CA_REG_UNBOUND_REPEAT:
1309
- obj = rb_funcall2(self, rb_intern("unbound_repeat"), (int) argc, argv);
1310
- break;
1311
1340
  case CA_REG_MAPPING:
1312
1341
  obj = rb_ca_fancy_index_chain(self, argv[0]);
1313
1342
  break;
@@ -1491,10 +1520,6 @@ rb_ca_store_method (int argc, VALUE *argv, VALUE self)
1491
1520
  obj = rb_ca_store_all(obj, rval);
1492
1521
  break;
1493
1522
  }
1494
- case CA_REG_UNBOUND_REPEAT:
1495
- obj = rb_funcall2(self, rb_intern("unbound_repeat"), (int) argc, argv);
1496
- obj = rb_ca_store_all(obj, rval);
1497
- break;
1498
1523
  case CA_REG_MAPPING: {
1499
1524
  obj = rb_ca_fancy_index_chain(self, argv[0]);
1500
1525
  obj = rb_ca_store_all(obj, rval);
@@ -1669,7 +1694,6 @@ rb_ca_s_scan_index (VALUE self, VALUE rdim, VALUE ridx)
1669
1694
  case CA_REG_GRID:
1670
1695
  case CA_REG_MAPPING:
1671
1696
  case CA_REG_METHOD_CALL:
1672
- case CA_REG_UNBOUND_REPEAT:
1673
1697
  case CA_REG_MEMBER:
1674
1698
  case CA_REG_ATTRIBUTE:
1675
1699
  break;
@@ -2094,8 +2118,6 @@ Init_carray_access (void)
2094
2118
  rb_define_const(rb_cObject, "CA_REG_MAPPING", INT2NUM(CA_REG_MAPPING));
2095
2119
  rb_define_const(rb_cObject, "CA_REG_METHOD_CALL",
2096
2120
  INT2NUM(CA_REG_METHOD_CALL));
2097
- rb_define_const(rb_cObject, "CA_REG_UNBOUND_REPEAT",
2098
- INT2NUM(CA_REG_UNBOUND_REPEAT));
2099
2121
  rb_define_const(rb_cObject, "CA_REG_MEMBER", INT2NUM(CA_REG_MEMBER));
2100
2122
  rb_define_const(rb_cObject, "CA_REG_ATTRIBUTE", INT2NUM(CA_REG_ATTRIBUTE));
2101
2123
 
@@ -3,11 +3,11 @@
3
3
  carray_bincount.c — dedicated bincount kernels (count-only + weighted)
4
4
 
5
5
  Tight per-element scatter into a pre-sized 1-D output buffer. The
6
- inner loop reads labels in their native integer dtype (no cast to
6
+ inner loop reads labels in their native integer data type (no cast to
7
7
  int64), skips per-iteration bounds checking (caller validates label
8
- range), and emits the output dtype directly.
8
+ range), and emits the output data type directly.
9
9
 
10
- Output dtype:
10
+ Output data type:
11
11
  - count-only: UInt32 if length < 2^32, else UInt64.
12
12
  - weighted: weights.data_type.
13
13
 
@@ -15,7 +15,7 @@
15
15
  weight is also skipped (its label contributes 0).
16
16
 
17
17
  Caller contract (= lib/carray/methods/bincount.rb's CArray#bincount):
18
- - self is integer dtype.
18
+ - self is integer data type.
19
19
  - length is the output size, pre-sized to max(length, label_max+1)
20
20
  with label_min >= 0 already verified.
21
21
 
@@ -30,8 +30,8 @@
30
30
 
31
31
  #include "carray.h"
32
32
 
33
- /* Tight inner loop: 2 mask-aware variants × 8 label dtypes × 2 output
34
- dtypes = 32 specializations. Generated via macro expansion.
33
+ /* Tight inner loop: 2 mask-aware variants × 8 label data types × 2 output
34
+ data types = 32 specializations. Generated via macro expansion.
35
35
 
36
36
  Layout:
37
37
  COUNT_KERNEL(LABEL_T, OUT_T)
@@ -91,7 +91,7 @@ rb_ca_bincount_count_kernel (VALUE self, VALUE rlength)
91
91
  rb_raise(rb_eArgError, "bincount: length must be non-negative");
92
92
  }
93
93
 
94
- /* Output dtype: UInt32 default; UInt64 if length doesn't fit. */
94
+ /* Output data type: UInt32 default; UInt64 if length doesn't fit. */
95
95
  out_type = (length > 0xFFFFFFFFLL) ? CA_UINT64 : CA_UINT32;
96
96
  shape_out[0] = length;
97
97
  vout = rb_carray_new(out_type, 1, shape_out, 0, NULL);
@@ -125,7 +125,7 @@ rb_ca_bincount_count_kernel (VALUE self, VALUE rlength)
125
125
 
126
126
  /* --------------------------------------------------------------- */
127
127
 
128
- /* Weighted variant: output dtype = weights dtype.
128
+ /* Weighted variant: output data type = weights data type.
129
129
  Inner: out[label[i]] += weight[i].
130
130
  Mask: skip if label[i] masked OR weight[i] masked. */
131
131
 
@@ -12,10 +12,12 @@
12
12
  ca_broadcast_pair (&self, &other)
13
13
  Two-sided expansion for binary ops (case A only: same ndim,
14
14
  size-1 axes broadcast pairwise). Leaves both operands
15
- unchanged when shapes are already equal or when they are
16
- incompatible (the caller's existing elements-mismatch raise
17
- path then handles the error). Called by carray_cast.c's
18
- coercion path and by the binop dispatcher.
15
+ unchanged when shapes are already equal, and raises when they
16
+ cannot be paired: both operands of a binary operation are
17
+ equally authoritative, so a shape conflict has no resolution.
18
+ Called by carray_cast.c's coercion path and by the binop,
19
+ bincmp and triop builders, which is every place two operands
20
+ are brought together.
19
21
 
20
22
  rb_ca_broadcast_to -- backs the public CArray#broadcast_to method
21
23
  (right-to-left axis pairing; see the docstring at the function).
@@ -29,6 +31,75 @@
29
31
 
30
32
  #include "carray.h"
31
33
 
34
+ NORETURN(static void ca_broadcast_refuse_write (VALUE dst, VALUE src));
35
+
36
+ /* Report a source that cannot be brought to the destination's shape. */
37
+ static void
38
+ ca_broadcast_refuse_write (VALUE dst, VALUE src)
39
+ {
40
+ volatile VALUE dst_s = rb_inspect(rb_funcall(dst, rb_intern("shape"), 0));
41
+ volatile VALUE src_s = rb_inspect(rb_funcall(src, rb_intern("shape"), 0));
42
+ rb_raise(rb_eRuntimeError,
43
+ "shape mismatch writing to carray (%s <- %s); shapes must agree "
44
+ "once size-1 axes are dropped, or one side must be 1-D, or the "
45
+ "source must be smaller and broadcastable -- use .flatten to "
46
+ "write the values in the order they lie",
47
+ StringValueCStr(dst_s), StringValueCStr(src_s));
48
+ }
49
+
50
+ /* Bring `*src` to `dst`'s shape for a write into `dst` (assignment, and
51
+ the in-place operators, whose result shape is the destination's by
52
+ definition). The destination owns the shape and the source only
53
+ supplies values, so what is allowed here is wider than what two
54
+ operands of a binary operation may do -- but only where nothing is
55
+ duplicated. When the counts already agree the values land in the
56
+ order they lie and the shape is merely a reading of them: shapes that
57
+ both sides claim must agree once size-1 axes are dropped, since such
58
+ an axis moves no element, and a 1-D side claims no shape at all,
59
+ whether it is the flat source or the flat container. When the source
60
+ is smaller it is repeated instead, and that is held to same-ndim
61
+ size-1 and is one-sided: the destination never grows. Raises when
62
+ neither reading applies. */
63
+ void
64
+ ca_broadcast_to_destination (VALUE dst, volatile VALUE *src)
65
+ {
66
+ CArray *cd, *cs;
67
+
68
+ TypedData_Get_Struct(dst, CArray, &carray_data_type, cd);
69
+ TypedData_Get_Struct(*src, CArray, &carray_data_type, cs);
70
+
71
+ if ( cs->elements == cd->elements ) {
72
+ ca_size_t dst_dim[CA_RANK_MAX], src_dim[CA_RANK_MAX];
73
+ int dst_ndim = 0, src_ndim = 0, i, agree;
74
+ if ( cd->ndim <= 1 || cs->ndim <= 1 ) return;
75
+ for ( i = 0; i < cd->ndim; i++ ) {
76
+ if ( cd->dim[i] != 1 ) dst_dim[dst_ndim++] = cd->dim[i];
77
+ }
78
+ for ( i = 0; i < cs->ndim; i++ ) {
79
+ if ( cs->dim[i] != 1 ) src_dim[src_ndim++] = cs->dim[i];
80
+ }
81
+ agree = ( dst_ndim == src_ndim );
82
+ for ( i = 0; agree && i < dst_ndim; i++ ) {
83
+ if ( dst_dim[i] != src_dim[i] ) agree = 0;
84
+ }
85
+ if ( ! agree ) ca_broadcast_refuse_write(dst, *src);
86
+ return;
87
+ }
88
+
89
+ if ( cs->elements < cd->elements && cs->ndim == cd->ndim ) {
90
+ int i;
91
+ for ( i = 0; i < cd->ndim; i++ ) {
92
+ if ( cs->dim[i] != cd->dim[i] && cs->dim[i] != 1 ) break;
93
+ }
94
+ if ( i == cd->ndim ) {
95
+ *src = ca_broadcast_view(*src, cd->ndim, cd->dim);
96
+ return;
97
+ }
98
+ }
99
+
100
+ ca_broadcast_refuse_write(dst, *src);
101
+ }
102
+
32
103
  /* Build a CAStride view of `src` with shape `target_dim`. For each
33
104
  axis where src.dim[i] == target_dim[i], inherit the row-major byte
34
105
  stride; for src.dim[i] == 1 and target_dim[i] > 1, use stride 0.
@@ -99,6 +170,24 @@ ca_broadcast_view (VALUE src, int8_t ndim, ca_size_t *target_dim)
99
170
  pairwise equal, no-op. If any axis pair is incompatible (neither
100
171
  side is 1 nor equal), no-op (caller's existing element-count check
101
172
  raises). */
173
+ NORETURN(static void ca_broadcast_refuse (VALUE self, VALUE other));
174
+
175
+ /* Report a pair that cannot be brought to a common shape. Naming both
176
+ shapes is the point of the message: the caller wrote two arrays that
177
+ cannot be combined, and the only remedy is to say which two. */
178
+ static void
179
+ ca_broadcast_refuse (VALUE self, VALUE other)
180
+ {
181
+ volatile VALUE self_s = rb_inspect(rb_funcall(self, rb_intern("shape"), 0));
182
+ volatile VALUE other_s = rb_inspect(rb_funcall(other, rb_intern("shape"), 0));
183
+ rb_raise(rb_eArgError,
184
+ "shape mismatch between operands (%s and %s); shapes must "
185
+ "agree, or differ only in size-1 axes with equal ndim -- use "
186
+ ".flatten on both sides to operate on the values in the order "
187
+ "they lie",
188
+ StringValueCStr(self_s), StringValueCStr(other_s));
189
+ }
190
+
102
191
  void
103
192
  ca_broadcast_pair (volatile VALUE *self, volatile VALUE *other)
104
193
  {
@@ -111,8 +200,11 @@ ca_broadcast_pair (volatile VALUE *self, volatile VALUE *other)
111
200
  TypedData_Get_Struct(*self, CArray, &carray_data_type, ca);
112
201
  TypedData_Get_Struct(*other, CArray, &carray_data_type, cb);
113
202
 
203
+ /* A scalar carries no shape and reaches every cell; leave the pair to
204
+ the caller's own scalar handling. A one-element array is not a
205
+ scalar: it states a shape, and is held to it. */
114
206
  if (ca_is_scalar(ca) || ca_is_scalar(cb)) return;
115
- if (ca->ndim != cb->ndim) return;
207
+ if (ca->ndim != cb->ndim) ca_broadcast_refuse(*self, *other);
116
208
  if (ca->ndim == 0) return;
117
209
 
118
210
  for (i = 0; i < ca->ndim; i++) {
@@ -133,7 +225,8 @@ ca_broadcast_pair (volatile VALUE *self, volatile VALUE *other)
133
225
  }
134
226
  }
135
227
 
136
- if (!can_broadcast || !needs_broadcast) return;
228
+ if (!can_broadcast) ca_broadcast_refuse(*self, *other);
229
+ if (!needs_broadcast) return;
137
230
 
138
231
  *self = ca_broadcast_view(*self, ca->ndim, target_dim);
139
232
  *other = ca_broadcast_view(*other, ca->ndim, target_dim);
@@ -0,0 +1,3 @@
1
+ /* GENERATED by extconf.rb: the flags this build's arithmetic
2
+ was compiled with. See CArray::BUILD_FLAGS. */
3
+ #define CA_BUILD_FLAGS "-O3 -fno-fast-math -fopenmp-simd -march=native"