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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +205 -0
- data/carray.gemspec +1 -1
- data/ext/ca_array_pool.c +25 -1
- data/ext/ca_axis_dispatch.c +13 -20
- data/ext/ca_categorical_iterator.c +27 -20
- data/ext/ca_compare.c +4 -4
- data/ext/ca_composite_dispatch.c +9 -9
- data/ext/ca_kernel_iterator.c +6 -6
- data/ext/ca_kernel_iterator.h +112 -83
- data/ext/ca_obj_array.c +69 -19
- data/ext/ca_obj_bincmp.c +1 -0
- data/ext/ca_obj_binop.c +1 -0
- data/ext/ca_obj_bitarray.c +5 -1
- data/ext/ca_obj_bitfield.c +58 -43
- data/ext/ca_obj_byte_swap.c +2 -1
- data/ext/ca_obj_const_string.c +15 -3
- data/ext/ca_obj_face.c +42 -1
- data/ext/ca_obj_field.c +13 -3
- data/ext/ca_obj_grid.c +52 -5
- data/ext/ca_obj_monop.c +46 -10
- data/ext/ca_obj_object.c +64 -12
- data/ext/ca_obj_record.c +3 -0
- data/ext/ca_obj_reduce.c +4 -0
- data/ext/ca_obj_refer.c +18 -9
- data/ext/ca_obj_roll.c +26 -6
- data/ext/ca_obj_select.c +34 -5
- data/ext/ca_obj_shift.c +31 -7
- data/ext/ca_obj_stack.c +1 -0
- data/ext/ca_obj_stride.c +28 -19
- data/ext/ca_obj_string.c +11 -2
- data/ext/ca_obj_tile.c +27 -7
- data/ext/ca_obj_transpose.c +8 -4
- data/ext/ca_obj_triop.c +3 -0
- data/ext/ca_obj_window.c +79 -50
- data/ext/ca_op_ipower.c +4 -4
- data/ext/ca_sort_kernels.h +6 -3
- data/ext/carray.h +33 -0
- data/ext/carray_access.c +16 -3
- data/ext/carray_attribute.c +24 -0
- data/ext/carray_cast.c +243 -82
- data/ext/carray_cast_func.rb +56 -26
- data/ext/carray_conversion.c +2 -2
- data/ext/carray_core.c +3 -0
- data/ext/carray_count.c +6 -0
- data/ext/carray_element.c +9 -4
- data/ext/carray_factorize.c +20 -20
- data/ext/carray_generate.c +8 -2
- data/ext/carray_histogram.c +1 -1
- data/ext/carray_index_classifier.c +4 -4
- data/ext/carray_internal.h +12 -0
- data/ext/carray_kernels_bincmp.c +168 -168
- data/ext/carray_kernels_binop.c +900 -900
- data/ext/carray_kernels_init.c +4 -4
- data/ext/carray_kernels_moncmp.c +96 -96
- data/ext/carray_kernels_monop.c +1984 -1984
- data/ext/carray_kernels_reduce_aggregate.c +1733 -1733
- data/ext/carray_kernels_reduce_cumulative.c +642 -642
- data/ext/carray_kernels_reduce_extreme.c +1150 -1150
- data/ext/carray_kernels_reduce_variance.c +258 -258
- data/ext/carray_kernels_scan.c +128 -128
- data/ext/carray_kernels_search.c +340 -340
- data/ext/carray_kernels_sort.c +295 -304
- data/ext/carray_kernels_triop.c +114 -114
- data/ext/carray_key_windows.c +4 -0
- data/ext/carray_mask.c +45 -22
- data/ext/carray_memory_view.c +2 -2
- data/ext/carray_operator.c +5 -5
- data/ext/carray_order.c +227 -8
- data/ext/carray_random.c +18 -18
- data/ext/carray_scatter.c +24 -7
- data/ext/carray_slab.c +32 -9
- data/ext/carray_sort.c +224 -16
- data/ext/carray_sort_kernel.c +5 -7
- data/ext/carray_struct.c +35 -43
- data/ext/carray_time_text.c +4 -2
- data/ext/carray_utils.c +63 -21
- data/ext/mkkernel.rb +210 -115
- data/ext/version.h +4 -4
- data/lib/carray/attribute.rb +17 -4
- data/lib/carray/basics.rb +27 -7
- data/lib/carray/categorical.rb +30 -16
- data/lib/carray/categorical_iterator.rb +6 -6
- data/lib/carray/const_string.rb +10 -0
- data/lib/carray/data_type_extension.rb +8 -1
- data/lib/carray/fixlen_string.rb +41 -2
- data/lib/carray/frame/column_verbs.rb +4 -3
- data/lib/carray/frame/concat.rb +17 -3
- data/lib/carray/frame/frame.rb +6 -7
- data/lib/carray/frame/group.rb +100 -13
- data/lib/carray/frame/io.rb +3 -1
- data/lib/carray/fuse_source.rb +31 -2
- data/lib/carray/lazy.rb +35 -0
- data/lib/carray/methods/is_in.rb +8 -1
- data/lib/carray/segment_iterator.rb +12 -8
- data/lib/carray/serialize.rb +19 -2
- data/lib/carray/stack.rb +15 -12
- data/lib/carray/string.rb +8 -0
- data/lib/carray/string_operation_extension.rb +13 -5
- data/lib/carray/struct_builder.rb +5 -3
- data/lib/carray/time.rb +107 -68
- data/yard-stubs/ca_obj_transpose.rb +5 -4
- data/yard-stubs/carray_attribute.rb +3 -1
- data/yard-stubs/carray_cast.rb +2 -0
- data/yard-stubs/carray_conversion.rb +5 -1
- data/yard-stubs/carray_element.rb +7 -6
- data/yard-stubs/carray_mask.rb +8 -3
- data/yard-stubs/carray_math.rb +14 -4
- data/yard-stubs/carray_order.rb +100 -4
- data/yard-stubs/carray_scatter.rb +7 -5
- metadata +1 -1
data/ext/mkkernel.rb
CHANGED
|
@@ -44,26 +44,29 @@ module MkKernel
|
|
|
44
44
|
# value_arg: dispatchers (CF.1) to cast a positional Ruby argument to
|
|
45
45
|
# the kernel's T_IN type. Signed integers use NUM2LL, unsigned
|
|
46
46
|
# NUM2ULL, floats NUM2DBL, with the dispatcher adding the per-type cast.
|
|
47
|
+
# `c` of a multi-byte type is its unaligned typedef from carray.h
|
|
48
|
+
# (ca_u_float64_t): a view's cells may sit at any byte, and a kernel
|
|
49
|
+
# reads them through `c *`.
|
|
47
50
|
DTYPES = {
|
|
48
51
|
i8: { c: "int8_t", ca: "CA_INT8", ruby: "LL2NUM", num2c: "NUM2LL",
|
|
49
52
|
limit_hi: "INT8_MAX", limit_lo: "INT8_MIN" },
|
|
50
53
|
u8: { c: "uint8_t", ca: "CA_UINT8", ruby: "ULL2NUM", num2c: "NUM2ULL",
|
|
51
54
|
limit_hi: "UINT8_MAX", limit_lo: "0" },
|
|
52
|
-
i16: { c: "
|
|
55
|
+
i16: { c: "ca_u_int16_t", ca: "CA_INT16", ruby: "LL2NUM", num2c: "NUM2LL",
|
|
53
56
|
limit_hi: "INT16_MAX", limit_lo: "INT16_MIN" },
|
|
54
|
-
u16: { c: "
|
|
57
|
+
u16: { c: "ca_u_uint16_t", ca: "CA_UINT16", ruby: "ULL2NUM", num2c: "NUM2ULL",
|
|
55
58
|
limit_hi: "UINT16_MAX", limit_lo: "0" },
|
|
56
|
-
i32: { c: "
|
|
59
|
+
i32: { c: "ca_u_int32_t", ca: "CA_INT32", ruby: "LL2NUM", num2c: "NUM2LL",
|
|
57
60
|
limit_hi: "INT32_MAX", limit_lo: "INT32_MIN" },
|
|
58
|
-
u32: { c: "
|
|
61
|
+
u32: { c: "ca_u_uint32_t", ca: "CA_UINT32", ruby: "ULL2NUM", num2c: "NUM2ULL",
|
|
59
62
|
limit_hi: "UINT32_MAX", limit_lo: "0" },
|
|
60
|
-
i64: { c: "
|
|
63
|
+
i64: { c: "ca_u_int64_t", ca: "CA_INT64", ruby: "LL2NUM", num2c: "NUM2LL",
|
|
61
64
|
limit_hi: "INT64_MAX", limit_lo: "INT64_MIN" },
|
|
62
|
-
u64: { c: "
|
|
65
|
+
u64: { c: "ca_u_uint64_t", ca: "CA_UINT64", ruby: "ULL2NUM", num2c: "NUM2ULL",
|
|
63
66
|
limit_hi: "UINT64_MAX", limit_lo: "0" },
|
|
64
|
-
f32: { c: "
|
|
67
|
+
f32: { c: "ca_u_float32_t", ca: "CA_FLOAT32", ruby: "rb_float_new", num2c: "NUM2DBL",
|
|
65
68
|
limit_hi: "INFINITY", limit_lo: "-INFINITY" },
|
|
66
|
-
f64: { c: "
|
|
69
|
+
f64: { c: "ca_u_float64_t", ca: "CA_FLOAT64", ruby: "rb_float_new", num2c: "NUM2DBL",
|
|
67
70
|
limit_hi: "INFINITY", limit_lo: "-INFINITY" },
|
|
68
71
|
# E.6a (2026-06-03): boolean as a first-class source DTYPE.
|
|
69
72
|
# Intentionally NOT included in ALL_NUMERIC -- kernels opt boolean in
|
|
@@ -90,11 +93,11 @@ module MkKernel
|
|
|
90
93
|
# CA_NTYPE coverage parity with mkmath, mkkernel needs to know the
|
|
91
94
|
# complex and object data_types too. These entries are NOT included in
|
|
92
95
|
# ALL_NUMERIC; pick up via MATH_TYPES / MATH_NUMERIC aliases.
|
|
93
|
-
cmplx64: { c: "
|
|
96
|
+
cmplx64: { c: "ca_u_cmplx64_t", ca: "CA_CMPLX64", ruby: "CC2NUM",
|
|
94
97
|
num2c: "NUM2CC", limit_hi: "0", limit_lo: "0" },
|
|
95
|
-
cmplx128: { c: "
|
|
98
|
+
cmplx128: { c: "ca_u_cmplx128_t", ca: "CA_CMPLX128", ruby: "CC2NUM",
|
|
96
99
|
num2c: "NUM2CC", limit_hi: "0", limit_lo: "0" },
|
|
97
|
-
object: { c: "VALUE",
|
|
100
|
+
object: { c: "VALUE", ca: "CA_OBJECT", ruby: "(VALUE)",
|
|
98
101
|
num2c: "(VALUE)", limit_hi: "0", limit_lo: "0" },
|
|
99
102
|
# P.5b.4: fixlen for bincmp (= match, eq/ne/gt/lt/ge/le against
|
|
100
103
|
# fixed-byte strings). The c: type is `char` because fixlen kernels
|
|
@@ -1775,13 +1778,15 @@ module MkKernel
|
|
|
1775
1778
|
|
|
1776
1779
|
# Emits the native function of one source data type. The object lane
|
|
1777
1780
|
# calls Ruby for every cell, so any cell can raise in the middle of a walk;
|
|
1778
|
-
# its function is rebuilt around ca_iter_ensure (see
|
|
1779
|
-
#
|
|
1780
|
-
|
|
1781
|
-
|
|
1781
|
+
# its function is rebuilt around ca_iter_ensure (see guard_native). So is
|
|
1782
|
+
# every lane of a family that passes guard: true, whose walks allocate
|
|
1783
|
+
# (an output mask made on the first empty slab, a scratch buffer), since a
|
|
1784
|
+
# failed allocation raises too. Other lanes are emitted as written.
|
|
1785
|
+
def self.emit_native_for(io, k, src, guard: false)
|
|
1786
|
+
if src == :object || guard
|
|
1782
1787
|
buf = StringIO.new
|
|
1783
1788
|
yield buf
|
|
1784
|
-
io.print
|
|
1789
|
+
io.print guard_native(buf.string, "#{k[:name]}_ki_native_#{src}")
|
|
1785
1790
|
else
|
|
1786
1791
|
yield io
|
|
1787
1792
|
end
|
|
@@ -1822,7 +1827,7 @@ module MkKernel
|
|
|
1822
1827
|
# frame is gone once the body raises. The body text is kept as written:
|
|
1823
1828
|
# the parameters are unpacked from the frame under their own names, and
|
|
1824
1829
|
# each state name is defined to its slot for the length of the body.
|
|
1825
|
-
def self.
|
|
1830
|
+
def self.guard_native(text, fn)
|
|
1826
1831
|
header = /^static VALUE\n#{fn} \(([^)]*)\)\n\{\n/
|
|
1827
1832
|
m = header.match(text) or raise "mkkernel: #{fn} not found"
|
|
1828
1833
|
close = matching_brace(text, m.end(0) - 2)
|
|
@@ -1845,9 +1850,9 @@ module MkKernel
|
|
|
1845
1850
|
|
|
1846
1851
|
out = StringIO.new
|
|
1847
1852
|
out.print m.pre_match
|
|
1848
|
-
out.puts "/* The
|
|
1849
|
-
out.puts "
|
|
1850
|
-
out.puts " finishes them however the body leaves. */"
|
|
1853
|
+
out.puts "/* The walks can raise part way (a Ruby call per cell in the object"
|
|
1854
|
+
out.puts " lane, an allocation in any lane). Their states live in the caller's"
|
|
1855
|
+
out.puts " frame, and ca_iter_ensure finishes them however the body leaves. */"
|
|
1851
1856
|
out.puts "typedef struct {"
|
|
1852
1857
|
out.puts " ca_iter_state states[#{states.size}];"
|
|
1853
1858
|
params.each { |type, name| out.puts " #{type} #{name};".sub(/\* /, "*") }
|
|
@@ -1882,7 +1887,7 @@ module MkKernel
|
|
|
1882
1887
|
io.puts
|
|
1883
1888
|
io.puts "/* ===== #{k[:name]}_ki ============================================ */"
|
|
1884
1889
|
k[:source].each do |src|
|
|
1885
|
-
emit_native_for(io, k, src) { |o| emit_reduce_native(o, k, src) }
|
|
1890
|
+
emit_native_for(io, k, src, guard: true) { |o| emit_reduce_native(o, k, src) }
|
|
1886
1891
|
end
|
|
1887
1892
|
emit_reduce_dispatch(io, k)
|
|
1888
1893
|
end
|
|
@@ -3571,17 +3576,21 @@ module MkKernel
|
|
|
3571
3576
|
wio.puts " cw = cv;"
|
|
3572
3577
|
wio.puts " }"
|
|
3573
3578
|
wio.puts " else if ( naxes == 1 && cv->ndim == 1 && cv->dim[0] == src->dim[slab_axes[0]] ) {"
|
|
3574
|
-
wio.puts " /* W-A2: 1-D axis-broadcast -> materialize to full shape
|
|
3575
|
-
wio.puts "
|
|
3579
|
+
wio.puts " /* W-A2: 1-D axis-broadcast -> materialize to full shape. cw and"
|
|
3580
|
+
wio.puts " its mask are made before cv is attached: making them allocates. */"
|
|
3576
3581
|
wio.puts " vcw = rb_carray_new(#{waca}, src->ndim, src->dim, 0, NULL);"
|
|
3577
3582
|
wio.puts " GetCArray(vcw, cw);"
|
|
3583
|
+
wio.puts " if ( ca_has_mask(cv) ) {"
|
|
3584
|
+
wio.puts " ca_create_mask(cw);"
|
|
3585
|
+
wio.puts " }"
|
|
3586
|
+
wio.puts " ca_attach(cv); /* window: nothing raises inside */"
|
|
3578
3587
|
wio.puts " /* broadcast cv (1-D, length src->dim[slab_axes[0]]) along reduce axis */"
|
|
3579
3588
|
wio.puts " {"
|
|
3580
3589
|
wio.puts " int8_t baxis = slab_axes[0];"
|
|
3581
3590
|
wio.puts " ca_size_t inner_stride = 1;"
|
|
3582
3591
|
wio.puts " for ( int8_t ii = baxis + 1; ii < src->ndim; ii++ ) inner_stride *= src->dim[ii];"
|
|
3583
3592
|
wio.puts " ca_size_t axis_n = src->dim[baxis];"
|
|
3584
|
-
wio.puts " ca_size_t outer = src->elements / (axis_n * inner_stride);"
|
|
3593
|
+
wio.puts " ca_size_t outer = ( axis_n && inner_stride ) ? src->elements / (axis_n * inner_stride) : 0; /* a zero-length axis leaves nothing to fill */"
|
|
3585
3594
|
wio.puts " for ( ca_size_t o = 0; o < outer; o++ ) {"
|
|
3586
3595
|
wio.puts " for ( ca_size_t a = 0; a < axis_n; a++ ) {"
|
|
3587
3596
|
wio.puts " for ( ca_size_t in = 0; in < inner_stride; in++ ) {"
|
|
@@ -3593,15 +3602,14 @@ module MkKernel
|
|
|
3593
3602
|
wio.puts " }"
|
|
3594
3603
|
wio.puts " /* Propagate cv mask via W-A3 overlay path below (= mask materialize) */"
|
|
3595
3604
|
wio.puts " if ( ca_has_mask(cv) ) {"
|
|
3596
|
-
wio.puts " /*
|
|
3597
|
-
wio.puts " ca_create_mask(cw);"
|
|
3605
|
+
wio.puts " /* broadcast cv mask along axis */"
|
|
3598
3606
|
wio.puts " boolean8_t *cw_m = (boolean8_t *)cw->mask->ptr;"
|
|
3599
3607
|
wio.puts " boolean8_t *cv_m = (boolean8_t *)cv->mask->ptr;"
|
|
3600
3608
|
wio.puts " int8_t baxis = slab_axes[0];"
|
|
3601
3609
|
wio.puts " ca_size_t inner_stride = 1;"
|
|
3602
3610
|
wio.puts " for ( int8_t ii = baxis + 1; ii < src->ndim; ii++ ) inner_stride *= src->dim[ii];"
|
|
3603
3611
|
wio.puts " ca_size_t axis_n = src->dim[baxis];"
|
|
3604
|
-
wio.puts " ca_size_t outer = src->elements / (axis_n * inner_stride);"
|
|
3612
|
+
wio.puts " ca_size_t outer = ( axis_n && inner_stride ) ? src->elements / (axis_n * inner_stride) : 0; /* a zero-length axis leaves nothing to fill */"
|
|
3605
3613
|
wio.puts " for ( ca_size_t o = 0; o < outer; o++ ) {"
|
|
3606
3614
|
wio.puts " for ( ca_size_t a = 0; a < axis_n; a++ ) {"
|
|
3607
3615
|
wio.puts " for ( ca_size_t in = 0; in < inner_stride; in++ ) {"
|
|
@@ -3827,6 +3835,8 @@ module MkKernel
|
|
|
3827
3835
|
# fiber's mask base.
|
|
3828
3836
|
mask_setup =
|
|
3829
3837
|
if gated
|
|
3838
|
+
# Before the walks open: creating the mask allocates, and a raise
|
|
3839
|
+
# inside them would jump past their finish.
|
|
3830
3840
|
"\n" \
|
|
3831
3841
|
" boolean8_t *op_mask = NULL;\n" \
|
|
3832
3842
|
" if ( ca_has_mask(ca) ) {\n" \
|
|
@@ -3867,6 +3877,7 @@ module MkKernel
|
|
|
3867
3877
|
INIT inside the macro. */
|
|
3868
3878
|
int8_t slab_axes[CA_RANK_MAX];
|
|
3869
3879
|
slab_axes[0] = (int8_t) axis;
|
|
3880
|
+
#{mask_setup}
|
|
3870
3881
|
ca_iter_state st_in, st_out;
|
|
3871
3882
|
int rc;
|
|
3872
3883
|
rc = ca_iter_state_init_l2(&st_in, ca, CA_SLAB_AXES,
|
|
@@ -3882,7 +3893,6 @@ module MkKernel
|
|
|
3882
3893
|
rb_raise(rb_eRuntimeError,
|
|
3883
3894
|
"%s: output init failed rc=%d", ca_calling_method_name(), rc);
|
|
3884
3895
|
}
|
|
3885
|
-
#{mask_setup}
|
|
3886
3896
|
char *pi, *po;
|
|
3887
3897
|
boolean8_t *mi, *mo;
|
|
3888
3898
|
while ( ca_iter_state_next_slab_axes(&st_in, &pi, &mi) &&
|
|
@@ -4167,7 +4177,7 @@ module MkKernel
|
|
|
4167
4177
|
if is_fixlen
|
|
4168
4178
|
io.puts " buf[#{dst}].vp = pi + #{kexpr} * istride; buf[#{dst}].nb = (ca_size_t) ca->bytes;"
|
|
4169
4179
|
else
|
|
4170
|
-
io.puts " buf[#{dst}].v
|
|
4180
|
+
io.puts " memcpy(&buf[#{dst}].v, pi + #{kexpr} * istride, sizeof(buf[#{dst}].v));"
|
|
4171
4181
|
end
|
|
4172
4182
|
end
|
|
4173
4183
|
|
|
@@ -4263,21 +4273,10 @@ module MkKernel
|
|
|
4263
4273
|
io.puts " when input has any mask. */"
|
|
4264
4274
|
io.puts " if ( ca_has_mask(ca) ) ca_create_mask(co);"
|
|
4265
4275
|
end
|
|
4266
|
-
|
|
4267
|
-
|
|
4268
|
-
|
|
4269
|
-
io.puts "
|
|
4270
|
-
io.puts " if ( rc != CA_ITER_OK ) {"
|
|
4271
|
-
io.puts %Q[ rb_raise(rb_eRuntimeError, "%s: input init failed rc=%d", ca_calling_method_name(), rc);]
|
|
4272
|
-
io.puts " }"
|
|
4273
|
-
io.puts " rc = ca_iter_state_init_l2_paired(&st_out, &st_in, co, CA_SLAB_AXES,"
|
|
4274
|
-
io.puts " slab_axes, 1, CA_KERNEL_WRITE);"
|
|
4275
|
-
io.puts " if ( rc != CA_ITER_OK ) {"
|
|
4276
|
-
io.puts " ca_iter_state_finish(&st_in);"
|
|
4277
|
-
io.puts %Q[ rb_raise(rb_eRuntimeError, "%s: output init failed rc=%d", ca_calling_method_name(), rc);]
|
|
4278
|
-
io.puts " }"
|
|
4279
|
-
io.puts
|
|
4280
|
-
io.puts " ca_size_t fiber_n = st_in.slab_elements;"
|
|
4276
|
+
# The buffers are taken before the walks open: an allocation that
|
|
4277
|
+
# raises inside them would jump past their finish. The slab is the
|
|
4278
|
+
# sort axis alone, so a fiber holds ca->dim[axis] cells.
|
|
4279
|
+
io.puts " ca_size_t fiber_n = ca->dim[axis];"
|
|
4281
4280
|
if src == :object
|
|
4282
4281
|
# The object comparator calls <=>, so the sort can raise part way.
|
|
4283
4282
|
# ALLOCV frees these buffers whether the function returns or unwinds.
|
|
@@ -4290,15 +4289,17 @@ module MkKernel
|
|
|
4290
4289
|
io.puts " #{name}_pair_#{src} *aux = ALLOCV_N(#{name}_pair_#{src}, haux, fiber_n);"
|
|
4291
4290
|
end
|
|
4292
4291
|
else
|
|
4293
|
-
# ALLOCV as well: the walk that fills buf is the only thing that
|
|
4294
|
-
# could raise, and it does not today, but the buffers need not rely
|
|
4295
|
-
# on that.
|
|
4296
4292
|
io.puts " volatile VALUE hbuf = 0, haux = 0;"
|
|
4297
4293
|
io.puts " #{name}_pair_#{src} *buf = ALLOCV_N(#{name}_pair_#{src}, hbuf, fiber_n);"
|
|
4298
4294
|
end
|
|
4299
|
-
if has_kind && src
|
|
4295
|
+
if has_kind && %i[f32 f64].include?(src)
|
|
4296
|
+
# aux is the mergesort's ping-pong buffer and, before that, the
|
|
4297
|
+
# scratch of the NaN pre-partition, so a float kernel always takes it.
|
|
4298
|
+
io.puts " #{name}_pair_#{src} *aux = ALLOCV_N(#{name}_pair_#{src}, haux, fiber_n);"
|
|
4299
|
+
elsif has_kind && src != :object && src != :fixlen
|
|
4300
4300
|
# The aux pair buffer for ca_sort_merge_pair_<src> (a ping-pong
|
|
4301
|
-
# bottom-up mergesort), taken once outside the fiber loop.
|
|
4301
|
+
# bottom-up mergesort), taken once outside the fiber loop. The fixlen
|
|
4302
|
+
# lane sorts with qsort and needs none.
|
|
4302
4303
|
io.puts " #{name}_pair_#{src} *aux = NULL;"
|
|
4303
4304
|
io.puts " if ( do_stable ) {"
|
|
4304
4305
|
io.puts " aux = ALLOCV_N(#{name}_pair_#{src}, haux, fiber_n);"
|
|
@@ -4307,6 +4308,20 @@ module MkKernel
|
|
|
4307
4308
|
io.puts " (void) haux;"
|
|
4308
4309
|
end
|
|
4309
4310
|
|
|
4311
|
+
io.puts " ca_iter_state st_in, st_out;"
|
|
4312
|
+
io.puts " int rc;"
|
|
4313
|
+
io.puts " rc = ca_iter_state_init_l2(&st_in, ca, CA_SLAB_AXES,"
|
|
4314
|
+
io.puts " slab_axes, 1, 0);"
|
|
4315
|
+
io.puts " if ( rc != CA_ITER_OK ) {"
|
|
4316
|
+
io.puts %Q[ rb_raise(rb_eRuntimeError, "%s: input init failed rc=%d", ca_calling_method_name(), rc);]
|
|
4317
|
+
io.puts " }"
|
|
4318
|
+
io.puts " rc = ca_iter_state_init_l2_paired(&st_out, &st_in, co, CA_SLAB_AXES,"
|
|
4319
|
+
io.puts " slab_axes, 1, CA_KERNEL_WRITE);"
|
|
4320
|
+
io.puts " if ( rc != CA_ITER_OK ) {"
|
|
4321
|
+
io.puts " ca_iter_state_finish(&st_in);"
|
|
4322
|
+
io.puts %Q[ rb_raise(rb_eRuntimeError, "%s: output init failed rc=%d", ca_calling_method_name(), rc);]
|
|
4323
|
+
io.puts " }"
|
|
4324
|
+
|
|
4310
4325
|
if view_flat
|
|
4311
4326
|
io.puts
|
|
4312
4327
|
io.puts " /* :view_flat semantics: kernel computes view-flat address"
|
|
@@ -4358,7 +4373,7 @@ module MkKernel
|
|
|
4358
4373
|
if is_fixlen
|
|
4359
4374
|
io.puts " buf[k].vp = pi + k * istride; buf[k].nb = (ca_size_t) ca->bytes;"
|
|
4360
4375
|
else
|
|
4361
|
-
io.puts " buf[k].v
|
|
4376
|
+
io.puts " memcpy(&buf[k].v, pi + k * istride, sizeof(buf[k].v));"
|
|
4362
4377
|
end
|
|
4363
4378
|
io.puts " buf[k].i = #{payload.call("k")};"
|
|
4364
4379
|
io.puts " }"
|
|
@@ -4373,7 +4388,7 @@ module MkKernel
|
|
|
4373
4388
|
if is_fixlen
|
|
4374
4389
|
io.puts " buf[k_unmasked].vp = pi + k * istride; buf[k_unmasked].nb = (ca_size_t) ca->bytes;"
|
|
4375
4390
|
else
|
|
4376
|
-
io.puts " buf[k_unmasked].v
|
|
4391
|
+
io.puts " memcpy(&buf[k_unmasked].v, pi + k * istride, sizeof(buf[k_unmasked].v));"
|
|
4377
4392
|
end
|
|
4378
4393
|
io.puts " buf[k_unmasked].i = k;"
|
|
4379
4394
|
io.puts " k_unmasked++;"
|
|
@@ -4389,7 +4404,7 @@ module MkKernel
|
|
|
4389
4404
|
if is_fixlen
|
|
4390
4405
|
io.puts " buf[k].vp = pi + k * istride; buf[k].nb = (ca_size_t) ca->bytes;"
|
|
4391
4406
|
else
|
|
4392
|
-
io.puts " buf[k].v
|
|
4407
|
+
io.puts " memcpy(&buf[k].v, pi + k * istride, sizeof(buf[k].v));"
|
|
4393
4408
|
end
|
|
4394
4409
|
io.puts " buf[k].i = k;"
|
|
4395
4410
|
io.puts " }"
|
|
@@ -4462,7 +4477,7 @@ module MkKernel
|
|
|
4462
4477
|
io.puts " /* P.3/P.4: NaN pre-partition for float argsort -- NaN pairs are"
|
|
4463
4478
|
io.puts " pushed to the tail (= NaN-at-end policy, matches sort_copy"
|
|
4464
4479
|
io.puts " AC8). Only the finite slice is sorted. */"
|
|
4465
|
-
io.puts " ca_size_t finite_n = ca_partition_nan_pair_#{src}((ca_pair_#{src} *) #{buf_expr}, (ca_size_t) #{sort_n_expr});"
|
|
4480
|
+
io.puts " ca_size_t finite_n = ca_partition_nan_pair_#{src}((ca_pair_#{src} *) #{buf_expr}, (ca_size_t) #{sort_n_expr}, (ca_pair_#{src} *) aux);"
|
|
4466
4481
|
io.puts " if ( do_stable ) {"
|
|
4467
4482
|
io.puts " ca_sort_merge_pair_#{src}((ca_pair_#{src} *) #{buf_expr}, (ca_pair_#{src} *) aux, finite_n);"
|
|
4468
4483
|
io.puts " } else {"
|
|
@@ -5004,7 +5019,7 @@ module MkKernel
|
|
|
5004
5019
|
io.puts
|
|
5005
5020
|
io.puts "/* ===== #{k[:name]}_ki ============================================ */"
|
|
5006
5021
|
k[:source].each do |src|
|
|
5007
|
-
emit_native_for(io, k, src) { |o| emit_search_native(o, k, src) }
|
|
5022
|
+
emit_native_for(io, k, src, guard: true) { |o| emit_search_native(o, k, src) }
|
|
5008
5023
|
end
|
|
5009
5024
|
emit_search_dispatch(io, k)
|
|
5010
5025
|
end
|
|
@@ -5264,7 +5279,7 @@ module MkKernel
|
|
|
5264
5279
|
CArray *co;
|
|
5265
5280
|
GetCArray(vout, co);
|
|
5266
5281
|
#{oi[:c]} *op = (#{oi[:c]} *) co->ptr;
|
|
5267
|
-
#{op_mask_decl}
|
|
5282
|
+
#{undef_nm ? op_mask_decl : ""}
|
|
5268
5283
|
|
|
5269
5284
|
ca_iter_state st;
|
|
5270
5285
|
int rc = ca_iter_state_init_l2(&st, ca, CA_SLAB_AXES,
|
|
@@ -5486,6 +5501,12 @@ module MkKernel
|
|
|
5486
5501
|
#{op_mask_decl}
|
|
5487
5502
|
|
|
5488
5503
|
#{entity_bc}
|
|
5504
|
+
/* The output mask is made before the window opens, since making it
|
|
5505
|
+
allocates; it comes off again below when no cell needed it. */
|
|
5506
|
+
if ( #{undef_nm ? "1" : "ca_has_mask(cv)"} ) {
|
|
5507
|
+
ca_create_mask(co);
|
|
5508
|
+
op_mask = (boolean8_t *) co->mask->ptr;
|
|
5509
|
+
}
|
|
5489
5510
|
/* Attach self + val; rev4 uses raw row-major byte addressing. */
|
|
5490
5511
|
ca_attach_n(2, ca, cv); /* window: numeric bodies do not raise; the object lane reads entities */
|
|
5491
5512
|
|
|
@@ -5592,6 +5613,11 @@ module MkKernel
|
|
|
5592
5613
|
|
|
5593
5614
|
ca_detach(ca);
|
|
5594
5615
|
ca_detach(cv);
|
|
5616
|
+
if ( op_mask && ! memchr(op_mask, 1, (size_t) co->elements) ) {
|
|
5617
|
+
CArray *unused = co->mask;
|
|
5618
|
+
co->mask = NULL;
|
|
5619
|
+
ca_free(unused);
|
|
5620
|
+
}
|
|
5595
5621
|
return vout;
|
|
5596
5622
|
}
|
|
5597
5623
|
C
|
|
@@ -5633,6 +5659,13 @@ module MkKernel
|
|
|
5633
5659
|
io.puts " VALUE rval = argv[0];"
|
|
5634
5660
|
io.puts " VALUE raxis = argv[1];"
|
|
5635
5661
|
io.puts " VALUE reps = (argc >= 3) ? argv[2] : Qnil;"
|
|
5662
|
+
io.puts " /* A tolerance is a distance: a negative one (or NaN) means nothing. */"
|
|
5663
|
+
io.puts " if ( ! NIL_P(reps) ) {"
|
|
5664
|
+
io.puts " double eps_given = NUM2DBL(reps);"
|
|
5665
|
+
io.puts " if ( ! ( eps_given >= 0.0 ) ) {"
|
|
5666
|
+
io.puts %Q[ rb_raise(rb_eArgError, "%s: eps must be zero or positive (got %"PRIsVALUE")", ca_calling_method_name(), rb_inspect(reps));]
|
|
5667
|
+
io.puts " }"
|
|
5668
|
+
io.puts " }"
|
|
5636
5669
|
end
|
|
5637
5670
|
io.puts " /* Query gate: the reference Face and the query are brought into one"
|
|
5638
5671
|
io.puts " space (ca_face_reconcile_pair), then the reference descends to"
|
|
@@ -5656,6 +5689,8 @@ module MkKernel
|
|
|
5656
5689
|
io.puts " GetCArray(self, src);"
|
|
5657
5690
|
io.puts " }"
|
|
5658
5691
|
io.puts " }"
|
|
5692
|
+
io.puts " self = ca_fixlen_widen_for_query(self, rval);"
|
|
5693
|
+
io.puts " GetCArray(self, src);"
|
|
5659
5694
|
|
|
5660
5695
|
case k[:mask_self]
|
|
5661
5696
|
when :raise
|
|
@@ -5687,13 +5722,14 @@ module MkKernel
|
|
|
5687
5722
|
# (`ca_monop_<name>_<suffix>`). mkmath uses typedef names
|
|
5688
5723
|
# `float32_t` / `float64_t` here, not the raw C types `float` / `double`,
|
|
5689
5724
|
# so that `ca_monop_dispatch.c`'s extern references stay ABI-stable.
|
|
5690
|
-
# For all other data_types, the suffix
|
|
5725
|
+
# For all other data_types, the suffix is `DTYPES[src][:c]` without the
|
|
5726
|
+
# `ca_u_` of its unaligned cell typedef (ca_u_int32_t -> int32_t).
|
|
5691
5727
|
def self.math_suffix(src)
|
|
5692
5728
|
case src
|
|
5693
5729
|
when :f32 then "float32_t"
|
|
5694
5730
|
when :f64 then "float64_t"
|
|
5695
5731
|
when :fixlen then "fixlen" # symbol suffix differs from c-type "char"
|
|
5696
|
-
else DTYPES[src][:c]
|
|
5732
|
+
else DTYPES[src][:c].sub(/\Aca_u_/, "")
|
|
5697
5733
|
end
|
|
5698
5734
|
end
|
|
5699
5735
|
|
|
@@ -5772,7 +5808,8 @@ module MkKernel
|
|
|
5772
5808
|
uses_input = raw.include?("#1")
|
|
5773
5809
|
# SL.2.x phase 2 (monop emit substrate hardening, MEMO_MONOP_STRIDED_AUDIT.md):
|
|
5774
5810
|
# 3-way split (mask / contig / strided) symmetric with binop SL.2.x phase 1.
|
|
5775
|
-
# contig branch (i1==1 && i2==1) uses index form + `
|
|
5811
|
+
# contig branch (i1==1 && i2==1) uses index form + `omp simd` (`__restrict__`
|
|
5812
|
+
# only when input and output are apart).
|
|
5776
5813
|
# strided branch uses `p += i` increment form (per-iter multiply removed).
|
|
5777
5814
|
# mask branch unchanged (per-cell predicate, not SIMD-amenable).
|
|
5778
5815
|
if uses_input
|
|
@@ -5780,11 +5817,11 @@ module MkKernel
|
|
|
5780
5817
|
|
|
5781
5818
|
static void
|
|
5782
5819
|
ca_monop_#{k[:name]}_#{suffix} (ca_size_t n, boolean8_t *m,
|
|
5783
|
-
char *
|
|
5784
|
-
char *
|
|
5820
|
+
char *ptr1, ca_size_t i1,
|
|
5821
|
+
char *ptr2, ca_size_t i2)
|
|
5785
5822
|
{
|
|
5786
|
-
#{c_type_in} *
|
|
5787
|
-
#{c_type_out} *
|
|
5823
|
+
#{c_type_in} *q1 = (#{c_type_in} *) ptr1;
|
|
5824
|
+
#{c_type_out} *q2 = (#{c_type_out} *) ptr2;
|
|
5788
5825
|
ca_size_t k;
|
|
5789
5826
|
if ( m ) {
|
|
5790
5827
|
#{c_type_in} *p1 = q1;
|
|
@@ -5800,17 +5837,38 @@ module MkKernel
|
|
|
5800
5837
|
}
|
|
5801
5838
|
} else if ( i1 == 1 && i2 == 1 ) {
|
|
5802
5839
|
/* contig branch: SIMD-friendly form. Index-form `q[k]` exposes
|
|
5803
|
-
the unit-stride access to the vectorizer
|
|
5804
|
-
|
|
5805
|
-
|
|
5806
|
-
|
|
5840
|
+
the unit-stride access to the vectorizer. Element-wise monop
|
|
5841
|
+
has no reassoc, so ε-close numeric contract is unaffected.
|
|
5842
|
+
The input and output are either apart, the same cells (a
|
|
5843
|
+
bang, or the lazy scratch applied in place), or overlapping
|
|
5844
|
+
otherwise; `__restrict__` is promised only when apart, since
|
|
5845
|
+
the body may store part of a cell before it has read it. */
|
|
5807
5846
|
#{c_type_in} *p1;
|
|
5808
5847
|
#{c_type_out} *p2;
|
|
5809
5848
|
(void)p1; (void)p2;
|
|
5810
|
-
|
|
5811
|
-
|
|
5812
|
-
|
|
5813
|
-
|
|
5849
|
+
uintptr_t a1 = (uintptr_t) q1, a2 = (uintptr_t) q2;
|
|
5850
|
+
if ( a2 + n * sizeof(*q2) <= a1 || a1 + n * sizeof(*q1) <= a2 ) {
|
|
5851
|
+
#{c_type_in} * __restrict__ r1 = q1;
|
|
5852
|
+
#{c_type_out} * __restrict__ r2 = q2;
|
|
5853
|
+
#pragma omp simd
|
|
5854
|
+
for (k=0; k<n; k++) {
|
|
5855
|
+
p1 = r1 + k; p2 = r2 + k;
|
|
5856
|
+
{ #{expr_body} }
|
|
5857
|
+
}
|
|
5858
|
+
}
|
|
5859
|
+
else if ( a1 == a2 && sizeof(*q1) == sizeof(*q2) ) {
|
|
5860
|
+
/* in place: an iteration reads and writes only its own cell */
|
|
5861
|
+
#pragma omp simd
|
|
5862
|
+
for (k=0; k<n; k++) {
|
|
5863
|
+
p1 = q1 + k; p2 = q2 + k;
|
|
5864
|
+
{ #{expr_body} }
|
|
5865
|
+
}
|
|
5866
|
+
}
|
|
5867
|
+
else {
|
|
5868
|
+
for (k=0; k<n; k++) {
|
|
5869
|
+
p1 = q1 + k; p2 = q2 + k;
|
|
5870
|
+
{ #{expr_body} }
|
|
5871
|
+
}
|
|
5814
5872
|
}
|
|
5815
5873
|
} else {
|
|
5816
5874
|
/* strided branch: numo-shape `p += s` increment form. */
|
|
@@ -5996,7 +6054,8 @@ module MkKernel
|
|
|
5996
6054
|
expr_body = raw.gsub(/#1/, "*p1").gsub(/#2/, "*p2").gsub(/#3/, "*p3").gsub(/<type>/, suffix)
|
|
5997
6055
|
# Option B+C pilot (binop emit substrate hardening, MEMO_BINOP_STRIDED_AUDIT.md):
|
|
5998
6056
|
# The no-mask body is split into a contig branch (i1==i2==i3==1) using the
|
|
5999
|
-
# `q[k]` index form with
|
|
6057
|
+
# `q[k]` index form with `#pragma omp simd` (`__restrict__`-qualified only
|
|
6058
|
+
# when the output is apart from both inputs)
|
|
6000
6059
|
# (SL.2.x-style insurance, element-wise has no reassoc -> ε-close unaffected),
|
|
6001
6060
|
# and a strided branch using `p += i` increment form (numo-shape inner loop).
|
|
6002
6061
|
# `i1`/`i2`/`i3` remain element units (NOT byte) -- byte-flip is Option A scope.
|
|
@@ -6006,13 +6065,13 @@ module MkKernel
|
|
|
6006
6065
|
|
|
6007
6066
|
static void
|
|
6008
6067
|
ca_binop_#{k[:name]}_#{suffix} (ca_size_t n, boolean8_t *m,
|
|
6009
|
-
char *
|
|
6010
|
-
char *
|
|
6011
|
-
char *
|
|
6068
|
+
char *ptr1, ca_size_t i1,
|
|
6069
|
+
char *ptr2, ca_size_t i2,
|
|
6070
|
+
char *ptr3, ca_size_t i3)
|
|
6012
6071
|
{
|
|
6013
|
-
#{c_type} *
|
|
6014
|
-
#{c_type} *
|
|
6015
|
-
#{c_type} *
|
|
6072
|
+
#{c_type} *q1 = (#{c_type} *) ptr1;
|
|
6073
|
+
#{c_type} *q2 = (#{c_type} *) ptr2;
|
|
6074
|
+
#{c_type} *q3 = (#{c_type} *) ptr3;
|
|
6016
6075
|
ca_size_t k;
|
|
6017
6076
|
if ( m ) {
|
|
6018
6077
|
#{c_type} *p1 = q1, *p2 = q2, *p3 = q3;
|
|
@@ -6028,16 +6087,41 @@ module MkKernel
|
|
|
6028
6087
|
}
|
|
6029
6088
|
} else if ( i1 == 1 && i2 == 1 && i3 == 1 ) {
|
|
6030
6089
|
/* contig branch: SIMD-friendly form. Index-form `q[k]` exposes
|
|
6031
|
-
the unit-stride access to the vectorizer
|
|
6032
|
-
|
|
6033
|
-
|
|
6034
|
-
|
|
6090
|
+
the unit-stride access to the vectorizer. Element-wise binop
|
|
6091
|
+
has no reassoc, so ε-close numeric contract is unaffected.
|
|
6092
|
+
The output is apart from each input, the same cells as it (a
|
|
6093
|
+
bang), or overlaps it otherwise; `__restrict__` is promised
|
|
6094
|
+
only when apart from both, since the body may store part of a
|
|
6095
|
+
cell before it has read it. */
|
|
6035
6096
|
#{c_type} *p1, *p2, *p3;
|
|
6097
|
+
uintptr_t a1 = (uintptr_t) q1, a2 = (uintptr_t) q2, a3 = (uintptr_t) q3;
|
|
6098
|
+
uintptr_t nb = (uintptr_t) n * sizeof(*q3);
|
|
6099
|
+
int apart1 = ( a3 + nb <= a1 || a1 + nb <= a3 );
|
|
6100
|
+
int apart2 = ( a3 + nb <= a2 || a2 + nb <= a3 );
|
|
6036
6101
|
(void)p1; (void)p2; (void)p3;
|
|
6037
|
-
|
|
6038
|
-
|
|
6039
|
-
|
|
6040
|
-
|
|
6102
|
+
if ( apart1 && apart2 ) {
|
|
6103
|
+
#{c_type} * __restrict__ r1 = q1;
|
|
6104
|
+
#{c_type} * __restrict__ r2 = q2;
|
|
6105
|
+
#{c_type} * __restrict__ r3 = q3;
|
|
6106
|
+
#pragma omp simd
|
|
6107
|
+
for (k=0; k<n; k++) {
|
|
6108
|
+
p1 = r1 + k; p2 = r2 + k; p3 = r3 + k;
|
|
6109
|
+
{ #{expr_body} }
|
|
6110
|
+
}
|
|
6111
|
+
}
|
|
6112
|
+
else if ( ( apart1 || a1 == a3 ) && ( apart2 || a2 == a3 ) ) {
|
|
6113
|
+
/* in place: an iteration reads and writes only its own cell */
|
|
6114
|
+
#pragma omp simd
|
|
6115
|
+
for (k=0; k<n; k++) {
|
|
6116
|
+
p1 = q1 + k; p2 = q2 + k; p3 = q3 + k;
|
|
6117
|
+
{ #{expr_body} }
|
|
6118
|
+
}
|
|
6119
|
+
}
|
|
6120
|
+
else {
|
|
6121
|
+
for (k=0; k<n; k++) {
|
|
6122
|
+
p1 = q1 + k; p2 = q2 + k; p3 = q3 + k;
|
|
6123
|
+
{ #{expr_body} }
|
|
6124
|
+
}
|
|
6041
6125
|
}
|
|
6042
6126
|
} else {
|
|
6043
6127
|
/* strided branch: numo-shape `p += s` increment form. */
|
|
@@ -6500,13 +6584,21 @@ module MkKernel
|
|
|
6500
6584
|
cmplx64 cmplx128].freeze
|
|
6501
6585
|
CAST_INEXACT = %i[f32 f64 cmplx64 cmplx128].freeze
|
|
6502
6586
|
|
|
6587
|
+
# The plain C type of a data type, for a body that leaves this file
|
|
6588
|
+
# (CArray.__kernel_body__, a fusion plan): the unaligned typedef in :c is
|
|
6589
|
+
# about how a kernel reaches a cell, not about the value.
|
|
6590
|
+
def self.value_c(src)
|
|
6591
|
+
{ "ca_u_float32_t" => "float", "ca_u_float64_t" => "double" }
|
|
6592
|
+
.fetch(DTYPES[src][:c]) { |c| c.sub(/\Aca_u_/, "") }
|
|
6593
|
+
end
|
|
6594
|
+
|
|
6503
6595
|
def self.cast_body_rows
|
|
6504
6596
|
CAST_SOURCES.product(CAST_TARGETS).filter_map { |src, dst|
|
|
6505
6597
|
next if CAST_INEXACT.include?(src) && !CAST_INEXACT.include?(dst)
|
|
6506
6598
|
name = DTYPES[dst][:ca].sub(/\ACA_/, "").downcase
|
|
6507
6599
|
via = ( %i[i64 u64].include?(src) && %i[f32 cmplx64].include?(dst) ) ? "(double)" : ""
|
|
6508
6600
|
["monop", "cast_#{name}", DTYPES[src][:ca].sub(/\ACA_/, "").downcase,
|
|
6509
|
-
"(#2) = (#{
|
|
6601
|
+
"(#2) = (#{value_c(dst)})#{via}(#1);"]
|
|
6510
6602
|
}
|
|
6511
6603
|
end
|
|
6512
6604
|
|
|
@@ -7678,8 +7770,8 @@ MkKernel.search :find_value_index,
|
|
|
7678
7770
|
mask_self: :raise,
|
|
7679
7771
|
fallback: :raise
|
|
7680
7772
|
|
|
7681
|
-
# bsearch_ki: per-slice binary search returning the matching position
|
|
7682
|
-
# (assumes a sorted slab). NaN handling: NaN sorts to end; query == NaN
|
|
7773
|
+
# bsearch_ki: per-slice binary search returning the first matching position
|
|
7774
|
+
# (assumes a sorted slab; of equal cells, the first). NaN handling: NaN sorts to end; query == NaN
|
|
7683
7775
|
# never matches. Hand-rolled strided binary search (= libc bsearch()
|
|
7684
7776
|
# requires contig memory; slab_stride may be > sizeof(T_LOAD) for a
|
|
7685
7777
|
# non-innermost search axis). NaN guards are dead code for integer
|
|
@@ -7705,7 +7797,7 @@ MkKernel.search :bsearch,
|
|
|
7705
7797
|
T_LOAD v = *(T_LOAD *)(slab_ptr + mid * slab_stride);
|
|
7706
7798
|
if ( v < query_val ) lo = mid + 1;
|
|
7707
7799
|
else if ( v > query_val ) hi = mid;
|
|
7708
|
-
else { result = mid;
|
|
7800
|
+
else { result = mid; hi = mid; } /* keep looking left: the first equal cell */
|
|
7709
7801
|
}
|
|
7710
7802
|
}
|
|
7711
7803
|
C
|
|
@@ -7720,7 +7812,7 @@ MkKernel.search :bsearch,
|
|
|
7720
7812
|
int c = CA_FLOAT_ORDER_CMP(v, query_val);
|
|
7721
7813
|
if ( c < 0 ) lo = mid + 1;
|
|
7722
7814
|
else if ( c > 0 ) hi = mid;
|
|
7723
|
-
else { result = mid;
|
|
7815
|
+
else { result = mid; hi = mid; } /* keep looking left: the first equal cell */
|
|
7724
7816
|
}
|
|
7725
7817
|
}
|
|
7726
7818
|
C
|
|
@@ -7737,7 +7829,7 @@ MkKernel.search :bsearch,
|
|
|
7737
7829
|
int c = ca_obj_order_cmp(v, query_val);
|
|
7738
7830
|
if ( c < 0 ) lo = mid + 1;
|
|
7739
7831
|
else if ( c > 0 ) hi = mid;
|
|
7740
|
-
else { result = mid;
|
|
7832
|
+
else { result = mid; hi = mid; } /* keep looking left: the first equal cell */
|
|
7741
7833
|
}
|
|
7742
7834
|
C
|
|
7743
7835
|
fixlen: <<~C,
|
|
@@ -7753,7 +7845,7 @@ MkKernel.search :bsearch,
|
|
|
7753
7845
|
int c = memcmp(v, query_val, (size_t) ca->bytes);
|
|
7754
7846
|
if ( c < 0 ) lo = mid + 1;
|
|
7755
7847
|
else if ( c > 0 ) hi = mid;
|
|
7756
|
-
else { result = mid;
|
|
7848
|
+
else { result = mid; hi = mid; } /* keep looking left: the first equal cell */
|
|
7757
7849
|
}
|
|
7758
7850
|
C
|
|
7759
7851
|
},
|
|
@@ -7938,7 +8030,7 @@ MkKernel.search :bsearch_addr,
|
|
|
7938
8030
|
T_LOAD v = *(T_LOAD *)(slab_ptr + mid * slab_stride);
|
|
7939
8031
|
if ( v < query_val ) lo = mid + 1;
|
|
7940
8032
|
else if ( v > query_val ) hi = mid;
|
|
7941
|
-
else { result = mid;
|
|
8033
|
+
else { result = mid; hi = mid; } /* keep looking left: the first equal cell */
|
|
7942
8034
|
}
|
|
7943
8035
|
}
|
|
7944
8036
|
C
|
|
@@ -7953,7 +8045,7 @@ MkKernel.search :bsearch_addr,
|
|
|
7953
8045
|
int c = CA_FLOAT_ORDER_CMP(v, query_val);
|
|
7954
8046
|
if ( c < 0 ) lo = mid + 1;
|
|
7955
8047
|
else if ( c > 0 ) hi = mid;
|
|
7956
|
-
else { result = mid;
|
|
8048
|
+
else { result = mid; hi = mid; } /* keep looking left: the first equal cell */
|
|
7957
8049
|
}
|
|
7958
8050
|
}
|
|
7959
8051
|
C
|
|
@@ -7967,7 +8059,7 @@ MkKernel.search :bsearch_addr,
|
|
|
7967
8059
|
int c = ca_obj_order_cmp(v, query_val);
|
|
7968
8060
|
if ( c < 0 ) lo = mid + 1;
|
|
7969
8061
|
else if ( c > 0 ) hi = mid;
|
|
7970
|
-
else { result = mid;
|
|
8062
|
+
else { result = mid; hi = mid; } /* keep looking left: the first equal cell */
|
|
7971
8063
|
}
|
|
7972
8064
|
C
|
|
7973
8065
|
fixlen: <<~C,
|
|
@@ -7981,7 +8073,7 @@ MkKernel.search :bsearch_addr,
|
|
|
7981
8073
|
int c = memcmp(v, query_val, (size_t) ca->bytes);
|
|
7982
8074
|
if ( c < 0 ) lo = mid + 1;
|
|
7983
8075
|
else if ( c > 0 ) hi = mid;
|
|
7984
|
-
else { result = mid;
|
|
8076
|
+
else { result = mid; hi = mid; } /* keep looking left: the first equal cell */
|
|
7985
8077
|
}
|
|
7986
8078
|
C
|
|
7987
8079
|
},
|
|
@@ -8152,13 +8244,13 @@ MkKernel.search :linear_section_binary,
|
|
|
8152
8244
|
if ( slab_n <= 1 ) {
|
|
8153
8245
|
/* A one-point axis has no segment: only the point itself is in
|
|
8154
8246
|
* range, at 0. An empty one has nothing in range. */
|
|
8155
|
-
if ( slab_n == 1 && query_val == *(
|
|
8247
|
+
if ( slab_n == 1 && query_val == *(ca_u_float64_t *)slab_ptr ) {
|
|
8156
8248
|
result = 0.0;
|
|
8157
8249
|
}
|
|
8158
8250
|
}
|
|
8159
8251
|
else {
|
|
8160
|
-
double y0 = *(
|
|
8161
|
-
double yn = *(
|
|
8252
|
+
double y0 = *(ca_u_float64_t *)(slab_ptr + 0 * slab_stride);
|
|
8253
|
+
double yn = *(ca_u_float64_t *)(slab_ptr + (slab_n - 1) * slab_stride);
|
|
8162
8254
|
int ascending = ( yn >= y0 ); /* grid direction (monotonic assumed) */
|
|
8163
8255
|
ca_size_t x1 = 0;
|
|
8164
8256
|
int found = 0;
|
|
@@ -8181,8 +8273,8 @@ MkKernel.search :linear_section_binary,
|
|
|
8181
8273
|
double gd = (query_val - y0) / (yn - y0) * (double)(slab_n - 1);
|
|
8182
8274
|
if ( gd >= 0.0 && gd < (double)(slab_n - 1) ) {
|
|
8183
8275
|
ca_size_t ag = (ca_size_t) gd;
|
|
8184
|
-
double yga = *(
|
|
8185
|
-
double ygb = *(
|
|
8276
|
+
double yga = *(ca_u_float64_t *)(slab_ptr + ag * slab_stride);
|
|
8277
|
+
double ygb = *(ca_u_float64_t *)(slab_ptr + (ag + 1) * slab_stride);
|
|
8186
8278
|
if ( (yga - query_val) * (ygb - query_val) <= 0 ) {
|
|
8187
8279
|
x1 = ag; found = 1;
|
|
8188
8280
|
}
|
|
@@ -8197,7 +8289,7 @@ MkKernel.search :linear_section_binary,
|
|
|
8197
8289
|
double ya = y0;
|
|
8198
8290
|
while ( (b - a) > 1 ) {
|
|
8199
8291
|
ca_size_t c = (a + b) / 2;
|
|
8200
|
-
double yc = *(
|
|
8292
|
+
double yc = *(ca_u_float64_t *)(slab_ptr + c * slab_stride);
|
|
8201
8293
|
if ( (ya - query_val) * (yc - query_val) <= 0 ) { b = c; }
|
|
8202
8294
|
else { a = c; ya = yc; }
|
|
8203
8295
|
}
|
|
@@ -8206,8 +8298,8 @@ MkKernel.search :linear_section_binary,
|
|
|
8206
8298
|
}
|
|
8207
8299
|
|
|
8208
8300
|
if ( found ) {
|
|
8209
|
-
double y1 = *(
|
|
8210
|
-
double y2 = *(
|
|
8301
|
+
double y1 = *(ca_u_float64_t *)(slab_ptr + x1 * slab_stride);
|
|
8302
|
+
double y2 = *(ca_u_float64_t *)(slab_ptr + (x1 + 1) * slab_stride);
|
|
8211
8303
|
double rest = (query_val - y1) / (y2 - y1);
|
|
8212
8304
|
if ( y2 != 0.0 && fabs(y2 - query_val) / fabs(y2) < DBL_EPSILON * 100 ) {
|
|
8213
8305
|
result = (double)(x1 + 1);
|
|
@@ -8243,7 +8335,7 @@ MkKernel.search :linear_section_linear,
|
|
|
8243
8335
|
if ( slab_n <= 1 ) {
|
|
8244
8336
|
/* A one-point axis has no segment: only the point itself is in
|
|
8245
8337
|
* range, at 0. An empty one has nothing in range. */
|
|
8246
|
-
if ( slab_n == 1 && query_val == *(
|
|
8338
|
+
if ( slab_n == 1 && query_val == *(ca_u_float64_t *)slab_ptr ) {
|
|
8247
8339
|
result = 0.0;
|
|
8248
8340
|
}
|
|
8249
8341
|
}
|
|
@@ -8251,15 +8343,15 @@ MkKernel.search :linear_section_linear,
|
|
|
8251
8343
|
ca_size_t x1 = 0;
|
|
8252
8344
|
int found = 0;
|
|
8253
8345
|
for ( ca_size_t k = 0; k < slab_n - 1; k++ ) {
|
|
8254
|
-
double yk = *(
|
|
8255
|
-
double yk1 = *(
|
|
8346
|
+
double yk = *(ca_u_float64_t *)(slab_ptr + k * slab_stride);
|
|
8347
|
+
double yk1 = *(ca_u_float64_t *)(slab_ptr + (k + 1) * slab_stride);
|
|
8256
8348
|
if ( (query_val - yk) * (query_val - yk1) <= 0 ) {
|
|
8257
8349
|
x1 = k; found = 1; break;
|
|
8258
8350
|
}
|
|
8259
8351
|
}
|
|
8260
8352
|
if ( found ) {
|
|
8261
|
-
double y1 = *(
|
|
8262
|
-
double y2 = *(
|
|
8353
|
+
double y1 = *(ca_u_float64_t *)(slab_ptr + x1 * slab_stride);
|
|
8354
|
+
double y2 = *(ca_u_float64_t *)(slab_ptr + (x1 + 1) * slab_stride);
|
|
8263
8355
|
result = (query_val - y1) / (y2 - y1) + (double) x1;
|
|
8264
8356
|
}
|
|
8265
8357
|
|
|
@@ -8286,8 +8378,8 @@ MkKernel.search :linear_fetch,
|
|
|
8286
8378
|
ca_size_t il = (ca_size_t) floor(query_val);
|
|
8287
8379
|
ca_size_t iu = (ca_size_t) ceil(query_val);
|
|
8288
8380
|
double w = query_val - floor(query_val);
|
|
8289
|
-
double yl = *(
|
|
8290
|
-
double yu = *(
|
|
8381
|
+
double yl = *(ca_u_float64_t *)(slab_ptr + il * slab_stride);
|
|
8382
|
+
double yu = *(ca_u_float64_t *)(slab_ptr + iu * slab_stride);
|
|
8291
8383
|
result = yu * w + yl * (1.0 - w);
|
|
8292
8384
|
}
|
|
8293
8385
|
C
|
|
@@ -8847,7 +8939,7 @@ MkKernel.monfunc :rad_pi,
|
|
|
8847
8939
|
# Pair-wise max / min — three flavours:
|
|
8848
8940
|
#
|
|
8849
8941
|
# pmax / pmin CArray legacy names (kept for backwards compat),
|
|
8850
|
-
# NaN-skip semantics
|
|
8942
|
+
# NaN-skip semantics, as C99 fmax / fmin.
|
|
8851
8943
|
# fmax / fmin Ruby-level aliases of pmax / pmin (= same kernel,
|
|
8852
8944
|
# same NaN-skip behaviour). Matches the C99 library
|
|
8853
8945
|
# function of the same name.
|
|
@@ -8857,12 +8949,15 @@ MkKernel.monfunc :rad_pi,
|
|
|
8857
8949
|
#
|
|
8858
8950
|
# Integer branches are identical across all three (no NaN). The float
|
|
8859
8951
|
# and object branches differ: pmax / pmin let a NaN lose (an object cell's
|
|
8860
|
-
# Float NaN included), maximum / minimum let it win.
|
|
8952
|
+
# Float NaN included), maximum / minimum let it win. In both flavours, of
|
|
8953
|
+
# two zeros the max is +0 and the min is -0 in either order. C's fmax /
|
|
8954
|
+
# fmin leave that choice to the library (glibc and macOS answer
|
|
8955
|
+
# differently), so the float branch does not call them.
|
|
8861
8956
|
MkKernel.binop :pmax,
|
|
8862
8957
|
source: MkKernel::ALL_NUMERIC + [:object],
|
|
8863
8958
|
expr: {
|
|
8864
8959
|
int: "(#3) = (#1) > (#2) ? (#1) : (#2);",
|
|
8865
|
-
float: "(#3) =
|
|
8960
|
+
float: "(#3) = isnan(#1) ? (#2) : isnan(#2) ? (#1) : ((#1) > (#2) || ((#1) == (#2) && !signbit(#1))) ? (#1) : (#2);",
|
|
8866
8961
|
object: '(#3) = ca_obj_extremum_replaces((#2), (#1), rb_intern(">")) ? (#2) : (#1);',
|
|
8867
8962
|
}
|
|
8868
8963
|
|
|
@@ -8870,7 +8965,7 @@ MkKernel.binop :pmin,
|
|
|
8870
8965
|
source: MkKernel::ALL_NUMERIC + [:object],
|
|
8871
8966
|
expr: {
|
|
8872
8967
|
int: "(#3) = (#1) < (#2) ? (#1) : (#2);",
|
|
8873
|
-
float: "(#3) =
|
|
8968
|
+
float: "(#3) = isnan(#1) ? (#2) : isnan(#2) ? (#1) : ((#1) < (#2) || ((#1) == (#2) && signbit(#1))) ? (#1) : (#2);",
|
|
8874
8969
|
object: '(#3) = ca_obj_extremum_replaces((#2), (#1), rb_intern("<")) ? (#2) : (#1);',
|
|
8875
8970
|
}
|
|
8876
8971
|
|