carray 3.0.4 → 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 (111) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +205 -0
  3. data/carray.gemspec +1 -1
  4. data/ext/ca_array_pool.c +25 -1
  5. data/ext/ca_axis_dispatch.c +13 -20
  6. data/ext/ca_categorical_iterator.c +27 -20
  7. data/ext/ca_compare.c +4 -4
  8. data/ext/ca_composite_dispatch.c +9 -9
  9. data/ext/ca_kernel_iterator.c +6 -6
  10. data/ext/ca_kernel_iterator.h +112 -83
  11. data/ext/ca_obj_array.c +69 -19
  12. data/ext/ca_obj_bincmp.c +1 -0
  13. data/ext/ca_obj_binop.c +1 -0
  14. data/ext/ca_obj_bitarray.c +5 -1
  15. data/ext/ca_obj_bitfield.c +58 -43
  16. data/ext/ca_obj_byte_swap.c +2 -1
  17. data/ext/ca_obj_const_string.c +15 -3
  18. data/ext/ca_obj_face.c +42 -1
  19. data/ext/ca_obj_field.c +13 -3
  20. data/ext/ca_obj_grid.c +52 -5
  21. data/ext/ca_obj_monop.c +46 -10
  22. data/ext/ca_obj_object.c +64 -12
  23. data/ext/ca_obj_record.c +3 -0
  24. data/ext/ca_obj_reduce.c +4 -0
  25. data/ext/ca_obj_refer.c +18 -9
  26. data/ext/ca_obj_roll.c +26 -6
  27. data/ext/ca_obj_select.c +34 -5
  28. data/ext/ca_obj_shift.c +31 -7
  29. data/ext/ca_obj_stack.c +1 -0
  30. data/ext/ca_obj_stride.c +28 -19
  31. data/ext/ca_obj_string.c +11 -2
  32. data/ext/ca_obj_tile.c +27 -7
  33. data/ext/ca_obj_transpose.c +8 -4
  34. data/ext/ca_obj_triop.c +3 -0
  35. data/ext/ca_obj_window.c +79 -50
  36. data/ext/ca_op_ipower.c +4 -4
  37. data/ext/ca_sort_kernels.h +6 -3
  38. data/ext/carray.h +33 -0
  39. data/ext/carray_access.c +16 -3
  40. data/ext/carray_attribute.c +24 -0
  41. data/ext/carray_cast.c +243 -82
  42. data/ext/carray_cast_func.rb +56 -26
  43. data/ext/carray_conversion.c +2 -2
  44. data/ext/carray_core.c +3 -0
  45. data/ext/carray_count.c +6 -0
  46. data/ext/carray_element.c +9 -4
  47. data/ext/carray_factorize.c +20 -20
  48. data/ext/carray_generate.c +8 -2
  49. data/ext/carray_histogram.c +1 -1
  50. data/ext/carray_index_classifier.c +4 -4
  51. data/ext/carray_internal.h +12 -0
  52. data/ext/carray_kernels_bincmp.c +168 -168
  53. data/ext/carray_kernels_binop.c +900 -900
  54. data/ext/carray_kernels_init.c +4 -4
  55. data/ext/carray_kernels_moncmp.c +96 -96
  56. data/ext/carray_kernels_monop.c +1984 -1984
  57. data/ext/carray_kernels_reduce_aggregate.c +1733 -1733
  58. data/ext/carray_kernels_reduce_cumulative.c +642 -642
  59. data/ext/carray_kernels_reduce_extreme.c +1150 -1150
  60. data/ext/carray_kernels_reduce_variance.c +258 -258
  61. data/ext/carray_kernels_scan.c +128 -128
  62. data/ext/carray_kernels_search.c +340 -340
  63. data/ext/carray_kernels_sort.c +295 -304
  64. data/ext/carray_kernels_triop.c +114 -114
  65. data/ext/carray_key_windows.c +4 -0
  66. data/ext/carray_mask.c +45 -22
  67. data/ext/carray_memory_view.c +2 -2
  68. data/ext/carray_operator.c +5 -5
  69. data/ext/carray_order.c +227 -8
  70. data/ext/carray_random.c +18 -18
  71. data/ext/carray_scatter.c +24 -7
  72. data/ext/carray_slab.c +32 -9
  73. data/ext/carray_sort.c +224 -16
  74. data/ext/carray_sort_kernel.c +5 -7
  75. data/ext/carray_struct.c +35 -43
  76. data/ext/carray_time_text.c +4 -2
  77. data/ext/carray_utils.c +63 -21
  78. data/ext/mkkernel.rb +210 -115
  79. data/ext/version.h +4 -4
  80. data/lib/carray/attribute.rb +17 -4
  81. data/lib/carray/basics.rb +27 -7
  82. data/lib/carray/categorical.rb +30 -16
  83. data/lib/carray/categorical_iterator.rb +6 -6
  84. data/lib/carray/const_string.rb +10 -0
  85. data/lib/carray/data_type_extension.rb +8 -1
  86. data/lib/carray/fixlen_string.rb +41 -2
  87. data/lib/carray/frame/column_verbs.rb +4 -3
  88. data/lib/carray/frame/concat.rb +17 -3
  89. data/lib/carray/frame/frame.rb +6 -7
  90. data/lib/carray/frame/group.rb +100 -13
  91. data/lib/carray/frame/io.rb +3 -1
  92. data/lib/carray/fuse_source.rb +31 -2
  93. data/lib/carray/lazy.rb +35 -0
  94. data/lib/carray/methods/is_in.rb +8 -1
  95. data/lib/carray/segment_iterator.rb +12 -8
  96. data/lib/carray/serialize.rb +19 -2
  97. data/lib/carray/stack.rb +15 -12
  98. data/lib/carray/string.rb +8 -0
  99. data/lib/carray/string_operation_extension.rb +13 -5
  100. data/lib/carray/struct_builder.rb +5 -3
  101. data/lib/carray/time.rb +107 -68
  102. data/yard-stubs/ca_obj_transpose.rb +5 -4
  103. data/yard-stubs/carray_attribute.rb +3 -1
  104. data/yard-stubs/carray_cast.rb +2 -0
  105. data/yard-stubs/carray_conversion.rb +5 -1
  106. data/yard-stubs/carray_element.rb +7 -6
  107. data/yard-stubs/carray_mask.rb +8 -3
  108. data/yard-stubs/carray_math.rb +14 -4
  109. data/yard-stubs/carray_order.rb +100 -4
  110. data/yard-stubs/carray_scatter.rb +7 -5
  111. metadata +1 -1
@@ -1410,7 +1410,7 @@ ca_mv_wrap_body (VALUE arg)
1410
1410
  a->target_class);
1411
1411
  rb_ivar_set(wrap, rb_intern("memory_view_source"), a->obj);
1412
1412
  rb_ivar_set(wrap, rb_intern("memory_view_holder"), a->data_holder_obj);
1413
- ca_mv_extract(wrap, &ca);
1413
+ GetCArray(wrap, ca);
1414
1414
  if ( v->readonly ) {
1415
1415
  ca_set_flag(ca, CA_FLAG_READ_ONLY);
1416
1416
  }
@@ -1527,7 +1527,7 @@ ca_mv_from_body (VALUE arg)
1527
1527
 
1528
1528
  result = rb_carray_new(data_type, d->ndim, d->dim,
1529
1529
  ca_mv_carray_bytes_for(data_type, v->item_size), NULL);
1530
- ca_mv_extract(result, &ca);
1530
+ GetCArray(result, ca);
1531
1531
 
1532
1532
  if ( d->contiguous ) {
1533
1533
  memcpy(ca->ptr, v->data, (size_t) ca_length(ca));
@@ -1473,7 +1473,7 @@ rb_ca_call_binop (volatile VALUE self, volatile VALUE other,
1473
1473
  /* 1-shot ALLOCV path. At most 1 operand needs ALLOCV; the other is
1474
1474
  alias-direct. */
1475
1475
  volatile VALUE h1 = Qnil, h2 = Qnil;
1476
- char *p1, *p2;
1476
+ char *p1 = NULL, *p2 = NULL;
1477
1477
  int a1 = 0, a2 = 0;
1478
1478
  (void) h1; (void) h2;
1479
1479
 
@@ -1841,7 +1841,7 @@ rb_ca_call_triop (VALUE self, VALUE other2, VALUE other3,
1841
1841
 
1842
1842
  if ( !use_chunked ) {
1843
1843
  volatile VALUE h1 = Qnil, h2 = Qnil, h3 = Qnil;
1844
- char *p1, *p2, *p3;
1844
+ char *p1 = NULL, *p2 = NULL, *p3 = NULL;
1845
1845
  int attached1, attached2, attached3;
1846
1846
  (void) h1; (void) h2; (void) h3;
1847
1847
 
@@ -1984,7 +1984,7 @@ rb_ca_call_triop_bang (VALUE self, VALUE other2, VALUE other3,
1984
1984
 
1985
1985
  if ( !use_chunked ) {
1986
1986
  volatile VALUE h2 = Qnil, h3 = Qnil;
1987
- char *p2, *p3;
1987
+ char *p2 = NULL, *p3 = NULL;
1988
1988
  int attached2, attached3;
1989
1989
  ca_triop_call_t k;
1990
1990
  CArray *held[3];
@@ -2129,7 +2129,7 @@ rb_ca_call_moncmp (VALUE self, ca_moncmp_func_t func[])
2129
2129
 
2130
2130
  {
2131
2131
  volatile VALUE h1 = Qnil;
2132
- char *p1;
2132
+ char *p1 = NULL;
2133
2133
  int attached1;
2134
2134
  (void) h1;
2135
2135
 
@@ -2452,7 +2452,7 @@ rb_ca_call_bincmp (volatile VALUE self, volatile VALUE other,
2452
2452
 
2453
2453
  if ( !use_chunked ) {
2454
2454
  volatile VALUE h1 = Qnil, h2 = Qnil;
2455
- char *p1, *p2;
2455
+ char *p1 = NULL, *p2 = NULL;
2456
2456
  int attached1, attached2;
2457
2457
  (void) h1; (void) h2;
2458
2458
 
data/ext/carray_order.c CHANGED
@@ -242,7 +242,13 @@ rb_ca_project (int argc, VALUE *argv, VALUE self)
242
242
  char *lfval, *ufval;
243
243
  int self_is_face;
244
244
 
245
- rb_scan_args(argc, argv, "12", (VALUE *)&ridx, (VALUE *) &vlfval, (VALUE *) &vufval);
245
+ {
246
+ VALUE ropt = Qnil;
247
+ /* project takes no keyword: one given was read as a fill value. */
248
+ rb_scan_args(argc, argv, "12:", (VALUE *)&ridx, (VALUE *) &vlfval,
249
+ (VALUE *) &vufval, &ropt);
250
+ rb_scan_options(ropt, "");
251
+ }
246
252
 
247
253
  TypedData_Get_Struct(self, CArray, &carray_data_type, ca);
248
254
  self_is_face = ca_is_face(ca);
@@ -721,13 +727,14 @@ rb_ca_range_method (VALUE self)
721
727
 
722
728
  /* ---- nlargest / nsmallest family ------------------------------------- */
723
729
 
724
- /* The partition picks the right values but, among cells that tie with the
725
- last one picked, any of them. The answer is the cells first in the
726
- order (value, then NaN, then masked; the values descending for the
727
- largest) with ties broken by position, as `order` breaks them. So the
728
- tied candidates are replaced by the earliest cells that tie, and the
729
- candidates are sorted by (order, position): O(n) per fiber besides the
730
- sort of the candidates. */
730
+ /* The answer is the cells first in the order (value, then NaN, then
731
+ masked; the values descending for the largest) with ties broken by
732
+ position, as `order` breaks them. Fewer cells than the fiber are found
733
+ by the buffered walk below. The other routes (the whole fiber, and the
734
+ keys the walk does not read) sort or partition every cell, which picks
735
+ the right values but, among cells that tie with the last one picked,
736
+ any of them; ca_topk_stable replaces the tied candidates by the
737
+ earliest cells that tie and sorts the candidates by (order, position). */
731
738
 
732
739
  typedef struct {
733
740
  const char *v; /* the fiber's values, contiguous */
@@ -936,6 +943,206 @@ ca_topk_has_cells_after_numbers (VALUE self, CArray *ca)
936
943
  return 0;
937
944
  }
938
945
 
946
+ /* ---- the buffered walk ------------------------------------------------
947
+ Each fiber is read once. The cells that come before the cut (the last
948
+ of the cap cells kept so far, in (order, position)) are collected in a
949
+ buffer of a few times cap; when it fills, a selection keeps the first
950
+ cap and the cut moves up. The work area is the buffer, whatever the
951
+ length of the fiber, and a fiber in order costs one selection per
952
+ bufferful instead of a heap update per cell.
953
+
954
+ The walk is written once as a macro over three others: CLASS(i) (0 a
955
+ value, 1 NaN, 2 masked), AFTER(i, j) (cell i comes after cell j in
956
+ (order, position)) and ENTERS(j) (cell j comes before the cut), and
957
+ instantiated per type so that the comparisons
958
+ are inline. The fixlen type takes the generic comparison. */
959
+
960
+ /* Rearranges b[0..nb) so that b[0..cap) hold the cap cells first in
961
+ (order, position): quickselect with median-of-three pivots; past a
962
+ depth limit the remaining range is sorted (tmp holds nb positions). */
963
+ #define CA_TOPK_SELECT(AFTER) \
964
+ do { \
965
+ lo = 0; hi = nb; \
966
+ depth = 2; \
967
+ for (s = nb; s > 1; s >>= 1) depth += 2; \
968
+ while ( hi - lo > 16 ) { \
969
+ mid = lo + ( hi - lo ) / 2; last = hi - 1; \
970
+ if ( depth-- == 0 ) break; \
971
+ if ( AFTER(b[lo], b[mid]) ) { t = b[lo]; b[lo] = b[mid]; b[mid] = t; } \
972
+ if ( AFTER(b[mid], b[last]) ) { t = b[mid]; b[mid] = b[last]; b[last] = t; } \
973
+ if ( AFTER(b[lo], b[mid]) ) { t = b[lo]; b[lo] = b[mid]; b[mid] = t; } \
974
+ t = b[mid]; b[mid] = b[last]; b[last] = t; \
975
+ for (s = lo, i = lo; i < last; i++) { \
976
+ if ( AFTER(b[last], b[i]) ) { t = b[s]; b[s] = b[i]; b[i] = t; s++; } \
977
+ } \
978
+ t = b[s]; b[s] = b[last]; b[last] = t; \
979
+ if ( s == cap || s + 1 == cap ) { lo = hi; break; } \
980
+ if ( cap < s ) hi = s; \
981
+ else lo = s + 1; \
982
+ } \
983
+ if ( hi > lo ) ca_topk_sort(f, b + lo, tmp, hi - lo); \
984
+ } while (0)
985
+
986
+ #define CA_TOPK_DEFINE_BUF(NAME, T, CLASS, AFTER, ENTERS) \
987
+ static void \
988
+ NAME (const ca_topk_fiber_t *f, ca_size_t len, ca_size_t cap, \
989
+ ca_size_t *b, ca_size_t *tmp, ca_size_t size) \
990
+ { \
991
+ const T *v_ = (const T *) f->v; \
992
+ ca_size_t j, nb = 0, cut = 0, lo, hi, i, s, t, mid, last; \
993
+ int rc_ = 3, depth; \
994
+ T rv_; \
995
+ (void) v_; \
996
+ memset(&rv_, 0, sizeof(rv_)); \
997
+ for (j = 0; j < len; j++) { \
998
+ if ( rc_ != 3 && ! ( ENTERS(j) ) ) continue; \
999
+ b[nb++] = j; \
1000
+ if ( nb == size ) { \
1001
+ CA_TOPK_SELECT(AFTER); \
1002
+ nb = cap; \
1003
+ } \
1004
+ else if ( ! ( nb == cap && rc_ == 3 ) ) { /* the first cap cells */ \
1005
+ continue; \
1006
+ } \
1007
+ cut = b[0]; \
1008
+ for (i = 1; i < cap; i++) if ( AFTER(b[i], cut) ) cut = b[i]; \
1009
+ rc_ = CLASS(cut); \
1010
+ if ( rc_ == 0 ) memcpy(&rv_, f->v + cut * f->bytes, sizeof(rv_)); \
1011
+ } \
1012
+ if ( nb > cap ) CA_TOPK_SELECT(AFTER); \
1013
+ ca_topk_sort(f, b, tmp, cap); \
1014
+ }
1015
+
1016
+ /* The typed comparisons. A cell enters when it comes before the cut;
1017
+ one that ties the cut does not, it lies later. */
1018
+ #define CA_TOPK_CLASS_INT(i) ( ( f->m && f->m[i] ) ? 2 : 0 )
1019
+ #define CA_TOPK_CLASS_FLT(i) ( ( f->m && f->m[i] ) ? 2 : ( isnan(v_[i]) ? 1 : 0 ) )
1020
+ #define CA_TOPK_AFTER_V(CLASS, i, j) \
1021
+ ( CLASS(i) != CLASS(j) ? CLASS(i) > CLASS(j) \
1022
+ : ( CLASS(i) == 0 && v_[i] != v_[j] ) \
1023
+ ? ( f->desc ? v_[i] < v_[j] : v_[i] > v_[j] ) \
1024
+ : (i) > (j) )
1025
+ #define CA_TOPK_AFTER_INT(i, j) CA_TOPK_AFTER_V(CA_TOPK_CLASS_INT, i, j)
1026
+ #define CA_TOPK_AFTER_FLT(i, j) CA_TOPK_AFTER_V(CA_TOPK_CLASS_FLT, i, j)
1027
+ #define CA_TOPK_ENTERS_V(CLASS, j) \
1028
+ ( CLASS(j) < rc_ || \
1029
+ ( CLASS(j) == 0 && rc_ == 0 && \
1030
+ ( f->desc ? v_[j] > rv_ : v_[j] < rv_ ) ) )
1031
+ #define CA_TOPK_ENTERS_INT(j) CA_TOPK_ENTERS_V(CA_TOPK_CLASS_INT, j)
1032
+ #define CA_TOPK_ENTERS_FLT(j) CA_TOPK_ENTERS_V(CA_TOPK_CLASS_FLT, j)
1033
+
1034
+ CA_TOPK_DEFINE_BUF(ca_topk_buf_u8, uint8_t, CA_TOPK_CLASS_INT, CA_TOPK_AFTER_INT, CA_TOPK_ENTERS_INT)
1035
+ CA_TOPK_DEFINE_BUF(ca_topk_buf_i8, int8_t, CA_TOPK_CLASS_INT, CA_TOPK_AFTER_INT, CA_TOPK_ENTERS_INT)
1036
+ CA_TOPK_DEFINE_BUF(ca_topk_buf_i16, int16_t, CA_TOPK_CLASS_INT, CA_TOPK_AFTER_INT, CA_TOPK_ENTERS_INT)
1037
+ CA_TOPK_DEFINE_BUF(ca_topk_buf_u16, uint16_t, CA_TOPK_CLASS_INT, CA_TOPK_AFTER_INT, CA_TOPK_ENTERS_INT)
1038
+ CA_TOPK_DEFINE_BUF(ca_topk_buf_i32, int32_t, CA_TOPK_CLASS_INT, CA_TOPK_AFTER_INT, CA_TOPK_ENTERS_INT)
1039
+ CA_TOPK_DEFINE_BUF(ca_topk_buf_u32, uint32_t, CA_TOPK_CLASS_INT, CA_TOPK_AFTER_INT, CA_TOPK_ENTERS_INT)
1040
+ CA_TOPK_DEFINE_BUF(ca_topk_buf_i64, int64_t, CA_TOPK_CLASS_INT, CA_TOPK_AFTER_INT, CA_TOPK_ENTERS_INT)
1041
+ CA_TOPK_DEFINE_BUF(ca_topk_buf_u64, uint64_t, CA_TOPK_CLASS_INT, CA_TOPK_AFTER_INT, CA_TOPK_ENTERS_INT)
1042
+ CA_TOPK_DEFINE_BUF(ca_topk_buf_f32, float, CA_TOPK_CLASS_FLT, CA_TOPK_AFTER_FLT, CA_TOPK_ENTERS_FLT)
1043
+ CA_TOPK_DEFINE_BUF(ca_topk_buf_f64, double, CA_TOPK_CLASS_FLT, CA_TOPK_AFTER_FLT, CA_TOPK_ENTERS_FLT)
1044
+
1045
+ /* fixlen: the generic comparison; v_ is unused. */
1046
+ #define CA_TOPK_CLASS_GEN(i) ca_topk_class(f, i)
1047
+ #define CA_TOPK_AFTER_GEN(i, j) ca_topk_after(f, i, j)
1048
+ #define CA_TOPK_ENTERS_GEN(j) ( ca_topk_cmp(f, j, cut) < 0 )
1049
+
1050
+ /* Whether cell i comes after cell j in (order, position). */
1051
+ static int
1052
+ ca_topk_after (const ca_topk_fiber_t *f, ca_size_t i, ca_size_t j)
1053
+ {
1054
+ int r = ca_topk_cmp(f, i, j);
1055
+ return r > 0 || ( r == 0 && i > j );
1056
+ }
1057
+
1058
+ CA_TOPK_DEFINE_BUF(ca_topk_buf_gen, char, CA_TOPK_CLASS_GEN, CA_TOPK_AFTER_GEN, CA_TOPK_ENTERS_GEN)
1059
+
1060
+ #undef CA_TOPK_DEFINE_BUF
1061
+ #undef CA_TOPK_SELECT
1062
+ #undef CA_TOPK_CLASS_INT
1063
+ #undef CA_TOPK_CLASS_FLT
1064
+ #undef CA_TOPK_AFTER_V
1065
+ #undef CA_TOPK_AFTER_INT
1066
+ #undef CA_TOPK_AFTER_FLT
1067
+ #undef CA_TOPK_ENTERS_V
1068
+ #undef CA_TOPK_ENTERS_INT
1069
+ #undef CA_TOPK_ENTERS_FLT
1070
+ #undef CA_TOPK_CLASS_GEN
1071
+ #undef CA_TOPK_AFTER_GEN
1072
+ #undef CA_TOPK_ENTERS_GEN
1073
+
1074
+ /* The first cap cells of one fiber in (order, position), sorted, into
1075
+ b[0..cap). b and tmp hold size positions, cap < size <= len. */
1076
+ static void
1077
+ ca_topk_buf_fiber (const ca_topk_fiber_t *f, ca_size_t len, ca_size_t cap,
1078
+ ca_size_t *b, ca_size_t *tmp, ca_size_t size)
1079
+ {
1080
+ switch ( f->type ) {
1081
+ case CA_BOOLEAN:
1082
+ case CA_UINT8: ca_topk_buf_u8(f, len, cap, b, tmp, size); break;
1083
+ case CA_INT8: ca_topk_buf_i8(f, len, cap, b, tmp, size); break;
1084
+ case CA_INT16: ca_topk_buf_i16(f, len, cap, b, tmp, size); break;
1085
+ case CA_UINT16: ca_topk_buf_u16(f, len, cap, b, tmp, size); break;
1086
+ case CA_INT32: ca_topk_buf_i32(f, len, cap, b, tmp, size); break;
1087
+ case CA_UINT32: ca_topk_buf_u32(f, len, cap, b, tmp, size); break;
1088
+ case CA_INT64: ca_topk_buf_i64(f, len, cap, b, tmp, size); break;
1089
+ case CA_UINT64: ca_topk_buf_u64(f, len, cap, b, tmp, size); break;
1090
+ case CA_FLOAT32: ca_topk_buf_f32(f, len, cap, b, tmp, size); break;
1091
+ case CA_FLOAT64: ca_topk_buf_f64(f, len, cap, b, tmp, size); break;
1092
+ default: ca_topk_buf_gen(f, len, cap, b, tmp, size); break;
1093
+ }
1094
+ }
1095
+
1096
+ /* Per-axis top-k positions of `key` (an order key: not object, not
1097
+ complex) by the buffered walk, for cap < the length of the axis. The
1098
+ fibers come in the row-major order of the other axes, so the answer is
1099
+ filled with the axis last and moved into place at the end (cap cells
1100
+ per fiber). */
1101
+ static VALUE
1102
+ ca_topk_buffered_positions (VALUE key, long cap, long axis, int desc)
1103
+ {
1104
+ volatile VALUE holder = 0, out;
1105
+ VALUE perm[CA_RANK_MAX];
1106
+ ca_size_t odim[CA_RANK_MAX];
1107
+ CArray *ck, *co;
1108
+ ca_iter_state st;
1109
+ ca_topk_fiber_t f;
1110
+ ca_size_t *buf, *tmp, n, o = 0, k, len, size;
1111
+ int64_t *op;
1112
+ boolean8_t *m;
1113
+ char *p;
1114
+ int ndim, i, j = 0;
1115
+
1116
+ GetCArray(key, ck);
1117
+ ndim = ck->ndim;
1118
+ for (i = 0; i < ndim; i++) if ( i != axis ) odim[j++] = ck->dim[i];
1119
+ odim[ndim - 1] = cap;
1120
+ len = ck->dim[axis];
1121
+ out = rb_carray_new(CA_INT64, ndim, odim, 0, NULL);
1122
+ GetCArray(out, co);
1123
+ op = (int64_t *) co->ptr;
1124
+
1125
+ f.type = ck->data_type;
1126
+ f.bytes = ck->bytes;
1127
+ f.desc = desc;
1128
+ size = ( len < 4 * cap + 256 ) ? len : 4 * cap + 256;
1129
+ buf = ALLOCV_N(ca_size_t, holder, 2 * size);
1130
+ tmp = buf + size;
1131
+ CA_FOR_EACH_FIBER_MASKED(st, ck, axis, 0, p, n, m) {
1132
+ f.v = p;
1133
+ f.m = m;
1134
+ ca_topk_buf_fiber(&f, n, cap, buf, tmp, size);
1135
+ for (k = 0; k < cap; k++) op[o * cap + k] = (int64_t) buf[k];
1136
+ o++;
1137
+ }
1138
+ ALLOCV_END(holder);
1139
+ if ( axis == ndim - 1 ) return out;
1140
+ for (i = 0; i < ndim; i++) {
1141
+ perm[i] = INT2FIX( i < axis ? i : ( i == axis ? ndim - 1 : i - 1 ) );
1142
+ }
1143
+ return rb_ca_copy(rb_funcallv(out, rb_intern("transpose"), ndim, perm));
1144
+ }
1145
+
939
1146
  /* Per-axis top-k positions (cap clamped, axis normalized).
940
1147
  * Called by rb_ca_topk_index below. */
941
1148
  static VALUE
@@ -946,6 +1153,18 @@ rb_ca_topk_positions (VALUE self, long cap, long axis_norm, int desc)
946
1153
  long dim_axis = (long) ca->dim[axis_norm];
947
1154
  VALUE raxis_norm = LONG2NUM(axis_norm);
948
1155
 
1156
+ /* The buffered walk reads any key but an object (its cells compare in
1157
+ Ruby) or a complex one (no order), and keeps cap cells per fiber. */
1158
+ if ( ca->data_type != CA_OBJECT && ! ca_is_complex_type(ca) ) {
1159
+ VALUE key = ca_order_key(self, desc ? "nlargest" : "nsmallest");
1160
+ CArray *ck;
1161
+ GetCArray(key, ck);
1162
+ if ( ck->data_type != CA_OBJECT && ! ca_is_complex_type(ck) &&
1163
+ cap < dim_axis ) {
1164
+ return ca_topk_buffered_positions(key, cap, axis_norm, desc);
1165
+ }
1166
+ }
1167
+
949
1168
  /* The largest: reversing the ascending order would put the NaN and the
950
1169
  masked cells first, so rank them descending (NaN, then the masked
951
1170
  cells, last) when there are any. An object array is ranked by
data/ext/carray_random.c CHANGED
@@ -165,7 +165,7 @@ ca_random_bang_fill (VALUE arg)
165
165
  ca_rng_bind(&c->source);
166
166
  switch (ca->data_type) {
167
167
  case CA_FLOAT64: {
168
- double *p = (double *)ca->ptr;
168
+ CA_U(double) *p = (CA_U(double) *)ca->ptr;
169
169
  if (is_default) {
170
170
  for (i = 0; i < n; i++)
171
171
  p[i] = ca_random_real(&c->source);
@@ -177,7 +177,7 @@ ca_random_bang_fill (VALUE arg)
177
177
  break;
178
178
  }
179
179
  case CA_FLOAT32: {
180
- float *p = (float *)ca->ptr;
180
+ CA_U(float) *p = (CA_U(float) *)ca->ptr;
181
181
  if (is_default) {
182
182
  for (i = 0; i < n; i++)
183
183
  p[i] = (float)ca_random_real(&c->source);
@@ -189,7 +189,7 @@ ca_random_bang_fill (VALUE arg)
189
189
  break;
190
190
  }
191
191
  case CA_CMPLX128: {
192
- double complex *p = (double complex *)ca->ptr;
192
+ ca_u_cmplx128_t *p = (ca_u_cmplx128_t *)ca->ptr;
193
193
  if (is_default) {
194
194
  for (i = 0; i < n; i++)
195
195
  p[i] = ca_random_real(&c->source) + ca_random_real(&c->source) * I;
@@ -202,7 +202,7 @@ ca_random_bang_fill (VALUE arg)
202
202
  break;
203
203
  }
204
204
  case CA_CMPLX64: {
205
- float complex *p = (float complex *)ca->ptr;
205
+ ca_u_cmplx64_t *p = (ca_u_cmplx64_t *)ca->ptr;
206
206
  if (is_default) {
207
207
  for (i = 0; i < n; i++)
208
208
  p[i] = (float)ca_random_real(&c->source) + (float)ca_random_real(&c->source) * I;
@@ -242,37 +242,37 @@ ca_random_bang_fill (VALUE arg)
242
242
  break;
243
243
  }
244
244
  case CA_INT16: {
245
- int16_t *p = (int16_t *)ca->ptr;
245
+ CA_U(int16_t) *p = (CA_U(int16_t) *)ca->ptr;
246
246
  for (i = 0; i < n; i++)
247
247
  p[i] = (int16_t)(low_long + ca_random_ulong_limited(&c->source, limit));
248
248
  break;
249
249
  }
250
250
  case CA_UINT16: {
251
- uint16_t *p = (uint16_t *)ca->ptr;
251
+ CA_U(uint16_t) *p = (CA_U(uint16_t) *)ca->ptr;
252
252
  for (i = 0; i < n; i++)
253
253
  p[i] = (uint16_t)(low_long + ca_random_ulong_limited(&c->source, limit));
254
254
  break;
255
255
  }
256
256
  case CA_INT32: {
257
- int32_t *p = (int32_t *)ca->ptr;
257
+ CA_U(int32_t) *p = (CA_U(int32_t) *)ca->ptr;
258
258
  for (i = 0; i < n; i++)
259
259
  p[i] = (int32_t)(low_long + ca_random_ulong_limited(&c->source, limit));
260
260
  break;
261
261
  }
262
262
  case CA_UINT32: {
263
- uint32_t *p = (uint32_t *)ca->ptr;
263
+ CA_U(uint32_t) *p = (CA_U(uint32_t) *)ca->ptr;
264
264
  for (i = 0; i < n; i++)
265
265
  p[i] = (uint32_t)(low_long + ca_random_ulong_limited(&c->source, limit));
266
266
  break;
267
267
  }
268
268
  case CA_INT64: {
269
- int64_t *p = (int64_t *)ca->ptr;
269
+ CA_U(int64_t) *p = (CA_U(int64_t) *)ca->ptr;
270
270
  for (i = 0; i < n; i++)
271
271
  p[i] = (int64_t)(low_long + ca_random_ulong_limited(&c->source, limit));
272
272
  break;
273
273
  }
274
274
  case CA_UINT64: {
275
- uint64_t *p = (uint64_t *)ca->ptr;
275
+ CA_U(uint64_t) *p = (CA_U(uint64_t) *)ca->ptr;
276
276
  for (i = 0; i < n; i++)
277
277
  p[i] = (uint64_t)(low_long + ca_random_ulong_limited(&c->source, limit));
278
278
  break;
@@ -483,19 +483,19 @@ ca_randomn_bang_fill (VALUE arg)
483
483
  if (c->source.kind == CA_RNG_OWN) {
484
484
  switch (ca->data_type) {
485
485
  case CA_FLOAT64: {
486
- double *p = (double *)ca->ptr;
486
+ CA_U(double) *p = (CA_U(double) *)ca->ptr;
487
487
  for (i = 0; i < n; i++)
488
488
  p[i] = ca_random_normal(&c->source);
489
489
  break;
490
490
  }
491
491
  case CA_FLOAT32: {
492
- float *p = (float *)ca->ptr;
492
+ CA_U(float) *p = (CA_U(float) *)ca->ptr;
493
493
  for (i = 0; i < n; i++)
494
494
  p[i] = (float) ca_random_normal(&c->source);
495
495
  break;
496
496
  }
497
497
  case CA_CMPLX128: {
498
- double complex *p = (double complex *)ca->ptr;
498
+ ca_u_cmplx128_t *p = (ca_u_cmplx128_t *)ca->ptr;
499
499
  for (i = 0; i < n; i++) {
500
500
  /* Into locals first: C does not say which order a call's
501
501
  arguments are evaluated in, and these two advance a state. */
@@ -506,7 +506,7 @@ ca_randomn_bang_fill (VALUE arg)
506
506
  break;
507
507
  }
508
508
  case CA_CMPLX64: {
509
- float complex *p = (float complex *)ca->ptr;
509
+ ca_u_cmplx64_t *p = (ca_u_cmplx64_t *)ca->ptr;
510
510
  for (i = 0; i < n; i++) {
511
511
  double re = ca_random_normal(&c->source);
512
512
  double im = ca_random_normal(&c->source);
@@ -522,7 +522,7 @@ ca_randomn_bang_fill (VALUE arg)
522
522
 
523
523
  switch (ca->data_type) {
524
524
  case CA_FLOAT64: {
525
- double *p = (double *)ca->ptr;
525
+ CA_U(double) *p = (CA_U(double) *)ca->ptr;
526
526
  ca_size_t pairs = n / 2;
527
527
  for (i = 0; i < pairs; i++) {
528
528
  box_muller_pair(&c->source, &p[2*i], &p[2*i+1]);
@@ -535,7 +535,7 @@ ca_randomn_bang_fill (VALUE arg)
535
535
  break;
536
536
  }
537
537
  case CA_FLOAT32: {
538
- float *p = (float *)ca->ptr;
538
+ CA_U(float) *p = (CA_U(float) *)ca->ptr;
539
539
  ca_size_t pairs = n / 2;
540
540
  for (i = 0; i < pairs; i++) {
541
541
  double r1, r2;
@@ -551,7 +551,7 @@ ca_randomn_bang_fill (VALUE arg)
551
551
  break;
552
552
  }
553
553
  case CA_CMPLX128: {
554
- double complex *p = (double complex *)ca->ptr;
554
+ ca_u_cmplx128_t *p = (ca_u_cmplx128_t *)ca->ptr;
555
555
  for (i = 0; i < n; i++) {
556
556
  double r1, r2;
557
557
  box_muller_pair(&c->source, &r1, &r2);
@@ -560,7 +560,7 @@ ca_randomn_bang_fill (VALUE arg)
560
560
  break;
561
561
  }
562
562
  case CA_CMPLX64: {
563
- float complex *p = (float complex *)ca->ptr;
563
+ ca_u_cmplx64_t *p = (ca_u_cmplx64_t *)ca->ptr;
564
564
  for (i = 0; i < n; i++) {
565
565
  double r1, r2;
566
566
  box_muller_pair(&c->source, &r1, &r2);
data/ext/carray_scatter.c CHANGED
@@ -13,7 +13,8 @@
13
13
  min / max: real numeric only (complex has no order)
14
14
  (boolean / object / fixlen → CADataTypeError; the bang
15
15
  cannot widen self, same rationale as fma! / fms!)
16
- replace: numeric or boolean (assignment, no widening)
16
+ replace: any type (assignment, no widening); an object or
17
+ fixlen array is written through the indexer
17
18
  scalar any Numeric, true / false, or UNDEF; converted to self's
18
19
  type as the indexer's store converts it (a value that does
19
20
  not fit raises, as `self[i] = v` does)
@@ -63,7 +64,7 @@ typedef union {
63
64
  #define LOOP_SCALAR(T, APPLY) LOOP_SCALAR_V(T, APPLY, SCALAR_REAL(T))
64
65
 
65
66
  #define LOOP_SCALAR_V(T, APPLY, VS) do { \
66
- T *q = (T *) qbase; \
67
+ CA_U(T) *q = (CA_U(T) *) qbase; \
67
68
  T vs = VS; \
68
69
  if ( mself ) { \
69
70
  for (i = 0; i < n; i++) { \
@@ -85,8 +86,8 @@ typedef union {
85
86
  } while (0)
86
87
 
87
88
  #define LOOP_VEC(T, APPLY) do { \
88
- T *q = (T *) qbase; \
89
- T *r = (T *) cv->ptr; \
89
+ CA_U(T) *q = (CA_U(T) *) qbase; \
90
+ CA_U(T) *r = (CA_U(T) *) cv->ptr; \
90
91
  if ( mself ) { \
91
92
  for (i = 0; i < n; i++) { \
92
93
  if ( maddrs && maddrs[i] ) continue; \
@@ -120,7 +121,7 @@ typedef union {
120
121
  #define LOOP_SCALAR_REPLACE(T) LOOP_SCALAR_REPLACE_V(T, SCALAR_REAL(T))
121
122
 
122
123
  #define LOOP_SCALAR_REPLACE_V(T, VS) do { \
123
- T *q = (T *) qbase; \
124
+ CA_U(T) *q = (CA_U(T) *) qbase; \
124
125
  T vs = VS; \
125
126
  for (i = 0; i < n; i++) { \
126
127
  if ( maddrs && maddrs[i] ) continue; \
@@ -136,8 +137,8 @@ typedef union {
136
137
  } while (0)
137
138
 
138
139
  #define LOOP_VEC_REPLACE(T) do { \
139
- T *q = (T *) qbase; \
140
- T *r = (T *) cv->ptr; \
140
+ CA_U(T) *q = (CA_U(T) *) qbase; \
141
+ CA_U(T) *r = (CA_U(T) *) cv->ptr; \
141
142
  for (i = 0; i < n; i++) { \
142
143
  if ( maddrs && maddrs[i] ) continue; \
143
144
  addr = p[i]; \
@@ -592,6 +593,22 @@ AT_BODY(ca_scatter_replace_body, DISPATCH_NUMERIC_REPLACE())
592
593
  static VALUE
593
594
  rb_ca_scatter_replace_bang (VALUE self, VALUE raddrs, VALUE rvals)
594
595
  {
596
+ /* An object or fixlen array takes the assignment through the indexer,
597
+ which reads and writes its cells by their own rules (a VALUE kept
598
+ visible to the GC, a fixlen value fitted to the cell); the kernel
599
+ below is for the numeric and boolean cells. */
600
+ {
601
+ CArray *cs;
602
+ TypedData_Get_Struct(self, CArray, &carray_data_type, cs);
603
+ if ( cs->data_type == CA_OBJECT || cs->data_type == CA_FIXLEN ) {
604
+ rb_ca_modify(self);
605
+ if ( RB_TYPE_P(raddrs, T_ARRAY) ) { /* a list of addresses */
606
+ raddrs = rb_ca_wrap_readonly(raddrs, INT2NUM(CA_SIZE));
607
+ }
608
+ rb_funcall(rb_ca_flatten(self), rb_intern("[]="), 2, raddrs, rvals);
609
+ return self;
610
+ }
611
+ }
595
612
  /* Pre-scan: if vals is a masked CArray and self isn't yet masked,
596
613
  promote self so the kernel can flip target cells to masked (indexer
597
614
  `self[addrs] = vals` establishes self.mask lazily the same way). */
data/ext/carray_slab.c CHANGED
@@ -346,6 +346,26 @@ ca_slab_build_output_view (int8_t data_type, ca_size_t bytes,
346
346
  return TypedData_Wrap_Struct(rb_cCAWrap, &cawrap_data_type, cw);
347
347
  }
348
348
 
349
+ /* The array the slab views move onto when the walk ends (see
350
+ slab_state_release_views), made with the carrier and held by it. It is
351
+ made here rather than at the end: the end runs in an ensure, after the
352
+ block, and an allocation failing there would leave the walk open and
353
+ the views pointing into freed scratch. */
354
+ static void
355
+ ca_slab_prepare_backing (ca_slab_iter_state_t *st)
356
+ {
357
+ CArray *cw, *backing;
358
+ volatile VALUE bv;
359
+ TypedData_Get_Struct(st->carrier, CArray, &carray_data_type, cw);
360
+ bv = rb_carray_new(cw->data_type, cw->ndim, cw->dim, cw->bytes, NULL);
361
+ TypedData_Get_Struct(bv, CArray, &carray_data_type, backing);
362
+ if ( cw->mask ) {
363
+ ca_create_mask(backing);
364
+ }
365
+ rb_ivar_set(st->carrier, rb_intern("__slab_backing__"), bv);
366
+ RB_GC_GUARD(bv);
367
+ }
368
+
349
369
  /* Common setup of the input slab view for all 4 forms. Decides ALIAS
350
370
  vs SCRATCH from T1's slab_strides[0] vs src->bytes equality, builds
351
371
  the slab_view + (when SCRATCH) carrier in place. The mask travels
@@ -465,6 +485,10 @@ ca_slab_setup_input_slab_view (ca_slab_iter_state_t *st, CArray *src,
465
485
  }
466
486
  }
467
487
 
488
+ if ( st->carrier != Qnil ) {
489
+ ca_slab_prepare_backing(st);
490
+ }
491
+
468
492
  /* The slab is a window onto the source when the fiber is contiguous and
469
493
  a copy in the iterator's scratch when it is not, so writing through it
470
494
  landed on one axis and vanished on the next. Nothing in the surface
@@ -861,7 +885,6 @@ ca_slab_reduce_setup (ca_slab_iter_state_t *st, CArray **out_src,
861
885
  ca_size_t *out_n)
862
886
  {
863
887
  CArray *src;
864
- CArray *out;
865
888
  ca_size_t fiber_len;
866
889
  ca_size_t out_dim[CA_RANK_MAX];
867
890
  int8_t out_ndim;
@@ -904,7 +927,6 @@ ca_slab_reduce_setup (ca_slab_iter_state_t *st, CArray **out_src,
904
927
 
905
928
  st->output = rb_carray_new(st->out_data_type, out_ndim, out_dim,
906
929
  st->out_bytes, NULL);
907
- TypedData_Get_Struct(st->output, CArray, &carray_data_type, out);
908
930
 
909
931
  /* FIBER_CONTIG is naxes==1 only (T1 substrate contract). Multi-axis
910
932
  omits the flag and uses bare CA_SLAB_AXES; T1 picks an in-src
@@ -1178,8 +1200,9 @@ slab_state_run_body (VALUE arg)
1178
1200
  as it has a ptr. Before the scratch they point into is freed, the
1179
1201
  input slab and its carrier are moved onto a copy of the last slab,
1180
1202
  held by the carrier, and the internal output view lets go of its
1181
- buffer. A slab that is a window onto the source itself points into
1182
- the source and needs nothing. */
1203
+ buffer. The copy is made into the array ca_slab_prepare_backing set
1204
+ aside, so nothing here allocates. A slab that is a window onto the
1205
+ source itself points into the source and needs nothing. */
1183
1206
  static void
1184
1207
  slab_state_release_views (ca_slab_iter_state_t *st)
1185
1208
  {
@@ -1196,12 +1219,12 @@ slab_state_release_views (ca_slab_iter_state_t *st)
1196
1219
  if ( cw->ptr == NULL ) {
1197
1220
  return;
1198
1221
  }
1199
- backing = ca_copy(cw);
1200
- bv = ca_wrap_struct(backing);
1201
- if ( cw->mask && ! backing->mask ) {
1202
- ca_create_mask(backing);
1222
+ bv = rb_ivar_get(st->carrier, rb_intern("__slab_backing__"));
1223
+ TypedData_Get_Struct(bv, CArray, &carray_data_type, backing);
1224
+ memcpy(backing->ptr, cw->ptr, (size_t) (cw->elements * cw->bytes));
1225
+ if ( cw->mask && cw->mask->ptr ) {
1226
+ memcpy(backing->mask->ptr, cw->mask->ptr, (size_t) cw->elements);
1203
1227
  }
1204
- rb_ivar_set(st->carrier, rb_intern("__slab_backing__"), bv);
1205
1228
  if ( cs->ptr ) {
1206
1229
  cs->ptr = backing->ptr;
1207
1230
  }