carray 3.0.6 → 3.0.7

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 (139) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +110 -0
  3. data/README.md +24 -3
  4. data/carray.gemspec +1 -1
  5. data/ext/ca_axis_group.c +6 -6
  6. data/ext/ca_binop_dispatch.c +8 -0
  7. data/ext/ca_binop_dispatch.h +1 -0
  8. data/ext/ca_categorical_iterator.c +7 -7
  9. data/ext/ca_kernel_iterator.h +6 -4
  10. data/ext/ca_obj_array.c +4 -4
  11. data/ext/ca_obj_bincmp.c +19 -3
  12. data/ext/ca_obj_binop.c +36 -11
  13. data/ext/ca_obj_bitarray.c +2 -4
  14. data/ext/ca_obj_bitfield.c +86 -4
  15. data/ext/ca_obj_byte_swap.c +2 -4
  16. data/ext/ca_obj_const_string.c +422 -257
  17. data/ext/ca_obj_face.c +71 -33
  18. data/ext/ca_obj_fake.c +112 -13
  19. data/ext/ca_obj_farray.c +7 -1
  20. data/ext/ca_obj_fixlen_string.c +51 -17
  21. data/ext/ca_obj_grid.c +12 -7
  22. data/ext/ca_obj_meld.c +5 -5
  23. data/ext/ca_obj_monop.c +65 -39
  24. data/ext/ca_obj_object.c +9 -9
  25. data/ext/ca_obj_reduce.c +2 -4
  26. data/ext/ca_obj_refer.c +104 -10
  27. data/ext/ca_obj_repeat.c +1 -1
  28. data/ext/ca_obj_roll.c +2 -4
  29. data/ext/ca_obj_select.c +8 -6
  30. data/ext/ca_obj_select_axis.c +8 -6
  31. data/ext/ca_obj_stack.c +5 -5
  32. data/ext/ca_obj_stride.c +46 -8
  33. data/ext/ca_obj_string.c +2 -2
  34. data/ext/ca_obj_tile.c +2 -4
  35. data/ext/ca_obj_triop.c +15 -12
  36. data/ext/ca_obj_window.c +22 -15
  37. data/ext/ca_op_ipower.c +63 -15
  38. data/ext/ca_op_powi.h +6 -29
  39. data/ext/ca_sort_kernels.h +3 -0
  40. data/ext/ca_sweep_engine.c +90 -23
  41. data/ext/ca_sweep_engine.h +5 -6
  42. data/ext/caframe_csv_reader.c +1 -1
  43. data/ext/carray.h +3 -1
  44. data/ext/carray_access.c +98 -39
  45. data/ext/carray_address_basis.c +98 -24
  46. data/ext/carray_broadcast.c +2 -2
  47. data/ext/carray_call_cfunc.c +5 -8
  48. data/ext/carray_cast.c +311 -37
  49. data/ext/carray_cast_func.rb +37 -5
  50. data/ext/carray_conversion.c +56 -2
  51. data/ext/carray_copy.c +24 -0
  52. data/ext/carray_core.c +69 -6
  53. data/ext/carray_count.c +28 -9
  54. data/ext/carray_element.c +9 -3
  55. data/ext/carray_factorize.c +11 -11
  56. data/ext/carray_histogram.c +2 -2
  57. data/ext/carray_index_classifier.c +7 -5
  58. data/ext/carray_internal.h +94 -0
  59. data/ext/carray_kernels_binop.c +55 -55
  60. data/ext/carray_kernels_init.c +14 -14
  61. data/ext/carray_kernels_monop.c +90 -140
  62. data/ext/carray_lazy.c +16 -1
  63. data/ext/carray_loop.c +9 -2
  64. data/ext/carray_mask.c +118 -11
  65. data/ext/carray_median_percentile.c +13 -8
  66. data/ext/carray_memory_view.c +41 -22
  67. data/ext/carray_operator.c +72 -11
  68. data/ext/carray_order.c +52 -9
  69. data/ext/carray_partition.c +1 -1
  70. data/ext/carray_random.c +1 -1
  71. data/ext/carray_round.c +24 -1
  72. data/ext/carray_scatter.c +4 -0
  73. data/ext/carray_segment.c +1 -1
  74. data/ext/carray_slab.c +13 -4
  75. data/ext/carray_sort.c +6 -3
  76. data/ext/carray_sort_kernel.c +24 -0
  77. data/ext/carray_struct.c +21 -6
  78. data/ext/carray_test.c +85 -39
  79. data/ext/carray_time_text.c +6 -0
  80. data/ext/carray_utils.c +124 -54
  81. data/ext/mk_call_cfunc.rb +6 -11
  82. data/ext/mkkernel.rb +37 -27
  83. data/ext/version.h +4 -4
  84. data/lib/carray/axis_group.rb +9 -6
  85. data/lib/carray/basics.rb +26 -8
  86. data/lib/carray/bincount_nd.rb +8 -2
  87. data/lib/carray/block_iterator.rb +48 -21
  88. data/lib/carray/categorical.rb +21 -8
  89. data/lib/carray/categorical_iterator.rb +2 -1
  90. data/lib/carray/conditional.rb +52 -4
  91. data/lib/carray/const_string.rb +74 -0
  92. data/lib/carray/construct.rb +2 -2
  93. data/lib/carray/core_extensions.rb +19 -68
  94. data/lib/carray/data_type_extension.rb +1 -0
  95. data/lib/carray/fixlen_string.rb +83 -1
  96. data/lib/carray/frame/column_verbs.rb +61 -6
  97. data/lib/carray/frame/concat.rb +50 -6
  98. data/lib/carray/frame/csv_parser.rb +13 -4
  99. data/lib/carray/frame/frame.rb +38 -6
  100. data/lib/carray/frame/group.rb +67 -40
  101. data/lib/carray/frame/io.rb +10 -5
  102. data/lib/carray/frame/join.rb +20 -6
  103. data/lib/carray/frame/records.rb +6 -4
  104. data/lib/carray/frame/reshape.rb +25 -5
  105. data/lib/carray/frame/verbs.rb +78 -25
  106. data/lib/carray/frame/windows.rb +30 -6
  107. data/lib/carray/fuse_source.rb +4 -2
  108. data/lib/carray/iterator.rb +8 -0
  109. data/lib/carray/key_windows.rb +10 -4
  110. data/lib/carray/lazy.rb +12 -17
  111. data/lib/carray/methods/bin.rb +3 -0
  112. data/lib/carray/methods/composition.rb +1 -1
  113. data/lib/carray/methods/factorize.rb +2 -0
  114. data/lib/carray/methods/gather_nd.rb +4 -1
  115. data/lib/carray/methods/is_in.rb +23 -3
  116. data/lib/carray/methods/locate_addr.rb +63 -12
  117. data/lib/carray/methods/repeat.rb +11 -1
  118. data/lib/carray/record_order.rb +35 -6
  119. data/lib/carray/runtime.rb +10 -0
  120. data/lib/carray/segment_iterator.rb +19 -16
  121. data/lib/carray/serialize.rb +22 -0
  122. data/lib/carray/slab_iterator.rb +1 -1
  123. data/lib/carray/stack.rb +3 -3
  124. data/lib/carray/string.rb +6 -6
  125. data/lib/carray/string_operation_extension.rb +25 -4
  126. data/lib/carray/struct_builder.rb +4 -1
  127. data/lib/carray/time.rb +190 -46
  128. data/lib/carray/window_iterator.rb +48 -12
  129. data/yard-stubs/ca_obj_bitfield.rb +4 -1
  130. data/yard-stubs/ca_obj_refer.rb +3 -0
  131. data/yard-stubs/ca_obj_window.rb +2 -1
  132. data/yard-stubs/carray_access.rb +4 -2
  133. data/yard-stubs/carray_cast.rb +6 -0
  134. data/yard-stubs/carray_count.rb +6 -3
  135. data/yard-stubs/carray_math.rb +5 -0
  136. data/yard-stubs/carray_order.rb +6 -0
  137. data/yard-stubs/carray_scatter.rb +2 -1
  138. data/yard-stubs/carray_test.rb +18 -5
  139. metadata +2 -2
data/ext/ca_obj_stack.c CHANGED
@@ -854,8 +854,8 @@ ca_stack_func_allocate (void *ap)
854
854
  {
855
855
  CAStack *ca = (CAStack *) ap;
856
856
  ca_stack_mask_refresh(ca);
857
- ca_attach_all(ca->parents, ca->n_parents);
858
- ca->ptr = ca_alloc_cells((CArray *) ca);
857
+ ca->ptr = ca_alloc_cells_then_attach((CArray *) ca, ca->n_parents,
858
+ ca->parents);
859
859
  }
860
860
 
861
861
  static VALUE
@@ -1408,7 +1408,7 @@ rb_ca_stack_new_with_axis (VALUE parents_ary, int8_t k_axis)
1408
1408
  }
1409
1409
  parents = ALLOCV_N(CArray *, holder, n);
1410
1410
  for ( i = 0; i < n; i++ ) {
1411
- VALUE p = rb_ary_entry(parents_ary, i);
1411
+ volatile VALUE p = rb_ary_entry(parents_ary, i);
1412
1412
  rb_check_carray_object(p);
1413
1413
  TypedData_Get_Struct(p, CArray, &carray_data_type, parents[i]);
1414
1414
  ca_multi_parent_check_part(parents[i], "stack");
@@ -1485,7 +1485,7 @@ rb_ca_stack_initialize (int argc, VALUE *argv, VALUE self)
1485
1485
  /* Resolve k_axis against the first parent's ndim (= insertion position
1486
1486
  range [0, parent_ndim], i.e. half-open [0, parent_ndim + 1)). */
1487
1487
  CArray *ref;
1488
- VALUE first = rb_ary_entry(list, 0);
1488
+ volatile VALUE first = rb_ary_entry(list, 0);
1489
1489
  rb_check_carray_object(first);
1490
1490
  TypedData_Get_Struct(first, CArray, &carray_data_type, ref);
1491
1491
  k_axis = (int8_t) ca_normalize_axis_arg(
@@ -1495,7 +1495,7 @@ rb_ca_stack_initialize (int argc, VALUE *argv, VALUE self)
1495
1495
  ca_check_uninitialized(ca);
1496
1496
  parents = ALLOCV_N(CArray *, holder, n);
1497
1497
  for ( i = 0; i < n; i++ ) {
1498
- VALUE p = rb_ary_entry(list, i);
1498
+ volatile VALUE p = rb_ary_entry(list, i);
1499
1499
  rb_check_carray_object(p);
1500
1500
  TypedData_Get_Struct(p, CArray, &carray_data_type, parents[i]);
1501
1501
  ca_multi_parent_check_part(parents[i], "stack");
data/ext/ca_obj_stride.c CHANGED
@@ -1145,7 +1145,10 @@ static int ca_layout_is_contiguous (int8_t ndim, const ca_size_t *dim,
1145
1145
  Entities hold their own buffer. A CAStride-family root is one the fold
1146
1146
  declined to walk through (a reshape over a transpose); its own attach
1147
1147
  folds onward from itself, so the answer is the one for the root it folds
1148
- to. What is left is the
1148
+ to -- unless two of its cells share memory (sliding windows). Its buffer
1149
+ then holds several copies of a cell, and sending the buffer back would
1150
+ let a stale copy overwrite one a view wrote, so it has nothing to lend
1151
+ either. What is left is the
1149
1152
  boundary views that compute or fetch their contents -- CAObject, the lazy
1150
1153
  per-element transforms, whatever a companion gem installed. All of them
1151
1154
  answer a region request: through their xfer_stride slot, or, when a type
@@ -1153,6 +1156,39 @@ static int ca_layout_is_contiguous (int8_t ndim, const ca_size_t *dim,
1153
1156
  addresses to xfer_addrs. Attaching one instead would produce all of it
1154
1157
  and write all of it back, so a cell nobody touched would go through the
1155
1158
  view's conversion both ways. */
1159
+ /* May two cells of this layout land on the same bytes? With the axes in
1160
+ order of stride, none can when each stride clears everything the smaller
1161
+ ones reach. The test is sufficient, not exact: a layout that interleaves
1162
+ its axes answers yes and takes the longer way. */
1163
+ static int
1164
+ ca_layout_cells_may_overlap (int8_t ndim, const ca_size_t *dim,
1165
+ const ca_size_t *strides, ca_size_t bytes)
1166
+ {
1167
+ ca_size_t s[CA_RANK_MAX], d[CA_RANK_MAX];
1168
+ ca_size_t reach = bytes;
1169
+ int n = 0;
1170
+ int8_t k;
1171
+ int i, j;
1172
+
1173
+ for (k = 0; k < ndim; k++) {
1174
+ if ( dim[k] > 1 ) {
1175
+ ca_size_t a = strides[k] < 0 ? -strides[k] : strides[k];
1176
+ for (i = n; i > 0 && s[i-1] > a; i--) {
1177
+ s[i] = s[i-1];
1178
+ d[i] = d[i-1];
1179
+ }
1180
+ s[i] = a;
1181
+ d[i] = dim[k];
1182
+ n++;
1183
+ }
1184
+ }
1185
+ for (j = 0; j < n; j++) {
1186
+ if ( s[j] < reach ) return 1;
1187
+ reach += s[j] * (d[j] - 1);
1188
+ }
1189
+ return 0;
1190
+ }
1191
+
1156
1192
  int
1157
1193
  ca_root_lends_no_memory (void *ap)
1158
1194
  {
@@ -1168,7 +1204,8 @@ ca_root_lends_no_memory (void *ap)
1168
1204
  ca_size_t cs[CA_RANK_MAX];
1169
1205
  ca_size_t base;
1170
1206
  ca_stride_compose_to_root((CAStride *) root, &below, cs, &base);
1171
- return ca_root_lends_no_memory(below);
1207
+ return ca_root_lends_no_memory(below)
1208
+ || ca_layout_cells_may_overlap(root->ndim, root->dim, cs, root->bytes);
1172
1209
  }
1173
1210
  return 1;
1174
1211
  }
@@ -1176,7 +1213,7 @@ ca_root_lends_no_memory (void *ap)
1176
1213
  /* The same question for the parent of a view that is not itself in the
1177
1214
  CAStride family (CAGrid, ...): whether the array its cells come from --
1178
1215
  the parent, or the root a CAStride parent folds to -- has no memory to
1179
- lend. Like ca_root_lends_no_memory it asks only what those arrays are,
1216
+ lend, or the parent has cells that share memory. Like ca_root_lends_no_memory it asks only what those arrays are,
1180
1217
  so the lifecycle slots can rely on getting the same answer from attach
1181
1218
  to detach. */
1182
1219
  int
@@ -1189,7 +1226,8 @@ ca_parent_lends_no_memory (void *ap)
1189
1226
  ca_size_t cs[CA_RANK_MAX];
1190
1227
  ca_size_t base;
1191
1228
  ca_stride_compose_to_root((CAStride *) parent, &root, cs, &base);
1192
- return ca_root_lends_no_memory(root);
1229
+ return ca_root_lends_no_memory(root)
1230
+ || ca_layout_cells_may_overlap(parent->ndim, parent->dim, cs, parent->bytes);
1193
1231
  }
1194
1232
  return ca_root_lends_no_memory(parent);
1195
1233
  }
@@ -1238,11 +1276,11 @@ ca_stride_func_allocate (void *ap)
1238
1276
  ca->ptr = ca_alloc_cells((CArray *) ca);
1239
1277
  return;
1240
1278
  }
1241
- ca_attach(root);
1242
1279
  if (ca_layout_is_contiguous(ca->ndim, ca->dim, composed_strides, ca->bytes)) {
1280
+ ca_attach(root);
1243
1281
  ca->ptr = root->ptr + composed_base;
1244
1282
  } else {
1245
- ca->ptr = ca_alloc_cells((CArray *) ca);
1283
+ ca->ptr = ca_alloc_cells_then_attach((CArray *) ca, 1, &root);
1246
1284
  }
1247
1285
  }
1248
1286
 
@@ -1277,11 +1315,11 @@ ca_stride_func_attach (void *ap)
1277
1315
  ca->ptr = buf;
1278
1316
  return;
1279
1317
  }
1280
- ca_attach(root);
1281
1318
  if (ca_layout_is_contiguous(ca->ndim, ca->dim, composed_strides, ca->bytes)) {
1319
+ ca_attach(root);
1282
1320
  ca->ptr = root->ptr + composed_base;
1283
1321
  } else {
1284
- ca->ptr = ca_alloc_cells((CArray *) ca);
1322
+ ca->ptr = ca_alloc_cells_then_attach((CArray *) ca, 1, &root);
1285
1323
  ca_stride_xfer_with_layout(ca, 0, root->ptr + composed_base, composed_strides);
1286
1324
  }
1287
1325
  }
data/ext/ca_obj_string.c CHANGED
@@ -212,8 +212,8 @@ ca_string_refuse_cell (VALUE v, const char *where)
212
212
  "UNDEF masks a cell)", where);
213
213
  }
214
214
  rb_raise(rb_eTypeError,
215
- "%s is of class %s, not a String (a CAString holds Strings; "
216
- "UNDEF masks a cell)", where, rb_obj_classname(v));
215
+ "%s is of class %"PRIsVALUE", not a String (a CAString holds Strings; "
216
+ "UNDEF masks a cell)", where, rb_obj_class(v));
217
217
  }
218
218
 
219
219
  /* Every cell of `parent` that is not masked is a String. Under a masked
data/ext/ca_obj_tile.c CHANGED
@@ -422,16 +422,14 @@ static void
422
422
  ca_tile_func_allocate (void *ap)
423
423
  {
424
424
  CATile *ca = (CATile *) ap;
425
- ca_attach(ca->parent);
426
- ca->ptr = ca_alloc_cells((CArray *) ca);
425
+ ca->ptr = ca_alloc_cells_then_attach((CArray *) ca, 1, &ca->parent);
427
426
  }
428
427
 
429
428
  static void
430
429
  ca_tile_func_attach (void *ap)
431
430
  {
432
431
  CATile *ca = (CATile *) ap;
433
- ca_attach(ca->parent);
434
- ca->ptr = ca_alloc_cells((CArray *) ca);
432
+ ca->ptr = ca_alloc_cells_then_attach((CArray *) ca, 1, &ca->parent);
435
433
  ca_tile_attach_into(ca, ca->ptr);
436
434
  }
437
435
 
data/ext/ca_obj_triop.c CHANGED
@@ -139,8 +139,8 @@ ca_triop_setup (CATriOp *ca, CArray *op1, CArray *op2, CArray *op3,
139
139
  ca->op2 = op2;
140
140
  ca->op3 = op3;
141
141
  ca->op_id = op_id;
142
- ca->op2_is_scalar = ( op2->elements == 1 && op1->elements > 1 ) ? 1 : 0;
143
- ca->op3_is_scalar = ( op3->elements == 1 && op1->elements > 1 ) ? 1 : 0;
142
+ ca->op2_is_scalar = ( op2->elements == 1 ) ? 1 : 0;
143
+ ca->op3_is_scalar = ( op3->elements == 1 ) ? 1 : 0;
144
144
  ca->operands[0] = op1;
145
145
  ca->operands[1] = op2;
146
146
  ca->operands[2] = op3;
@@ -503,8 +503,9 @@ insert_cast (VALUE cary, int8_t target_dt, CArray **ca_out)
503
503
  data_type differs from the common data_type, then resolves
504
504
  same-ndim size-1 broadcast pairwise against op1 (the walk anchor).
505
505
 
506
- Broadcast: each of op2 / op3 is aligned against op1 via
507
- ca_broadcast_pair. After alignment, each must have either the
506
+ Broadcast: the three are aligned pairwise via ca_broadcast_pair
507
+ (op1 / op2 again last, as two of them can meet at a shape smaller than
508
+ the third's). After alignment, op2 / op3 must each have either the
508
509
  same elements as op1 or be a 1-element CScalar (walked with
509
510
  element_step = 0 by the kernel). If op1 itself is a 1-element
510
511
  CScalar and any of op2 / op3 is an array, op1 is lifted via
@@ -535,17 +536,16 @@ rb_ca_triop_build (VALUE cary1, VALUE cary2, VALUE cary3, uint16_t op_id)
535
536
  /* Scalar operands (clip's Numeric bounds, a CScalar) are wrapped and
536
537
  promoted pairwise in the order the eager triop uses. */
537
538
  else if ( ! rb_obj_is_carray(r1) || ! rb_obj_is_carray(r2) || ! rb_obj_is_carray(r3) ||
538
- rb_obj_is_cscalar(r2) || rb_obj_is_cscalar(r3) ) {
539
+ rb_obj_is_cscalar(r1) || rb_obj_is_cscalar(r2) || rb_obj_is_cscalar(r3) ) {
539
540
  if ( ! rb_obj_is_carray(r1) ) {
540
541
  rb_raise(rb_eArgError,
541
542
  "CATriOp: the first operand must be a CArray");
542
543
  }
544
+ int r1_scalar = rb_obj_is_cscalar(r1);
543
545
  int r2_scalar = ! rb_obj_is_carray(r2) || rb_obj_is_cscalar(r2);
544
546
  int r3_scalar = ! rb_obj_is_carray(r3) || rb_obj_is_cscalar(r3);
545
- rb_ca_cast_self_or_other(&r1, &r2);
546
- rb_ca_cast_self_or_other(&r1, &r3);
547
- rb_ca_cast_self_or_other(&r2, &r3);
548
- rb_ca_cast_self_or_other(&r1, &r2);
547
+ rb_ca_cast_three(&r1, &r2, &r3);
548
+ r1 = ca_lazy_settle_scalar(r1, r1_scalar);
549
549
  r2 = ca_lazy_settle_scalar(r2, r2_scalar);
550
550
  r3 = ca_lazy_settle_scalar(r3, r3_scalar);
551
551
  }
@@ -609,12 +609,15 @@ rb_ca_triop_build (VALUE cary1, VALUE cary2, VALUE cary3, uint16_t op_id)
609
609
  }
610
610
  }
611
611
 
612
- /* Now align op2 / op3 against op1 for size-1 broadcast. */
612
+ /* Now bring the three to one shape for size-1 broadcast. Every pair is
613
+ aligned, and the first again after the others: op1 and op2 may meet
614
+ at a shape smaller than op3's ([1,4] [1,4] [3,4]). */
613
615
  ca_broadcast_pair(&r1, &r2);
614
- TypedData_Get_Struct(r1, CArray, &carray_data_type, op1);
615
- TypedData_Get_Struct(r2, CArray, &carray_data_type, op2);
616
616
  ca_broadcast_pair(&r1, &r3);
617
+ ca_broadcast_pair(&r2, &r3);
618
+ ca_broadcast_pair(&r1, &r2);
617
619
  TypedData_Get_Struct(r1, CArray, &carray_data_type, op1);
620
+ TypedData_Get_Struct(r2, CArray, &carray_data_type, op2);
618
621
  TypedData_Get_Struct(r3, CArray, &carray_data_type, op3);
619
622
 
620
623
  /* Step 3: each of op2 / op3 must match op1's element count OR be a
data/ext/ca_obj_window.c CHANGED
@@ -275,10 +275,11 @@ static int
275
275
  ca_window_func_fold_stride (void *ap, ca_fold_t *f, void **next_parent)
276
276
  {
277
277
  CAWindow *w = (CAWindow *) ap;
278
- ca_window_mask_current(w);
279
- /* A write folded through to a stand-in would bypass the settle in this
280
- window's write slots. */
281
- if ( ca_window_mask_reads_standin(w) ) {
278
+ /* A mask window never folds. A write folded through to a stand-in would
279
+ bypass the settle in this window's write slots, and answering by what
280
+ the window reads now would change when a write settles: a view attached
281
+ over this window would detach a root it never attached. */
282
+ if ( w->mask_owner ) {
282
283
  return 0;
283
284
  }
284
285
  ca_size_t synth_strides[CA_RANK_MAX];
@@ -701,7 +702,8 @@ ca_window_setup (CAWindow *ca, CArray *parent,
701
702
  /* Under "ruby" or "strict" an index outside the parent raises. The
702
703
  parent's shape is fixed, so a window that reaches outside it would raise
703
704
  on every whole read or write, and a write would have changed the cells
704
- before the one that raised. It is refused here instead. */
705
+ before the one that raised. It is refused here instead. Under
706
+ "nearest" an axis with no cells has no edge to replicate. */
705
707
  for (i=0; i<ndim; i++) {
706
708
  ca_size_t lo, hi, size = dim[i];
707
709
  if ( count[i] == 0 ) {
@@ -717,6 +719,9 @@ ca_window_setup (CAWindow *ca, CArray *parent,
717
719
  if ( lo < -size ) ca_index_out_of_range(lo, size, "index");
718
720
  if ( hi >= size ) ca_index_out_of_range(hi, size, "index");
719
721
  }
722
+ else if ( bounds[i] == CA_BOUNDS_NEAREST && size == 0 ) {
723
+ ca_index_out_of_range(lo, size, "index");
724
+ }
720
725
  }
721
726
 
722
727
  /* ca == NULL asks for the checks above only, so that a constructor can
@@ -1114,12 +1119,12 @@ ca_window_func_allocate (void *ap)
1114
1119
  ca->ptr = ca_alloc_cells((CArray *) ca);
1115
1120
  return;
1116
1121
  }
1117
- ca_attach(ca->parent);
1118
1122
  if ( ca->embed_alias_eligible ) {
1123
+ ca_attach(ca->parent);
1119
1124
  ca->ptr = ca_window_alias_ptr(ca);
1120
1125
  return;
1121
1126
  }
1122
- ca->ptr = ca_alloc_cells((CArray *) ca);
1127
+ ca->ptr = ca_alloc_cells_then_attach((CArray *) ca, 1, &ca->parent);
1123
1128
  }
1124
1129
 
1125
1130
  /* The engine paths below emit a per-axis descriptor (ca_window_describe_axes)
@@ -1139,7 +1144,6 @@ ca_window_func_attach (void *ap)
1139
1144
  ca_axis_view_attach_owned(ca);
1140
1145
  return;
1141
1146
  }
1142
- ca_attach(ca->parent);
1143
1147
  if ( ca->embed_alias_eligible ) {
1144
1148
  /* Alias path: inner axes full + interior, so the embedded region is a
1145
1149
  contiguous run of parent storage. Skip malloc/memcpy and point
@@ -1147,10 +1151,13 @@ ca_window_func_attach (void *ap)
1147
1151
  CAREFUL: sync and detach must agree with this — sync has nothing to
1148
1152
  scatter back (the writes already landed in the parent) and detach must
1149
1153
  not xfree a pointer it does not own. */
1154
+ ca_attach(ca->parent);
1150
1155
  ca->ptr = ca_window_alias_ptr(ca);
1151
- } else if ( ca->embed_eligible ) {
1152
- /* Embed path: allocate, then 1 fill + 1 strided memcpy. */
1153
- ca->ptr = ca_alloc_cells((CArray *) ca);
1156
+ return;
1157
+ }
1158
+ ca->ptr = ca_alloc_cells_then_attach((CArray *) ca, 1, &ca->parent);
1159
+ if ( ca->embed_eligible ) {
1160
+ /* Embed path: 1 fill + 1 strided memcpy. */
1154
1161
  ca_window_attach_embed(ca, ca->ptr);
1155
1162
  } else {
1156
1163
  /* Fallback: PERIODIC / REFLECT / NEAREST / RUBY / STRICT go through
@@ -1158,8 +1165,8 @@ ca_window_func_attach (void *ap)
1158
1165
  ca_axis_desc_t desc[CA_RANK_MAX];
1159
1166
  ca_size_t pdims[CA_RANK_MAX];
1160
1167
  ca_window_describe_axes(ca, desc, pdims);
1161
- ca->ptr = ca_axis_dispatch_attach(ca->parent, pdims, desc, ca->ndim,
1162
- ca->bytes, ca->elements, ca->fill);
1168
+ ca_axis_dispatch_gather(ca->parent, pdims, desc, ca->ndim,
1169
+ ca->bytes, ca->elements, ca->fill, ca->ptr);
1163
1170
  }
1164
1171
  }
1165
1172
 
@@ -1700,8 +1707,8 @@ rb_ca_window (int argc, VALUE *argv, VALUE self)
1700
1707
  }
1701
1708
  if ( ! RB_TYPE_P(rbounds, T_STRING) ) {
1702
1709
  rb_raise(rb_eTypeError,
1703
- "window: bounds: must be a Symbol or a String (got %s)",
1704
- rb_obj_classname(rbounds));
1710
+ "window: bounds: must be a Symbol or a String (got %"PRIsVALUE")",
1711
+ rb_obj_class(rbounds));
1705
1712
  }
1706
1713
  cbounds = StringValueCStr(rbounds);
1707
1714
  if ( ! strcmp(cbounds, "ruby") ) {
data/ext/ca_op_ipower.c CHANGED
@@ -1,7 +1,10 @@
1
1
  /* ---------------------------------------------------------------------------
2
2
 
3
- Float/Complex ** Integer integer-exponent fast path via
4
- binary exponentiation (O(log p)).
3
+ Float/Complex ** Integer: the exponent stays an int64 (no cast of the
4
+ array to a wider type), and each cell is computed exactly as `a ** b`
5
+ computes it when b is an array of that integer: the exponents -2 .. 4
6
+ by at most two multiplications or divisions, the rest by pow / powf /
7
+ cpow.
5
8
 
6
9
  Contents:
7
10
  (1) LAZY kernel table `ca_binop_ipower[CA_NTYPE]` — CABinOp dispatch
@@ -11,14 +14,59 @@
11
14
  `rb_ca_pow{,_bang}` dispatch between ipower (Float/Complex **
12
15
  Integer) and mkkernel-generated `rb_ca_power` (general pow).
13
16
 
14
- Both paths share `op_powi_<type>` from ca_op_powi.h (= repeated
15
- squaring, ~1-3 mul/cell for typical p).
17
+ Both paths share `ca_ipow_<type>` below. The exponent is converted
18
+ the way an int64 cell is converted to the base's type (through double
19
+ for float32 / cmplx64), so the answer is the one the general power
20
+ kernel gives for the same exponent held in an array.
16
21
 
17
22
  ---------------------------------------------------------------------------- */
18
23
 
19
24
  #include "carray.h"
20
25
  #include "ca_binop_dispatch.h"
21
- #include "ca_op_powi.h"
26
+ #include <math.h>
27
+ #include <complex.h>
28
+
29
+ /* The exponents -2 .. 4 take at most two roundings, as in the power
30
+ kernel (MkKernel.pow_short). */
31
+ #define CA_IPOW_SHORT(x, p) do { \
32
+ switch ( p ) { \
33
+ case 2: return (x) * (x); \
34
+ case 3: return (x) * (x) * (x); \
35
+ case 4: return ((x) * (x)) * ((x) * (x)); \
36
+ case 1: return (x); \
37
+ case 0: return 1; \
38
+ case -1: return 1 / (x); \
39
+ case -2: return 1 / ((x) * (x)); \
40
+ } \
41
+ } while (0)
42
+
43
+ static inline float32_t
44
+ ca_ipow_float32_t (float32_t x, int64_t p)
45
+ {
46
+ CA_IPOW_SHORT(x, p);
47
+ return powf(x, (float) (double) p);
48
+ }
49
+
50
+ static inline float64_t
51
+ ca_ipow_float64_t (float64_t x, int64_t p)
52
+ {
53
+ CA_IPOW_SHORT(x, p);
54
+ return pow(x, (double) p);
55
+ }
56
+
57
+ static inline cmplx64_t
58
+ ca_ipow_cmplx64_t (cmplx64_t x, int64_t p)
59
+ {
60
+ CA_IPOW_SHORT(x, p);
61
+ return cpow(x, (float) (double) p);
62
+ }
63
+
64
+ static inline cmplx128_t
65
+ ca_ipow_cmplx128_t (cmplx128_t x, int64_t p)
66
+ {
67
+ CA_IPOW_SHORT(x, p);
68
+ return cpow(x, (double) p);
69
+ }
22
70
 
23
71
  /* Defined in ext/carray_kernels.c (mkkernel-generated general pow /
24
72
  cpow binop). Called by rb_ca_pow{,_bang} when the fast path does
@@ -71,7 +119,7 @@ ca_binop_ipower_##T (ca_size_t n, boolean8_t *m, \
71
119
  p1 = q1 + k*i1; \
72
120
  p2 = q2 + k*i2; \
73
121
  p3 = q3 + k*i3; \
74
- *p3 = op_powi_##T(*p1, *p2); \
122
+ *p3 = ca_ipow_##T(*p1, *p2); \
75
123
  } \
76
124
  } \
77
125
  } else { \
@@ -79,7 +127,7 @@ ca_binop_ipower_##T (ca_size_t n, boolean8_t *m, \
79
127
  p1 = q1 + k*i1; \
80
128
  p2 = q2 + k*i2; \
81
129
  p3 = q3 + k*i3; \
82
- *p3 = op_powi_##T(*p1, *p2); \
130
+ *p3 = ca_ipow_##T(*p1, *p2); \
83
131
  } \
84
132
  } \
85
133
  }
@@ -115,12 +163,12 @@ ca_ipower_float32_t (ca_size_t n, boolean8_t *m,
115
163
  ca_u_float32_t *p1 = (ca_u_float32_t *) ptr1, *p2 = (ca_u_float32_t *) ptr2;
116
164
  if ( m ) {
117
165
  while (n--) {
118
- if ( ! *m++ ) { *p2 = op_powi_float32_t(*p1, ipow); }
166
+ if ( ! *m++ ) { *p2 = ca_ipow_float32_t(*p1, ipow); }
119
167
  p1++; p2++;
120
168
  }
121
169
  }
122
170
  else
123
- while (n--) { (*p2) = op_powi_float32_t(*p1, ipow); p1++; p2++; }
171
+ while (n--) { (*p2) = ca_ipow_float32_t(*p1, ipow); p1++; p2++; }
124
172
  }
125
173
 
126
174
  static void
@@ -130,12 +178,12 @@ ca_ipower_float64_t (ca_size_t n, boolean8_t *m,
130
178
  ca_u_float64_t *p1 = (ca_u_float64_t *) ptr1, *p2 = (ca_u_float64_t *) ptr2;
131
179
  if ( m ) {
132
180
  while (n--) {
133
- if ( ! *m++ ) { *p2 = op_powi_float64_t(*p1, ipow); }
181
+ if ( ! *m++ ) { *p2 = ca_ipow_float64_t(*p1, ipow); }
134
182
  p1++; p2++;
135
183
  }
136
184
  }
137
185
  else
138
- while (n--) { (*p2) = op_powi_float64_t(*p1, ipow); p1++; p2++; }
186
+ while (n--) { (*p2) = ca_ipow_float64_t(*p1, ipow); p1++; p2++; }
139
187
  }
140
188
 
141
189
  static void
@@ -145,12 +193,12 @@ ca_ipower_cmplx64_t (ca_size_t n, boolean8_t *m,
145
193
  ca_u_cmplx64_t *p1 = (ca_u_cmplx64_t *) ptr1, *p2 = (ca_u_cmplx64_t *) ptr2;
146
194
  if ( m ) {
147
195
  while (n--) {
148
- if ( ! *m++ ) { *p2 = op_powi_cmplx64_t(*p1, ipow); }
196
+ if ( ! *m++ ) { *p2 = ca_ipow_cmplx64_t(*p1, ipow); }
149
197
  p1++; p2++;
150
198
  }
151
199
  }
152
200
  else
153
- while (n--) { (*p2) = op_powi_cmplx64_t(*p1, ipow); p1++; p2++; }
201
+ while (n--) { (*p2) = ca_ipow_cmplx64_t(*p1, ipow); p1++; p2++; }
154
202
  }
155
203
 
156
204
  static void
@@ -160,12 +208,12 @@ ca_ipower_cmplx128_t (ca_size_t n, boolean8_t *m,
160
208
  ca_u_cmplx128_t *p1 = (ca_u_cmplx128_t *) ptr1, *p2 = (ca_u_cmplx128_t *) ptr2;
161
209
  if ( m ) {
162
210
  while (n--) {
163
- if ( ! *m++ ) { *p2 = op_powi_cmplx128_t(*p1, ipow); }
211
+ if ( ! *m++ ) { *p2 = ca_ipow_cmplx128_t(*p1, ipow); }
164
212
  p1++; p2++;
165
213
  }
166
214
  }
167
215
  else
168
- while (n--) { (*p2) = op_powi_cmplx128_t(*p1, ipow); p1++; p2++; }
216
+ while (n--) { (*p2) = ca_ipow_cmplx128_t(*p1, ipow); p1++; p2++; }
169
217
  }
170
218
 
171
219
  /* The data types the ipower kernels cover; checked before any window
data/ext/ca_op_powi.h CHANGED
@@ -1,19 +1,11 @@
1
1
  /* ---------------------------------------------------------------------------
2
2
 
3
- ca_op_powi.h -- shared `op_powi_<type>` integer-power helpers
3
+ ca_op_powi.h -- `op_powi_<type>`, an integer to an integer power
4
4
 
5
- Used by:
6
- - ext/carray_kernels.c (generated from ext/mkkernel.rb)
7
- the `power` binop kernel's integer variants call
8
- `op_powi_<type>` to compute integer exponentiation by binary
9
- exponentiation (O(log p) vs pow's general path).
10
- - ext/carray_math.c (hand-written, P.5b.5)
11
- the `ipower` family (rb_ca_ipower, rb_ca_ipower_bang) uses
12
- `op_powi_<type>` for the Float**Integer / Complex**Integer
13
- fast path when invoked via CArray#pow with an Integer rhs.
14
-
15
- Both files include this header independently; each TU gets its own
16
- `static inline` copy with no linker conflicts.
5
+ The `power` binop kernel's integer variants (generated from
6
+ ext/mkkernel.rb) call `op_powi_<type>`, binary exponentiation in the
7
+ integer type. A float or complex base goes through pow / cpow
8
+ instead (ext/ca_op_ipower.c for an Integer exponent).
17
9
 
18
10
  --------------------------------------------------------------------------- */
19
11
 
@@ -27,7 +19,7 @@
27
19
  an evaluator that compiles a kernel body calling op_powi_<type>
28
20
  (CArray.__kernel_helpers__, written into the build by mkkernel.rb). It
29
21
  may use only <stdint.h>, float32_t / float64_t and ca_zerodiv: what such
30
- an evaluator's C declares. The complex instantiations stay outside. */
22
+ an evaluator's C declares. */
31
23
 
32
24
  /* BEGIN kernel helpers */
33
25
 
@@ -85,17 +77,6 @@ op_powi_## type (type x, int64_t p) \
85
77
  return op_powi_mag_## type(x, (uint64_t) p); \
86
78
  }
87
79
 
88
- #define op_powi_fc(type) \
89
- op_powi_magnitude(type) \
90
- static inline type \
91
- op_powi_## type (type x, int64_t p) \
92
- { \
93
- if ( p < 0 ) { \
94
- return 1 / op_powi_mag_## type(x, (uint64_t) 0 - (uint64_t) p); \
95
- } \
96
- return op_powi_mag_## type(x, (uint64_t) p); \
97
- }
98
-
99
80
  op_powi(int8_t)
100
81
  op_powi_u(uint8_t)
101
82
  op_powi(int16_t)
@@ -104,12 +85,8 @@ op_powi(int32_t)
104
85
  op_powi_u(uint32_t)
105
86
  op_powi(int64_t)
106
87
  op_powi_u(uint64_t)
107
- op_powi_fc(float32_t)
108
- op_powi_fc(float64_t)
109
88
 
110
89
  /* END kernel helpers */
111
90
 
112
- op_powi_fc(cmplx64_t)
113
- op_powi_fc(cmplx128_t)
114
91
 
115
92
  #endif /* CA_OP_POWI_H */
@@ -61,6 +61,9 @@ void ca_sort_merge_f64 (double *a, double *aux, ca_size_t n);
61
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
+ /* The same, keeping the order within both regions; scratch holds n values. */
65
+ ca_size_t ca_partition_nan_stable_f32 (float32_t *a, ca_size_t n, float32_t *scratch);
66
+ ca_size_t ca_partition_nan_stable_f64 (double *a, ca_size_t n, double *scratch);
64
67
 
65
68
  /* Pair variant of NaN pre-partition for float argsort kernels. scratch
66
69
  holds n pairs; its contents on return are unspecified. */