carray 3.0.3 → 3.0.4
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 +264 -0
- data/README.md +3 -2
- data/carray.gemspec +1 -1
- data/ext/ca_axis_descriptor.h +27 -24
- data/ext/ca_axis_dispatch.c +1 -6
- data/ext/ca_axis_group.c +27 -27
- data/ext/ca_binop_dispatch.c +1 -29
- data/ext/ca_binop_dispatch.h +0 -1
- data/ext/ca_categorical_iterator.c +205 -134
- data/ext/ca_compare.c +7 -18
- data/ext/ca_group_iter.c +16 -17
- data/ext/ca_kernel_iterator.c +1234 -1994
- data/ext/ca_kernel_iterator.h +391 -437
- data/ext/ca_monop_dispatch.c +82 -59
- data/ext/ca_monop_dispatch.h +1 -0
- data/ext/ca_obj_array.c +96 -39
- data/ext/ca_obj_bincmp.c +24 -21
- data/ext/ca_obj_binop.c +15 -26
- data/ext/ca_obj_bitarray.c +3 -2
- data/ext/ca_obj_bitfield.c +5 -6
- data/ext/ca_obj_block.c +18 -9
- data/ext/ca_obj_byte_swap.c +5 -6
- data/ext/ca_obj_const_string.c +173 -19
- data/ext/ca_obj_face.c +238 -57
- data/ext/ca_obj_face.h +19 -0
- data/ext/ca_obj_fake.c +15 -29
- data/ext/ca_obj_farray.c +6 -3
- data/ext/ca_obj_field.c +17 -5
- data/ext/ca_obj_fixlen_string.c +2 -3
- data/ext/ca_obj_grid.c +406 -132
- data/ext/ca_obj_meld.c +172 -34
- data/ext/ca_obj_moncmp.c +23 -8
- data/ext/ca_obj_monop.c +67 -57
- data/ext/ca_obj_object.c +157 -51
- data/ext/ca_obj_record.c +12 -7
- data/ext/ca_obj_reduce.c +13 -8
- data/ext/ca_obj_refer.c +25 -27
- data/ext/ca_obj_remap.c +30 -9
- data/ext/ca_obj_repeat.c +9 -10
- data/ext/ca_obj_roll.c +6 -5
- data/ext/ca_obj_select.c +14 -9
- data/ext/ca_obj_select_axis.c +15 -47
- data/ext/ca_obj_shift.c +11 -1
- data/ext/ca_obj_stack.c +179 -34
- data/ext/ca_obj_stride.c +162 -115
- data/ext/ca_obj_string.c +71 -4
- data/ext/ca_obj_tile.c +6 -5
- data/ext/ca_obj_time.c +22 -6
- data/ext/ca_obj_timedelta.c +22 -6
- data/ext/ca_obj_transpose.c +5 -3
- data/ext/ca_obj_triop.c +23 -32
- data/ext/ca_obj_window.c +190 -26
- data/ext/ca_sweep_engine.c +2 -2
- data/ext/caframe_csv_reader.c +80 -1
- data/ext/carray.h +181 -75
- data/ext/carray_access.c +184 -96
- data/ext/carray_address_basis.c +343 -87
- data/ext/carray_attribute.c +89 -6
- data/ext/carray_bincount.c +10 -8
- data/ext/carray_broadcast.c +13 -8
- data/ext/carray_build_flags.h +3 -0
- data/ext/carray_call_cfunc.c +108 -0
- data/ext/carray_cast.c +142 -24
- data/ext/carray_cast_func.rb +46 -6
- data/ext/carray_class.c +4 -3
- data/ext/carray_config.h +28 -0
- data/ext/carray_conversion.c +16 -7
- data/ext/carray_copy.c +51 -14
- data/ext/carray_core.c +147 -55
- data/ext/carray_count.c +50 -20
- data/ext/carray_element.c +76 -33
- data/ext/carray_factorize.c +118 -64
- data/ext/carray_generate.c +191 -50
- data/ext/carray_histogram.c +62 -17
- data/ext/carray_hold.c +12 -15
- data/ext/carray_index_classifier.c +388 -176
- data/ext/carray_internal.h +336 -29
- data/ext/carray_kernels_bincmp.c +4453 -0
- data/ext/carray_kernels_binop.c +11368 -0
- data/ext/carray_kernels_init.c +1734 -0
- data/ext/carray_kernels_moncmp.c +2104 -0
- data/ext/carray_kernels_monop.c +18369 -0
- data/ext/carray_kernels_reduce_aggregate.c +23818 -0
- data/ext/carray_kernels_reduce_boolean.c +406 -0
- data/ext/carray_kernels_reduce_cumulative.c +10086 -0
- data/ext/carray_kernels_reduce_extreme.c +17634 -0
- data/ext/carray_kernels_reduce_variance.c +3849 -0
- data/ext/carray_kernels_scan.c +4302 -0
- data/ext/carray_kernels_search.c +33829 -0
- data/ext/carray_kernels_sort.c +10906 -0
- data/ext/carray_kernels_triop.c +1864 -0
- data/ext/carray_key_windows.c +216 -0
- data/ext/carray_lazy.c +170 -111
- data/ext/carray_mask.c +292 -72
- data/ext/carray_median_percentile.c +82 -28
- data/ext/carray_memory_view.c +669 -561
- data/ext/carray_operator.c +124 -47
- data/ext/carray_order.c +384 -76
- data/ext/carray_partition.c +28 -27
- data/ext/carray_random.c +67 -13
- data/ext/carray_round.c +273 -0
- data/ext/carray_scatter.c +63 -38
- data/ext/carray_segment.c +3 -3
- data/ext/carray_slab.c +91 -28
- data/ext/carray_sort.c +35 -35
- data/ext/carray_struct.c +19 -18
- data/ext/carray_test.c +26 -5
- data/ext/carray_undef.c +4 -0
- data/ext/carray_utils.c +246 -45
- data/ext/mk_call_cfunc.rb +19 -2
- data/ext/mkkernel.rb +198 -240
- data/ext/ruby_carray.c +10 -4
- data/ext/version.h +4 -4
- data/lib/carray/attribute.rb +126 -22
- data/lib/carray/autoload_carray.rb +8 -31
- data/lib/carray/autoload_method_extension.rb +14 -5
- data/lib/carray/axis_group.rb +255 -106
- data/lib/carray/basics.rb +78 -10
- data/lib/carray/bincount_nd.rb +12 -2
- data/lib/carray/block_iterator.rb +126 -23
- data/lib/carray/boolean_reduce.rb +17 -17
- data/lib/carray/categorical.rb +165 -43
- data/lib/carray/categorical_iterator.rb +130 -27
- data/lib/carray/complex.rb +5 -5
- data/lib/carray/conditional.rb +67 -18
- data/lib/carray/const_string.rb +14 -5
- data/lib/carray/construct.rb +15 -11
- data/lib/carray/data_type_extension.rb +1 -0
- data/lib/carray/distance_limit.rb +77 -0
- data/lib/carray/fixlen_string.rb +7 -1
- data/lib/carray/frame/column_verbs.rb +70 -0
- data/lib/carray/frame/convert.rb +69 -0
- data/lib/carray/frame/csv_parser.rb +111 -17
- data/lib/carray/frame/describe.rb +3 -2
- data/lib/carray/frame/frame.rb +246 -21
- data/lib/carray/frame/group.rb +510 -95
- data/lib/carray/frame/io.rb +115 -28
- data/lib/carray/frame/join.rb +312 -51
- data/lib/carray/frame/records.rb +83 -7
- data/lib/carray/frame/reshape.rb +42 -35
- data/lib/carray/frame/sort.rb +7 -19
- data/lib/carray/frame/verbs.rb +188 -48
- data/lib/carray/frame/windows.rb +117 -0
- data/lib/carray/frame.rb +2 -0
- data/lib/carray/fuse_source.rb +11 -4
- data/lib/carray/fusion.rb +40 -16
- data/lib/carray/histogram.rb +6 -1
- data/lib/carray/inspect.rb +31 -8
- data/lib/carray/iterator.rb +85 -7
- data/lib/carray/key_windows.rb +620 -0
- data/lib/carray/lazy.rb +182 -183
- data/lib/carray/mask_gap_fill.rb +63 -57
- data/lib/carray/meld_reduce.rb +12 -14
- data/lib/carray/methods/bin.rb +26 -18
- data/lib/carray/methods/bincount.rb +16 -0
- data/lib/carray/methods/bit_string.rb +1 -1
- data/lib/carray/methods/choose.rb +16 -3
- data/lib/carray/methods/composition.rb +3 -1
- data/lib/carray/methods/gather_nd.rb +14 -5
- data/lib/carray/methods/insert_block.rb +2 -2
- data/lib/carray/methods/is_between.rb +59 -0
- data/lib/carray/methods/is_in.rb +53 -36
- data/lib/carray/methods/join.rb +1 -6
- data/lib/carray/methods/lag.rb +49 -0
- data/lib/carray/methods/locate_addr.rb +47 -51
- data/lib/carray/methods/mode.rb +1 -1
- data/lib/carray/methods/nunique.rb +11 -6
- data/lib/carray/methods/resize.rb +3 -3
- data/lib/carray/methods/value_counts.rb +4 -11
- data/lib/carray/record_order.rb +3 -3
- data/lib/carray/rng.rb +3 -0
- data/lib/carray/runtime.rb +53 -8
- data/lib/carray/segment_iterator.rb +313 -82
- data/lib/carray/serialize.rb +24 -10
- data/lib/carray/slab_iterator.rb +50 -0
- data/lib/carray/string.rb +105 -5
- data/lib/carray/string_operation_extension.rb +8 -1
- data/lib/carray/struct.rb +9 -21
- data/lib/carray/struct_builder.rb +19 -8
- data/lib/carray/time.rb +726 -104
- data/lib/carray/window_iterator.rb +122 -16
- data/lib/carray.rb +7 -0
- data/yard-stubs/ca_obj_bitfield.rb +1 -1
- data/yard-stubs/ca_obj_field.rb +4 -0
- data/yard-stubs/ca_obj_grid.rb +21 -10
- data/yard-stubs/ca_obj_refer.rb +2 -1
- data/yard-stubs/ca_obj_roll.rb +2 -2
- data/yard-stubs/ca_obj_shift.rb +5 -1
- data/yard-stubs/ca_obj_tile.rb +1 -1
- data/yard-stubs/ca_obj_window.rb +8 -3
- data/yard-stubs/carray_access.rb +6 -1
- data/yard-stubs/carray_attribute.rb +0 -5
- data/yard-stubs/carray_cast.rb +10 -0
- data/yard-stubs/carray_conversion.rb +2 -1
- data/yard-stubs/carray_copy.rb +4 -2
- data/yard-stubs/carray_count.rb +4 -4
- data/yard-stubs/carray_element.rb +10 -4
- data/yard-stubs/carray_generate.rb +21 -1
- data/yard-stubs/carray_mask.rb +26 -2
- data/yard-stubs/carray_math.rb +27 -1
- data/yard-stubs/carray_median_percentile.rb +5 -2
- data/yard-stubs/carray_memory_view.rb +40 -23
- data/yard-stubs/carray_order.rb +29 -18
- data/yard-stubs/carray_random.rb +13 -4
- data/yard-stubs/carray_scatter.rb +14 -9
- data/yard-stubs/carray_slab.rb +9 -2
- data/yard-stubs/carray_sort.rb +6 -5
- data/yard-stubs/carray_test.rb +5 -3
- metadata +25 -3
- data/lib/carray/arrow_tensor.rb +0 -401
- data/lib/carray/table.rb +0 -108
data/ext/mkkernel.rb
CHANGED
|
@@ -177,10 +177,8 @@ module MkKernel
|
|
|
177
177
|
# expr: { object: "..." }.merge(float_widths("(#2) = sin<f>(#1);"))
|
|
178
178
|
#
|
|
179
179
|
# Not every double-taking call wants this. `fabs`, `fmin` / `fmax`,
|
|
180
|
-
# `ceil` / `floor` / `trunc` and the `isnan` family are exact
|
|
181
|
-
# float either way
|
|
182
|
-
# double -- narrowing it would round `x + 0.5f` first and step the
|
|
183
|
-
# answer at the boundary.
|
|
180
|
+
# `ceil` / `floor` / `trunc` / `round` and the `isnan` family are exact
|
|
181
|
+
# on a float either way.
|
|
184
182
|
def self.float_widths(body)
|
|
185
183
|
{
|
|
186
184
|
[:f32] => at_width(body, "f", "float"),
|
|
@@ -706,8 +704,13 @@ module MkKernel
|
|
|
706
704
|
# the input is masked). Aligns core cummax / cummin with
|
|
707
705
|
# the reduction contract (empty max / min = UNDEF, no
|
|
708
706
|
# identity) and the axis-group scan family.
|
|
707
|
+
# private:
|
|
708
|
+
# false (default) -> bound as the public `<name>_ki`
|
|
709
|
+
# true -> bound only as the private method `<name>`, a part
|
|
710
|
+
# that lib reaches with `send` (not user surface)
|
|
709
711
|
def self.scan(name, source:, output:, init:, step:, fallback: :raise,
|
|
710
|
-
acc_type: nil, axis_default: nil, empty: nil, face_gate: nil
|
|
712
|
+
acc_type: nil, axis_default: nil, empty: nil, face_gate: nil,
|
|
713
|
+
private: false)
|
|
711
714
|
raise "duplicate kernel #{name}" if KERNELS.any? { |k| k[:name] == name }
|
|
712
715
|
source.each do |s|
|
|
713
716
|
raise "unknown source data_type #{s}" unless DTYPES.key?(s)
|
|
@@ -749,6 +752,7 @@ module MkKernel
|
|
|
749
752
|
axis_default: axis_default,
|
|
750
753
|
empty: empty,
|
|
751
754
|
face_gate: face_gate,
|
|
755
|
+
private: private,
|
|
752
756
|
}
|
|
753
757
|
end
|
|
754
758
|
|
|
@@ -944,7 +948,7 @@ module MkKernel
|
|
|
944
948
|
# is up to the body (= value-returning search variants).
|
|
945
949
|
def self.search(name, source:, output:, body:,
|
|
946
950
|
no_match: "-1", no_match_check: nil, undef_no_match: false,
|
|
947
|
-
mask_self: :raise,
|
|
951
|
+
mask_self: :raise, runtime_args: nil,
|
|
948
952
|
fallback: :raise, semantics: :fiber_local)
|
|
949
953
|
raise "search: unknown semantics #{semantics} (expected :fiber_local or :view_flat)" \
|
|
950
954
|
unless %i[fiber_local view_flat].include?(semantics)
|
|
@@ -956,12 +960,10 @@ module MkKernel
|
|
|
956
960
|
raise "search: no_match must be a String" unless no_match.is_a?(String)
|
|
957
961
|
raise "search: unknown mask_self #{mask_self} (expected :raise | :skip | :ignore)" \
|
|
958
962
|
unless %i[raise skip ignore].include?(mask_self)
|
|
959
|
-
#
|
|
960
|
-
#
|
|
961
|
-
#
|
|
962
|
-
#
|
|
963
|
-
raise "search: unknown mask_query #{mask_query} (expected :ignore | :undef)" \
|
|
964
|
-
unless %i[ignore undef].include?(mask_query)
|
|
963
|
+
# A masked query cell (or a scalar UNDEF query) has no answer: its output
|
|
964
|
+
# cell is UNDEF, so an undetermined query cannot become a determined
|
|
965
|
+
# result from the value that happens to sit under the mask. There is no
|
|
966
|
+
# option to read under the mask.
|
|
965
967
|
raise "search: unknown fallback #{fallback} (expected :raise)" \
|
|
966
968
|
unless fallback == :raise
|
|
967
969
|
|
|
@@ -1026,7 +1028,6 @@ module MkKernel
|
|
|
1026
1028
|
no_match_check: no_match_check,
|
|
1027
1029
|
undef_no_match: undef_no_match,
|
|
1028
1030
|
mask_self: mask_self,
|
|
1029
|
-
mask_query: mask_query,
|
|
1030
1031
|
runtime_args: runtime_args || [],
|
|
1031
1032
|
fallback: fallback,
|
|
1032
1033
|
semantics: semantics,
|
|
@@ -2178,20 +2179,22 @@ module MkKernel
|
|
|
2178
2179
|
else
|
|
2179
2180
|
if k[:array_arg]
|
|
2180
2181
|
wc = (k[:array_arg][:data_type] == :promote) ? "double" : si[:c]
|
|
2181
|
-
io.puts "
|
|
2182
|
+
io.puts " ca_size_t __sra_throwaway_mc = 0;"
|
|
2183
|
+
io.puts " CA_SLAB_REDUCE_ARRAY_T_EX(#{si[:c]}, #{wc}, st, p, m, st_w, p_w, #{acc_var}, #{acc_init}, #{reduce_stmt}, __sra_throwaway_mc);"
|
|
2184
|
+
io.puts " (void) __sra_throwaway_mc;"
|
|
2182
2185
|
else
|
|
2183
2186
|
suffix = MkKernel.reduce_macro_suffix(k, src)
|
|
2184
|
-
# Non-mask_policy path:
|
|
2185
|
-
#
|
|
2186
|
-
#
|
|
2187
|
-
if
|
|
2187
|
+
# Non-mask_policy path: the slab macros always take a masked_cnt,
|
|
2188
|
+
# so hand them a throwaway. The generic form (suffix "", the
|
|
2189
|
+
# :object lane) has a wrapper that hides it.
|
|
2190
|
+
if suffix == ""
|
|
2191
|
+
io.puts " CA_SLAB_REDUCE_T(#{si[:c]}, st, p, m, #{acc_var}, #{acc_init}, #{reduce_stmt});"
|
|
2192
|
+
else
|
|
2188
2193
|
io.puts " ca_size_t __sr_throwaway_mc = 0;"
|
|
2189
2194
|
emit_reduce_slab_call(io, k, src, si, oi, suffix, acc_var, acc_init,
|
|
2190
2195
|
reduce_stmt, "__sr_throwaway_mc",
|
|
2191
2196
|
indent: " ")
|
|
2192
2197
|
io.puts " (void) __sr_throwaway_mc;"
|
|
2193
|
-
else
|
|
2194
|
-
io.puts " CA_SLAB_REDUCE_T#{suffix}(#{si[:c]}, st, p, m, #{acc_var}, #{acc_init}, #{reduce_stmt});"
|
|
2195
2198
|
end
|
|
2196
2199
|
end
|
|
2197
2200
|
finish_emit = view_flat \
|
|
@@ -3549,9 +3552,12 @@ module MkKernel
|
|
|
3549
3552
|
wio.puts " else {"
|
|
3550
3553
|
wio.puts " /* CArray val: coerce to the weight storage type (f64 for :promote, so a"
|
|
3551
3554
|
wio.puts " float weight against an int source is not truncated) */"
|
|
3552
|
-
wio.puts " vcw
|
|
3555
|
+
wio.puts " /* vcv holds the view while cv is read: W-A2 below moves vcw on to"
|
|
3556
|
+
wio.puts " the materialised copy before it is done with cv. */"
|
|
3557
|
+
wio.puts " volatile VALUE vcv = rb_ca_wrap_readonly(rweights, INT2NUM(#{waca}));"
|
|
3553
3558
|
wio.puts " CArray *cv;"
|
|
3554
|
-
wio.puts " GetCArray(
|
|
3559
|
+
wio.puts " GetCArray(vcv, cv);"
|
|
3560
|
+
wio.puts " vcw = vcv;"
|
|
3555
3561
|
wio.puts ""
|
|
3556
3562
|
wio.puts " /* rev5 form detection: A3 (same ndim) -> A2 (1-D axis-broadcast) -> raise */"
|
|
3557
3563
|
wio.puts " if ( cv->ndim == src->ndim ) {"
|
|
@@ -3813,14 +3819,16 @@ module MkKernel
|
|
|
3813
3819
|
|
|
3814
3820
|
# empty: :undef -> allocate the output mask up front when the input
|
|
3815
3821
|
# carries one (a fiber can only go UNDEF on its leading masked cells,
|
|
3816
|
-
# which exist only when the input is masked).
|
|
3817
|
-
#
|
|
3818
|
-
# the
|
|
3822
|
+
# which exist only when the input is masked). The output is a fresh
|
|
3823
|
+
# row-major entity, and the walk cuts its slabs from a buffer laid out
|
|
3824
|
+
# the same way (the output's memory, or the walk's scratch when it
|
|
3825
|
+
# delivers by transfer), so a slab's offset from st_out.alias_ptr is
|
|
3826
|
+
# its offset in the output, and op_mask + that offset / cell size is the
|
|
3827
|
+
# fiber's mask base.
|
|
3819
3828
|
mask_setup =
|
|
3820
3829
|
if gated
|
|
3821
3830
|
"\n" \
|
|
3822
3831
|
" boolean8_t *op_mask = NULL;\n" \
|
|
3823
|
-
" char *co_root = (char *) co->ptr;\n" \
|
|
3824
3832
|
" if ( ca_has_mask(ca) ) {\n" \
|
|
3825
3833
|
" ca_create_mask(co);\n" \
|
|
3826
3834
|
" op_mask = (boolean8_t *) co->mask->ptr;\n" \
|
|
@@ -3835,7 +3843,7 @@ module MkKernel
|
|
|
3835
3843
|
" st_out, po, #{init_expr}, #{step_stmt});"
|
|
3836
3844
|
elsif gated
|
|
3837
3845
|
"boolean8_t *mo_fiber = op_mask\n" \
|
|
3838
|
-
" ? op_mask + (ca_size_t)(po -
|
|
3846
|
+
" ? op_mask + (ca_size_t)(po - st_out.alias_ptr) / (ca_size_t) co->bytes\n" \
|
|
3839
3847
|
" : NULL;\n" \
|
|
3840
3848
|
" CA_SLAB_SCAN_T_GATED(#{si[:c]}, #{oi[:c]}, st_in, pi, mi,\n" \
|
|
3841
3849
|
" st_out, po, mo_fiber, #{init_expr}, #{step_stmt});"
|
|
@@ -3968,11 +3976,7 @@ module MkKernel
|
|
|
3968
3976
|
io.puts " if ( TYPE(raxis) == T_ARRAY ) {"
|
|
3969
3977
|
io.puts %Q[ rb_raise(rb_eArgError, "%s: axis must be a single Integer (got Array); a scan over several axes is ambiguous, chain explicitly: a.#{name}(axis: 0).#{name}(axis: 1)", ca_calling_method_name());]
|
|
3970
3978
|
io.puts " }"
|
|
3971
|
-
io.puts " int axis = (
|
|
3972
|
-
io.puts " if ( axis < 0 ) axis += src->ndim;"
|
|
3973
|
-
io.puts " if ( axis < 0 || axis >= src->ndim ) {"
|
|
3974
|
-
io.puts %Q[ rb_raise(rb_eArgError, "%s: axis %d out of range for ndim %d", ca_calling_method_name(), NUM2INT(raxis), src->ndim);]
|
|
3975
|
-
io.puts " }"
|
|
3979
|
+
io.puts " int axis = ca_normalize_axis_arg(raxis, src->ndim, NULL);"
|
|
3976
3980
|
io.puts " switch ( src->data_type ) {"
|
|
3977
3981
|
k[:source].each do |s|
|
|
3978
3982
|
si = DTYPES[s]
|
|
@@ -4134,35 +4138,15 @@ module MkKernel
|
|
|
4134
4138
|
io.puts " const #{name}_pair_#{src} *pa = (const #{name}_pair_#{src} *) a;"
|
|
4135
4139
|
io.puts " const #{name}_pair_#{src} *pb = (const #{name}_pair_#{src} *) b;"
|
|
4136
4140
|
if is_object
|
|
4137
|
-
#
|
|
4138
|
-
#
|
|
4139
|
-
|
|
4140
|
-
|
|
4141
|
-
|
|
4142
|
-
#
|
|
4143
|
-
|
|
4144
|
-
io.puts "
|
|
4145
|
-
io.puts " int nan_b = CA_OBJ_ISNAN(pb->v);"
|
|
4146
|
-
io.puts " if ( nan_a && !nan_b ) return 1;"
|
|
4147
|
-
io.puts " if ( nan_b && !nan_a ) return -1;"
|
|
4148
|
-
io.puts " if ( !nan_a ) {"
|
|
4149
|
-
io.puts %Q{ VALUE r = rb_funcall(pa->v, rb_intern("<=>"), 1, pb->v);}
|
|
4150
|
-
io.puts " if ( NIL_P(r) ) {"
|
|
4151
|
-
io.puts " ca_obj_compare_refused(pa->v, pb->v);"
|
|
4152
|
-
io.puts " }"
|
|
4153
|
-
io.puts " int c = NUM2INT(r);"
|
|
4154
|
-
io.puts " if ( c > 0 ) return 1;"
|
|
4155
|
-
io.puts " if ( c < 0 ) return -1;"
|
|
4156
|
-
io.puts " }"
|
|
4141
|
+
# The object order: `<=>`, a Float NaN after every other value
|
|
4142
|
+
# (ca_obj_order_cmp); two NaNs fall to the index tie-break.
|
|
4143
|
+
io.puts " int c = ca_obj_order_cmp(pa->v, pb->v);"
|
|
4144
|
+
io.puts " if ( c != 0 ) return c;"
|
|
4145
|
+
elsif is_fp
|
|
4146
|
+
# The float order: NaN after every number (CA_FLOAT_ORDER_CMP).
|
|
4147
|
+
io.puts " int c = CA_FLOAT_ORDER_CMP(pa->v, pb->v);"
|
|
4148
|
+
io.puts " if ( c != 0 ) return c;"
|
|
4157
4149
|
else
|
|
4158
|
-
if is_fp
|
|
4159
|
-
# NaN policy :end -- NaN sorts to the end (matches qcmp_f_type in
|
|
4160
|
-
# carray_sort.c).
|
|
4161
|
-
io.puts " int nan_a = isnan(pa->v);"
|
|
4162
|
-
io.puts " int nan_b = isnan(pb->v);"
|
|
4163
|
-
io.puts " if ( nan_a && !nan_b ) return 1;"
|
|
4164
|
-
io.puts " if ( nan_b && !nan_a ) return -1;"
|
|
4165
|
-
end
|
|
4166
4150
|
io.puts " if ( pa->v > pb->v ) return 1;"
|
|
4167
4151
|
io.puts " if ( pa->v < pb->v ) return -1;"
|
|
4168
4152
|
end
|
|
@@ -4306,17 +4290,21 @@ module MkKernel
|
|
|
4306
4290
|
io.puts " #{name}_pair_#{src} *aux = ALLOCV_N(#{name}_pair_#{src}, haux, fiber_n);"
|
|
4307
4291
|
end
|
|
4308
4292
|
else
|
|
4309
|
-
|
|
4310
|
-
|
|
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
|
+
io.puts " volatile VALUE hbuf = 0, haux = 0;"
|
|
4297
|
+
io.puts " #{name}_pair_#{src} *buf = ALLOCV_N(#{name}_pair_#{src}, hbuf, fiber_n);"
|
|
4311
4298
|
end
|
|
4312
4299
|
if has_kind && src != :object
|
|
4313
|
-
#
|
|
4314
|
-
#
|
|
4315
|
-
# Allocated once outside the fiber loop, reused across fibers.
|
|
4300
|
+
# The aux pair buffer for ca_sort_merge_pair_<src> (a ping-pong
|
|
4301
|
+
# bottom-up mergesort), taken once outside the fiber loop.
|
|
4316
4302
|
io.puts " #{name}_pair_#{src} *aux = NULL;"
|
|
4317
4303
|
io.puts " if ( do_stable ) {"
|
|
4318
|
-
io.puts " aux =
|
|
4304
|
+
io.puts " aux = ALLOCV_N(#{name}_pair_#{src}, haux, fiber_n);"
|
|
4319
4305
|
io.puts " }"
|
|
4306
|
+
elsif src != :object
|
|
4307
|
+
io.puts " (void) haux;"
|
|
4320
4308
|
end
|
|
4321
4309
|
|
|
4322
4310
|
if view_flat
|
|
@@ -4502,16 +4490,21 @@ module MkKernel
|
|
|
4502
4490
|
io.puts " gaps ordinal would have spent on the tie are never used, so a"
|
|
4503
4491
|
io.puts " later out_r stays <= the ordinal r at that position, preserving"
|
|
4504
4492
|
io.puts " both the ascending order and (via the order() method's"
|
|
4505
|
-
io.puts "
|
|
4506
|
-
io.puts "
|
|
4493
|
+
io.puts " descending transform) the descending order. NaN cells tie"
|
|
4494
|
+
io.puts " with each other, as sort_addr and the value-hash family treat"
|
|
4495
|
+
io.puts " every NaN as one value. */"
|
|
4507
4496
|
io.puts " ca_size_t out_r = 0;"
|
|
4508
4497
|
io.puts " for ( ca_size_t r = 0; r < #{rank_n}; r++ ) {"
|
|
4509
4498
|
io.puts " if ( r > 0 ) {"
|
|
4510
4499
|
io.puts " if ( dense ) {"
|
|
4511
4500
|
if is_object
|
|
4512
|
-
io.puts " if ( !
|
|
4501
|
+
io.puts " if ( ! ( ( CA_OBJ_ISNAN(buf[r].v) && CA_OBJ_ISNAN(buf[r - 1].v) )"
|
|
4502
|
+
io.puts " || ca_obj_equal(buf[r].v, buf[r - 1].v) ) ) out_r++;"
|
|
4513
4503
|
elsif is_fixlen
|
|
4514
4504
|
io.puts " if ( memcmp(buf[r].vp, buf[r - 1].vp, (size_t) buf[r].nb) != 0 ) out_r++;"
|
|
4505
|
+
elsif FLOAT_DTYPES.include?(src)
|
|
4506
|
+
io.puts " if ( buf[r].v != buf[r - 1].v"
|
|
4507
|
+
io.puts " && ! ( isnan(buf[r].v) && isnan(buf[r - 1].v) ) ) out_r++;"
|
|
4515
4508
|
else
|
|
4516
4509
|
io.puts " if ( buf[r].v != buf[r - 1].v ) out_r++;"
|
|
4517
4510
|
end
|
|
@@ -4521,15 +4514,32 @@ module MkKernel
|
|
|
4521
4514
|
io.puts " }"
|
|
4522
4515
|
io.puts " *(#{oi[:c]} *) (po + buf[r].i * ostride) = (#{oi[:c]}) out_r;"
|
|
4523
4516
|
io.puts " }"
|
|
4517
|
+
# dense == 2 (from CArray#order only): descending dense ranks, the
|
|
4518
|
+
# largest value 0, the NaN cells (sorted to the end) after the numbers.
|
|
4519
|
+
nan_test = if is_object then "CA_OBJ_ISNAN(buf[nan_start - 1].v)"
|
|
4520
|
+
elsif FLOAT_DTYPES.include?(src) then "isnan(buf[nan_start - 1].v)"
|
|
4521
|
+
end
|
|
4522
|
+
io.puts " if ( dense == 2 && #{rank_n} > 0 ) {"
|
|
4523
|
+
io.puts " ca_size_t nan_start = #{rank_n};"
|
|
4524
|
+
io.puts " while ( nan_start > 0 && #{nan_test} ) nan_start--;" if nan_test
|
|
4525
|
+
io.puts " #{oi[:c]} top = nan_start > 0"
|
|
4526
|
+
io.puts " ? *(#{oi[:c]} *) (po + buf[nan_start - 1].i * ostride) : (#{oi[:c]}) -1;"
|
|
4527
|
+
io.puts " for ( ca_size_t r = 0; r < #{rank_n}; r++ ) {"
|
|
4528
|
+
io.puts " #{oi[:c]} *o = (#{oi[:c]} *) (po + buf[r].i * ostride);"
|
|
4529
|
+
io.puts " *o = ( r < nan_start ) ? top - *o : top + 1;"
|
|
4530
|
+
io.puts " }"
|
|
4531
|
+
io.puts " }"
|
|
4524
4532
|
if mask_skip
|
|
4525
4533
|
io.puts " /* Mirror input mask to output (= UNDEF rank for masked"
|
|
4526
|
-
io.puts " cells). Write via co->mask->ptr directly
|
|
4527
|
-
io.puts "
|
|
4528
|
-
io.puts "
|
|
4529
|
-
io.puts "
|
|
4534
|
+
io.puts " cells). Write via co->mask->ptr directly, because a"
|
|
4535
|
+
io.puts " mask written through a WRITE walk's cursor does not"
|
|
4536
|
+
io.puts " reach the array. co is a fresh row-major entity and"
|
|
4537
|
+
io.puts " the walk cuts its slabs from a buffer laid out the"
|
|
4538
|
+
io.puts " same way, so the slab's offset from st_out.alias_ptr"
|
|
4539
|
+
io.puts " is its offset in co. */"
|
|
4530
4540
|
io.puts " if ( mi && co->mask ) {"
|
|
4531
4541
|
io.puts " boolean8_t *omask_base = (boolean8_t *) co->mask->ptr"
|
|
4532
|
-
io.puts " + ((po -
|
|
4542
|
+
io.puts " + ((po - st_out.alias_ptr) / (ca_size_t) co->bytes);"
|
|
4533
4543
|
io.puts " ca_size_t mostride = st_out.slab_mask_strides[0];"
|
|
4534
4544
|
io.puts " for ( ca_size_t k = 0; k < fiber_n; k++ ) {"
|
|
4535
4545
|
io.puts " if ( mi[k * mstride] ) omask_base[k * mostride] = 1;"
|
|
@@ -4555,45 +4565,33 @@ module MkKernel
|
|
|
4555
4565
|
end
|
|
4556
4566
|
|
|
4557
4567
|
io.puts " }"
|
|
4558
|
-
|
|
4559
|
-
|
|
4560
|
-
io.puts " ALLOCV_END(haux);"
|
|
4561
|
-
else
|
|
4562
|
-
io.puts " xfree(buf);"
|
|
4563
|
-
if has_kind
|
|
4564
|
-
io.puts " if ( aux ) xfree(aux);"
|
|
4565
|
-
end
|
|
4566
|
-
end
|
|
4568
|
+
io.puts " ALLOCV_END(hbuf);"
|
|
4569
|
+
io.puts " ALLOCV_END(haux);"
|
|
4567
4570
|
io.puts " ca_iter_state_finish(&st_in);"
|
|
4568
4571
|
io.puts " ca_iter_state_finish(&st_out);"
|
|
4569
4572
|
io.puts " return vout;"
|
|
4570
4573
|
io.puts "}"
|
|
4571
4574
|
end
|
|
4572
4575
|
|
|
4573
|
-
# Shared sort-family Face gate.
|
|
4574
|
-
#
|
|
4575
|
-
#
|
|
4576
|
-
#
|
|
4577
|
-
#
|
|
4578
|
-
#
|
|
4576
|
+
# Shared sort-family Face gate. Every kernel of the family answers
|
|
4577
|
+
# positions, so it runs on the array the Face's order is read from
|
|
4578
|
+
# (ca_order_key): the storage of an ORDERABLE Face, the Face's own dense
|
|
4579
|
+
# rank for a fixlen storage that is not ORDERABLE (CAConstString's offsets,
|
|
4580
|
+
# a record's bytes), and a refusal otherwise. `src`/`self` are rebound to
|
|
4581
|
+
# that array.
|
|
4579
4582
|
def self.emit_sort_face_gate(io, name)
|
|
4580
4583
|
io.puts " if ( ca_is_face(src) ) {"
|
|
4581
|
-
io.puts "
|
|
4582
|
-
io.puts "
|
|
4583
|
-
io.puts " VALUE _st = rb_ca_strip_face_value(self);"
|
|
4584
|
-
io.puts " CArray *_sc;"
|
|
4585
|
-
io.puts " GetCArray(_st, _sc);"
|
|
4586
|
-
io.puts " if ( _sc->data_type != CA_FIXLEN && ! _orderable ) {"
|
|
4587
|
-
io.puts %Q[ ca_face_refuse_order(ca_calling_method_name(), _face, "order");]
|
|
4588
|
-
io.puts " }"
|
|
4589
|
-
io.puts " self = _st;"
|
|
4590
|
-
io.puts " src = _sc;"
|
|
4584
|
+
io.puts " self = ca_order_key(self, ca_calling_method_name());"
|
|
4585
|
+
io.puts " GetCArray(self, src);"
|
|
4591
4586
|
io.puts " }"
|
|
4592
4587
|
end
|
|
4593
4588
|
|
|
4594
|
-
# Reduce-family Face gate.
|
|
4595
|
-
#
|
|
4596
|
-
#
|
|
4589
|
+
# Reduce-family Face gate. Descends an ORDERABLE Face to its storage and
|
|
4590
|
+
# refuses any other (ca_face_require_orderable): a fixlen storage is
|
|
4591
|
+
# reduced by memcmp only when the Face declares that its bytes are its
|
|
4592
|
+
# order (CAFixlenString). CAConstString and CARecord, whose bytes are no
|
|
4593
|
+
# order, answer min / max / minmax in Ruby and do not reach this gate.
|
|
4594
|
+
# self/src are rebound to storage. When
|
|
4597
4595
|
# `relift` is set (value-returning kernels), the original Face is saved to
|
|
4598
4596
|
# the function-scope `_reduce_face` so the dispatcher can re-lift the output.
|
|
4599
4597
|
def self.emit_reduce_face_gate(io, name, relift)
|
|
@@ -4602,13 +4600,11 @@ module MkKernel
|
|
|
4602
4600
|
end
|
|
4603
4601
|
io.puts " if ( ca_is_face(src) ) {"
|
|
4604
4602
|
io.puts " VALUE _face = self;"
|
|
4605
|
-
io.puts "
|
|
4606
|
-
io.puts " VALUE _st = rb_ca_strip_face_value(self);"
|
|
4603
|
+
io.puts " VALUE _st;"
|
|
4607
4604
|
io.puts " CArray *_sc;"
|
|
4605
|
+
io.puts %Q[ ca_face_require_orderable(_face, ca_calling_method_name(), "order");]
|
|
4606
|
+
io.puts " _st = rb_ca_strip_face_value(self);"
|
|
4608
4607
|
io.puts " GetCArray(_st, _sc);"
|
|
4609
|
-
io.puts " if ( _sc->data_type != CA_FIXLEN && ! _orderable ) {"
|
|
4610
|
-
io.puts %Q[ ca_face_refuse_order(ca_calling_method_name(), _face, "order");]
|
|
4611
|
-
io.puts " }"
|
|
4612
4608
|
if relift
|
|
4613
4609
|
io.puts " _reduce_face = _face;"
|
|
4614
4610
|
end
|
|
@@ -4713,10 +4709,10 @@ module MkKernel
|
|
|
4713
4709
|
io.puts " CArray *src;"
|
|
4714
4710
|
io.puts " GetCArray(self, src);"
|
|
4715
4711
|
emit_lazy_evaluated(io)
|
|
4716
|
-
io.puts " /* Face gate:
|
|
4717
|
-
io.puts "
|
|
4718
|
-
io.puts "
|
|
4719
|
-
io.puts "
|
|
4712
|
+
io.puts " /* Face gate: run on the array the Face's order is read from."
|
|
4713
|
+
io.puts " It is never a Face, so the switch below and every index/addr"
|
|
4714
|
+
io.puts " template output build on a plain array, not a face-lifted"
|
|
4715
|
+
io.puts " (NULL-ptr) view. */"
|
|
4720
4716
|
emit_sort_face_gate(io, name)
|
|
4721
4717
|
if k[:mask_self] == :raise
|
|
4722
4718
|
io.puts " /* sort family rejects masked sources globally (R3 / Q3): "
|
|
@@ -4725,11 +4721,7 @@ module MkKernel
|
|
|
4725
4721
|
io.puts %Q[ rb_raise(rb_eArgError, "%s: masked input not supported (use ca.value or ca.strip_mask(fill))", ca_calling_method_name());]
|
|
4726
4722
|
io.puts " }"
|
|
4727
4723
|
end
|
|
4728
|
-
io.puts " int axis = (
|
|
4729
|
-
io.puts " if ( axis < 0 ) axis += src->ndim;"
|
|
4730
|
-
io.puts " if ( axis < 0 || axis >= src->ndim ) {"
|
|
4731
|
-
io.puts %Q[ rb_raise(rb_eArgError, "%s: axis %d out of range for ndim %d", ca_calling_method_name(), NUM2INT(vaxis), src->ndim);]
|
|
4732
|
-
io.puts " }"
|
|
4724
|
+
io.puts " int axis = ca_normalize_axis_arg(vaxis, src->ndim, NULL);"
|
|
4733
4725
|
io.puts " switch ( src->data_type ) {"
|
|
4734
4726
|
k[:source].each do |s|
|
|
4735
4727
|
si = DTYPES[s]
|
|
@@ -4825,8 +4817,7 @@ module MkKernel
|
|
|
4825
4817
|
io.puts " CArray *src;"
|
|
4826
4818
|
io.puts " GetCArray(self, src);"
|
|
4827
4819
|
emit_lazy_evaluated(io)
|
|
4828
|
-
io.puts " /* Face gate (partition): same as the sort dispatcher
|
|
4829
|
-
io.puts " fixlen -> memcmp default, numeric -> ORDERABLE required. */"
|
|
4820
|
+
io.puts " /* Face gate (partition): same as the sort dispatcher. */"
|
|
4830
4821
|
emit_sort_face_gate(io, name)
|
|
4831
4822
|
if k[:mask_self] == :raise
|
|
4832
4823
|
io.puts " /* sort family rejects masked sources globally (R3 / Q3): "
|
|
@@ -4835,11 +4826,7 @@ module MkKernel
|
|
|
4835
4826
|
io.puts %Q[ rb_raise(rb_eArgError, "%s: masked input not supported (use ca.value or ca.strip_mask(fill))", ca_calling_method_name());]
|
|
4836
4827
|
io.puts " }"
|
|
4837
4828
|
end
|
|
4838
|
-
io.puts " int axis = (
|
|
4839
|
-
io.puts " if ( axis < 0 ) axis += src->ndim;"
|
|
4840
|
-
io.puts " if ( axis < 0 || axis >= src->ndim ) {"
|
|
4841
|
-
io.puts %Q[ rb_raise(rb_eArgError, "%s: axis %d out of range for ndim %d", ca_calling_method_name(), NUM2INT(vaxis), src->ndim);]
|
|
4842
|
-
io.puts " }"
|
|
4829
|
+
io.puts " int axis = ca_normalize_axis_arg(vaxis, src->ndim, NULL);"
|
|
4843
4830
|
io.puts " ca_size_t kth_raw = (ca_size_t) ca_integer_arg(vkth, \"kth\", NULL);"
|
|
4844
4831
|
io.puts " ca_size_t kth = kth_raw;"
|
|
4845
4832
|
io.puts " ca_size_t axis_n = src->dim[axis];"
|
|
@@ -5175,56 +5162,41 @@ module MkKernel
|
|
|
5175
5162
|
# flat array path. op_mask is lazily allocated on the first no-match.
|
|
5176
5163
|
undef_nm = k[:undef_no_match]
|
|
5177
5164
|
nm_cond = "result == (#{oi[:c]})(#{no_match})"
|
|
5178
|
-
#
|
|
5179
|
-
#
|
|
5180
|
-
#
|
|
5181
|
-
|
|
5182
|
-
# op_mask declaration (only when an output cell can be masked);
|
|
5183
|
-
# content-only (the heredoc carries indentation).
|
|
5184
|
-
op_mask_decl = (undef_nm || undef_q) \
|
|
5185
|
-
? "boolean8_t *op_mask = NULL; /* lazily allocated on the first masked output cell */" \
|
|
5186
|
-
: "/* (no masked output cell) */"
|
|
5165
|
+
# A masked query cell yields a masked output cell, so an undetermined
|
|
5166
|
+
# query cannot be answered from the value that happens to sit under the
|
|
5167
|
+
# mask. Shares op_mask with undef_no_match.
|
|
5168
|
+
op_mask_decl = "boolean8_t *op_mask = NULL; /* lazily allocated on the first masked output cell */"
|
|
5187
5169
|
|
|
5188
5170
|
# Case A scalar UNDEF query (also reached by the A1 single-element
|
|
5189
5171
|
# reduction when that element is masked): every output cell is
|
|
5190
5172
|
# undetermined, so short-circuit before the query is coerced to a number.
|
|
5191
|
-
undef_query_a =
|
|
5192
|
-
|
|
5193
|
-
|
|
5194
|
-
|
|
5195
|
-
|
|
5196
|
-
|
|
5197
|
-
|
|
5198
|
-
|
|
5199
|
-
|
|
5200
|
-
|
|
5201
|
-
|
|
5202
|
-
|
|
5203
|
-
|
|
5204
|
-
C
|
|
5205
|
-
else
|
|
5206
|
-
"/* (mask_query: :ignore) */"
|
|
5207
|
-
end
|
|
5173
|
+
undef_query_a = <<~C.chomp
|
|
5174
|
+
if ( rval == CA_UNDEF ) {
|
|
5175
|
+
int8_t undef_axes[1] = { (int8_t) axis };
|
|
5176
|
+
VALUE voutu = rb_ca_new_reduced(self, undef_axes, 1, #{oi[:ca]}, 0);
|
|
5177
|
+
CArray *cou;
|
|
5178
|
+
GetCArray(voutu, cou);
|
|
5179
|
+
MEMZERO((#{oi[:c]} *) cou->ptr, #{oi[:c]}, cou->elements);
|
|
5180
|
+
ca_create_mask(cou);
|
|
5181
|
+
memset(cou->mask->ptr, 1, (size_t) cou->elements);
|
|
5182
|
+
if ( ca->ndim == 1 ) { return Qnil; }
|
|
5183
|
+
return voutu;
|
|
5184
|
+
}
|
|
5185
|
+
C
|
|
5208
5186
|
|
|
5209
5187
|
# Case B/C query mask base. NULL when the query carries no mask.
|
|
5210
|
-
cv_mask_decl_bc =
|
|
5211
|
-
? "boolean8_t *cv_mask_base = ca_has_mask(cv) ? (boolean8_t *) cv->mask->ptr : NULL;" \
|
|
5212
|
-
: "/* (mask_query: :ignore) */"
|
|
5188
|
+
cv_mask_decl_bc = "boolean8_t *cv_mask_base = ca_has_mask(cv) ? (boolean8_t *) cv->mask->ptr : NULL;"
|
|
5213
5189
|
|
|
5214
5190
|
# Per-cell guard wrapping the body + write in case B/C. A masked query
|
|
5215
5191
|
# cell skips the body entirely and marks the output cell instead.
|
|
5216
|
-
undef_query_open_bc =
|
|
5217
|
-
|
|
5218
|
-
|
|
5219
|
-
|
|
5220
|
-
|
|
5221
|
-
|
|
5222
|
-
|
|
5223
|
-
|
|
5224
|
-
else
|
|
5225
|
-
"/* (mask_query: :ignore) */"
|
|
5226
|
-
end
|
|
5227
|
-
undef_query_close_bc = undef_q ? "}" : "/* (mask_query: :ignore) */"
|
|
5192
|
+
undef_query_open_bc = <<~C.chomp
|
|
5193
|
+
if ( cv_mask_base && cv_mask_base[val_offset / cv->bytes] ) {
|
|
5194
|
+
if ( ! op_mask ) { ca_create_mask(co); op_mask = (boolean8_t *) co->mask->ptr; }
|
|
5195
|
+
op_mask[flat] = 1;
|
|
5196
|
+
op[flat] = (#{oi[:c]}) 0; /* undetermined query; the mask bit is what counts */
|
|
5197
|
+
} else {
|
|
5198
|
+
C
|
|
5199
|
+
undef_query_close_bc = "}"
|
|
5228
5200
|
|
|
5229
5201
|
# Per-cell write block for case A (scalar query). `out_i` is the output
|
|
5230
5202
|
# cursor, `co` the output CArray. When undef_no_match: a no-match masks
|
|
@@ -5642,21 +5614,15 @@ module MkKernel
|
|
|
5642
5614
|
end
|
|
5643
5615
|
io.puts "{"
|
|
5644
5616
|
io.puts " CArray *src;"
|
|
5645
|
-
io.puts " VALUE self_ref = self; /* pre-strip Face, for to_comparable */"
|
|
5646
5617
|
io.puts " int self_was_face = 0;"
|
|
5647
5618
|
io.puts " GetCArray(self, src);"
|
|
5648
5619
|
emit_lazy_evaluated(io)
|
|
5649
|
-
io.puts " /* Face gate (self):
|
|
5650
|
-
io.puts "
|
|
5651
|
-
io.puts "
|
|
5652
|
-
io.puts " query into the same space. */"
|
|
5620
|
+
io.puts " /* Face gate (self): a Face reference is searched on its numeric"
|
|
5621
|
+
io.puts " storage, which ORDERABLE licenses; the query gate below brings"
|
|
5622
|
+
io.puts " the reference and the query into one space and descends. */"
|
|
5653
5623
|
io.puts " if ( ca_is_face(src) ) {"
|
|
5654
|
-
io.puts
|
|
5655
|
-
io.puts %Q[ ca_face_refuse_order(ca_calling_method_name(), self, "order");]
|
|
5656
|
-
io.puts " }"
|
|
5624
|
+
io.puts %Q[ ca_face_require_orderable(self, ca_calling_method_name(), "order");]
|
|
5657
5625
|
io.puts " self_was_face = 1;"
|
|
5658
|
-
io.puts " self = rb_ca_strip_face_value(self);"
|
|
5659
|
-
io.puts " GetCArray(self, src);"
|
|
5660
5626
|
io.puts " }"
|
|
5661
5627
|
|
|
5662
5628
|
if has_eps
|
|
@@ -5668,32 +5634,23 @@ module MkKernel
|
|
|
5668
5634
|
io.puts " VALUE raxis = argv[1];"
|
|
5669
5635
|
io.puts " VALUE reps = (argc >= 3) ? argv[2] : Qnil;"
|
|
5670
5636
|
end
|
|
5671
|
-
io.puts " /* Query gate: the reference Face
|
|
5672
|
-
io.puts " space (
|
|
5637
|
+
io.puts " /* Query gate: the reference Face and the query are brought into one"
|
|
5638
|
+
io.puts " space (ca_face_reconcile_pair), then the reference descends to"
|
|
5639
|
+
io.puts " its storage. */"
|
|
5673
5640
|
io.puts " if ( self_was_face ) {"
|
|
5674
|
-
io.puts "
|
|
5641
|
+
io.puts " volatile VALUE ref = self, query = rval;"
|
|
5642
|
+
io.puts " ca_face_reconcile_pair(&ref, &query, ca_calling_method_name());"
|
|
5643
|
+
io.puts " rval = query;"
|
|
5644
|
+
io.puts " self = rb_ca_strip_face_value(ref);"
|
|
5645
|
+
io.puts " GetCArray(self, src);"
|
|
5675
5646
|
io.puts " }"
|
|
5676
5647
|
|
|
5677
|
-
# The query and the reference are compared in the type they
|
|
5678
|
-
# `a.eq(q)` compares
|
|
5679
|
-
#
|
|
5680
|
-
# reference
|
|
5681
|
-
|
|
5682
|
-
|
|
5683
|
-
# a cast view of that type. An Integer scalar keeps the reference's type
|
|
5684
|
-
# and is refused when it does not fit.
|
|
5685
|
-
io.puts " if ( ca_is_integer_type(src) || ca_is_float_type(src) || src->data_type == CA_BOOLEAN ) {"
|
|
5686
|
-
io.puts " int8_t qt = src->data_type;"
|
|
5687
|
-
io.puts " if ( RB_FLOAT_TYPE_P(rval) ) {"
|
|
5688
|
-
io.puts " qt = ca_promote_scalar_type(src->data_type, rval, CA_FLOAT64);"
|
|
5689
|
-
io.puts " } else if ( rb_obj_is_kind_of(rval, rb_cCArray) ) {"
|
|
5690
|
-
io.puts " CArray *qv;"
|
|
5691
|
-
io.puts " TypedData_Get_Struct(rval, CArray, &carray_data_type, qv);"
|
|
5692
|
-
io.puts " if ( ca_is_integer_type(qv) || ca_is_float_type(qv) || qv->data_type == CA_BOOLEAN ) {"
|
|
5693
|
-
io.puts " qt = ca_value_match_type(ca_promote_type(src->data_type, qv->data_type),"
|
|
5694
|
-
io.puts " src->data_type, qv->data_type);"
|
|
5695
|
-
io.puts " }"
|
|
5696
|
-
io.puts " }"
|
|
5648
|
+
# The query and the reference are compared in the type they match in,
|
|
5649
|
+
# as `a.eq(q)` compares (ca_query_match_type): the reference is read
|
|
5650
|
+
# through a cast view of that type, so the query is never cut to the
|
|
5651
|
+
# reference's type.
|
|
5652
|
+
io.puts " if ( ca_query_match_applies(src, rval) ) {"
|
|
5653
|
+
io.puts " int8_t qt = ca_query_match_type(src->data_type, rval);"
|
|
5697
5654
|
io.puts " if ( qt != src->data_type ) {"
|
|
5698
5655
|
io.puts " self = rb_ca_wrap_readonly(self, INT2NUM(qt));"
|
|
5699
5656
|
io.puts " GetCArray(self, src);"
|
|
@@ -5710,11 +5667,7 @@ module MkKernel
|
|
|
5710
5667
|
# body handles mask_in or ignores it
|
|
5711
5668
|
end
|
|
5712
5669
|
|
|
5713
|
-
io.puts " int axis = (
|
|
5714
|
-
io.puts " if ( axis < 0 ) axis += src->ndim;"
|
|
5715
|
-
io.puts " if ( axis < 0 || axis >= src->ndim ) {"
|
|
5716
|
-
io.puts %Q[ rb_raise(rb_eArgError, "%s: axis %d out of range for ndim %d", ca_calling_method_name(), NUM2INT(raxis), src->ndim);]
|
|
5717
|
-
io.puts " }"
|
|
5670
|
+
io.puts " int axis = ca_normalize_axis_arg(raxis, src->ndim, NULL);"
|
|
5718
5671
|
io.puts " switch ( src->data_type ) {"
|
|
5719
5672
|
k[:source].each do |s|
|
|
5720
5673
|
si = DTYPES[s]
|
|
@@ -6539,7 +6492,8 @@ module MkKernel
|
|
|
6539
6492
|
# the source's data type. Left out: a boolean target, which checks its
|
|
6540
6493
|
# input and raises, and a float or complex source to an integer target,
|
|
6541
6494
|
# where an out-of-range value or NaN has no defined C result for a
|
|
6542
|
-
# second compilation to agree on.
|
|
6495
|
+
# second compilation to agree on. An int64 or uint64 source to float32
|
|
6496
|
+
# or cmplx64 goes through double, as ca_cast_block does.
|
|
6543
6497
|
CAST_SOURCES = %i[bool i8 u8 i16 u16 i32 u32 i64 u64 f32 f64
|
|
6544
6498
|
cmplx64 cmplx128].freeze
|
|
6545
6499
|
CAST_TARGETS = %i[i8 u8 i16 u16 i32 u32 i64 u64 f32 f64
|
|
@@ -6550,8 +6504,9 @@ module MkKernel
|
|
|
6550
6504
|
CAST_SOURCES.product(CAST_TARGETS).filter_map { |src, dst|
|
|
6551
6505
|
next if CAST_INEXACT.include?(src) && !CAST_INEXACT.include?(dst)
|
|
6552
6506
|
name = DTYPES[dst][:ca].sub(/\ACA_/, "").downcase
|
|
6507
|
+
via = ( %i[i64 u64].include?(src) && %i[f32 cmplx64].include?(dst) ) ? "(double)" : ""
|
|
6553
6508
|
["monop", "cast_#{name}", DTYPES[src][:ca].sub(/\ACA_/, "").downcase,
|
|
6554
|
-
"(#2) = (#{DTYPES[dst][:c]})(#1);"]
|
|
6509
|
+
"(#2) = (#{DTYPES[dst][:c]})#{via}(#1);"]
|
|
6555
6510
|
}
|
|
6556
6511
|
end
|
|
6557
6512
|
|
|
@@ -6756,12 +6711,16 @@ module MkKernel
|
|
|
6756
6711
|
# reduce: default false (E.8 retire), opt-in via bind_ruby: true
|
|
6757
6712
|
# (used by CF.1 count_equal smoke + future kernel testing)
|
|
6758
6713
|
# sort: default true, opt-out via bind_ruby: false
|
|
6759
|
-
# scan:
|
|
6714
|
+
# scan: bound as <name>_ki, or only privately as <name> (private:)
|
|
6760
6715
|
bind = case k[:kind]
|
|
6761
6716
|
when :reduce then k[:bind_ruby] == true
|
|
6762
6717
|
when :sort then k[:bind_ruby] != false
|
|
6718
|
+
when :scan then ! k[:private]
|
|
6763
6719
|
else true
|
|
6764
6720
|
end
|
|
6721
|
+
if k[:kind] == :scan && k[:private]
|
|
6722
|
+
io.puts %Q[ rb_define_private_method(rb_cCArray, "#{k[:name]}", rb_ca_#{k[:name]}_ki, #{arity});]
|
|
6723
|
+
end
|
|
6765
6724
|
# Smoke kernels (e.g. count_equal) expose their _ki binding only
|
|
6766
6725
|
# in dev builds, gated by CARRAY_DEV_BUILD. See
|
|
6767
6726
|
# devel/PROPOSAL_SMOKE_DEV_BUILD_GATE.md.
|
|
@@ -6774,7 +6733,7 @@ module MkKernel
|
|
|
6774
6733
|
# directly without a Ruby wrapper. This is paired with
|
|
6775
6734
|
# axis_default: :flatten because both express the legacy-compat
|
|
6776
6735
|
# convenience of these specific scan kernels.
|
|
6777
|
-
if k[:kind] == :scan && k[:axis_default] == :flatten
|
|
6736
|
+
if k[:kind] == :scan && k[:axis_default] == :flatten && ! k[:private]
|
|
6778
6737
|
io.puts %Q[ rb_define_method(rb_cCArray, "#{k[:name]}", rb_ca_#{k[:name]}_ki, #{arity});]
|
|
6779
6738
|
end
|
|
6780
6739
|
# Phase E rewire: when this kernel replaces a legacy CArray method,
|
|
@@ -7398,7 +7357,7 @@ MkKernel.reduce :wsum,
|
|
|
7398
7357
|
shape: :rev5_strict, mask: :overlay },
|
|
7399
7358
|
mask_policy: :min_count,
|
|
7400
7359
|
identity_on_empty: true, # ERI.2: empty / all-masked -> 0.0 (weighted sum identity)
|
|
7401
|
-
object_escape: :
|
|
7360
|
+
object_escape: :wsum_object, # CA_OBJECT -> (w*self).sum (runtime.rb)
|
|
7402
7361
|
bind_ruby: true,
|
|
7403
7362
|
public_method: true
|
|
7404
7363
|
|
|
@@ -7415,7 +7374,7 @@ MkKernel.reduce :wsum,
|
|
|
7415
7374
|
# - min_count semantic "max masked" -> "min valid required" (Phase E)
|
|
7416
7375
|
# - complex (CMPLX64/128) dropped (= ALL_NUMERIC + :raise)
|
|
7417
7376
|
# - per-axis support gained
|
|
7418
|
-
# CA_OBJECT is handled via object_escape (runtime.rb
|
|
7377
|
+
# CA_OBJECT is handled via object_escape (runtime.rb wmean_object), not
|
|
7419
7378
|
# the numeric kernel: weight-coerce vs object arithmetic is cleaner as a
|
|
7420
7379
|
# (w*self).sum / Σw composition than a dedicated :object kernel branch.
|
|
7421
7380
|
|
|
@@ -7432,7 +7391,7 @@ MkKernel.reduce :wmean,
|
|
|
7432
7391
|
array_arg: { name: :weights, data_type: :promote,
|
|
7433
7392
|
shape: :rev5_strict, mask: :overlay },
|
|
7434
7393
|
mask_policy: :min_count,
|
|
7435
|
-
object_escape: :
|
|
7394
|
+
object_escape: :wmean_object, # CA_OBJECT -> weighted mean via runtime.rb
|
|
7436
7395
|
bind_ruby: true,
|
|
7437
7396
|
public_method: true
|
|
7438
7397
|
|
|
@@ -7457,6 +7416,18 @@ MkKernel.scan :cumsum,
|
|
|
7457
7416
|
fallback: :raise,
|
|
7458
7417
|
axis_default: :flatten
|
|
7459
7418
|
|
|
7419
|
+
# The running sum in the source's own integer type, added without a range
|
|
7420
|
+
# check (int64 wraps, as accumulate does). CATimedelta#cumsum reads it
|
|
7421
|
+
# for an exact running sum of durations.
|
|
7422
|
+
MkKernel.scan :cumsum_in_own_type,
|
|
7423
|
+
source: MkKernel::INT_DTYPES,
|
|
7424
|
+
output: :preserve,
|
|
7425
|
+
init: "0",
|
|
7426
|
+
step: "acc += v; r = acc",
|
|
7427
|
+
fallback: :raise,
|
|
7428
|
+
axis_default: :flatten,
|
|
7429
|
+
private: true
|
|
7430
|
+
|
|
7460
7431
|
MkKernel.scan :cumprod,
|
|
7461
7432
|
source: MkKernel::ALL_NUMERIC + MkKernel::CMPLX_DTYPES + [:bool, :object],
|
|
7462
7433
|
output: { numeric: :f64, complex: :cmplx128, bool: :u64, object: :object },
|
|
@@ -7732,7 +7703,6 @@ MkKernel.search :bsearch,
|
|
|
7732
7703
|
while ( lo < hi ) {
|
|
7733
7704
|
ca_size_t mid = lo + (hi - lo) / 2;
|
|
7734
7705
|
T_LOAD v = *(T_LOAD *)(slab_ptr + mid * slab_stride);
|
|
7735
|
-
if ( isnan((double) v) ) { hi = mid; continue; }
|
|
7736
7706
|
if ( v < query_val ) lo = mid + 1;
|
|
7737
7707
|
else if ( v > query_val ) hi = mid;
|
|
7738
7708
|
else { result = mid; break; }
|
|
@@ -7741,35 +7711,30 @@ MkKernel.search :bsearch,
|
|
|
7741
7711
|
C
|
|
7742
7712
|
float: <<~C,
|
|
7743
7713
|
result = (ca_size_t) -1;
|
|
7744
|
-
if ( ! isnan((double) query_val) ) {
|
|
7714
|
+
if ( ! isnan((double) query_val) ) { /* a NaN query matches nothing */
|
|
7745
7715
|
ca_size_t lo = 0;
|
|
7746
7716
|
ca_size_t hi = slab_n;
|
|
7747
7717
|
while ( lo < hi ) {
|
|
7748
7718
|
ca_size_t mid = lo + (hi - lo) / 2;
|
|
7749
7719
|
T_LOAD v = *(T_LOAD *)(slab_ptr + mid * slab_stride);
|
|
7750
|
-
|
|
7751
|
-
if (
|
|
7752
|
-
else if (
|
|
7720
|
+
int c = CA_FLOAT_ORDER_CMP(v, query_val);
|
|
7721
|
+
if ( c < 0 ) lo = mid + 1;
|
|
7722
|
+
else if ( c > 0 ) hi = mid;
|
|
7753
7723
|
else { result = mid; break; }
|
|
7754
7724
|
}
|
|
7755
7725
|
}
|
|
7756
7726
|
C
|
|
7757
7727
|
object: <<~C,
|
|
7758
|
-
/* CA_OBJECT bsearch:
|
|
7759
|
-
slab is sorted by
|
|
7760
|
-
|
|
7728
|
+
/* CA_OBJECT bsearch: the object order (ca_obj_order_cmp), which the
|
|
7729
|
+
slab is assumed to be sorted by; a NaN query matches nothing, as in
|
|
7730
|
+
float. */
|
|
7761
7731
|
result = (ca_size_t) -1;
|
|
7762
7732
|
ca_size_t lo = 0;
|
|
7763
7733
|
ca_size_t hi = CA_OBJ_ISNAN(query_val) ? 0 : slab_n; /* NaN matches nothing */
|
|
7764
7734
|
while ( lo < hi ) {
|
|
7765
7735
|
ca_size_t mid = lo + (hi - lo) / 2;
|
|
7766
7736
|
T_LOAD v = *(T_LOAD *)(slab_ptr + mid * slab_stride);
|
|
7767
|
-
|
|
7768
|
-
VALUE r = rb_funcall(v, rb_intern("<=>"), 1, query_val);
|
|
7769
|
-
if ( NIL_P(r) ) {
|
|
7770
|
-
ca_obj_compare_refused(v, query_val);
|
|
7771
|
-
}
|
|
7772
|
-
int c = NUM2INT(r);
|
|
7737
|
+
int c = ca_obj_order_cmp(v, query_val);
|
|
7773
7738
|
if ( c < 0 ) lo = mid + 1;
|
|
7774
7739
|
else if ( c > 0 ) hi = mid;
|
|
7775
7740
|
else { result = mid; break; }
|
|
@@ -7971,7 +7936,6 @@ MkKernel.search :bsearch_addr,
|
|
|
7971
7936
|
while ( lo < hi ) {
|
|
7972
7937
|
ca_size_t mid = lo + (hi - lo) / 2;
|
|
7973
7938
|
T_LOAD v = *(T_LOAD *)(slab_ptr + mid * slab_stride);
|
|
7974
|
-
if ( isnan((double) v) ) { hi = mid; continue; }
|
|
7975
7939
|
if ( v < query_val ) lo = mid + 1;
|
|
7976
7940
|
else if ( v > query_val ) hi = mid;
|
|
7977
7941
|
else { result = mid; break; }
|
|
@@ -7980,15 +7944,15 @@ MkKernel.search :bsearch_addr,
|
|
|
7980
7944
|
C
|
|
7981
7945
|
float: <<~C,
|
|
7982
7946
|
result = (ca_size_t) -1;
|
|
7983
|
-
if ( ! isnan((double) query_val) ) {
|
|
7947
|
+
if ( ! isnan((double) query_val) ) { /* a NaN query matches nothing */
|
|
7984
7948
|
ca_size_t lo = 0;
|
|
7985
7949
|
ca_size_t hi = slab_n;
|
|
7986
7950
|
while ( lo < hi ) {
|
|
7987
7951
|
ca_size_t mid = lo + (hi - lo) / 2;
|
|
7988
7952
|
T_LOAD v = *(T_LOAD *)(slab_ptr + mid * slab_stride);
|
|
7989
|
-
|
|
7990
|
-
if (
|
|
7991
|
-
else if (
|
|
7953
|
+
int c = CA_FLOAT_ORDER_CMP(v, query_val);
|
|
7954
|
+
if ( c < 0 ) lo = mid + 1;
|
|
7955
|
+
else if ( c > 0 ) hi = mid;
|
|
7992
7956
|
else { result = mid; break; }
|
|
7993
7957
|
}
|
|
7994
7958
|
}
|
|
@@ -8000,12 +7964,7 @@ MkKernel.search :bsearch_addr,
|
|
|
8000
7964
|
while ( lo < hi ) {
|
|
8001
7965
|
ca_size_t mid = lo + (hi - lo) / 2;
|
|
8002
7966
|
T_LOAD v = *(T_LOAD *)(slab_ptr + mid * slab_stride);
|
|
8003
|
-
|
|
8004
|
-
VALUE r = rb_funcall(v, rb_intern("<=>"), 1, query_val);
|
|
8005
|
-
if ( NIL_P(r) ) {
|
|
8006
|
-
ca_obj_compare_refused(v, query_val);
|
|
8007
|
-
}
|
|
8008
|
-
int c = NUM2INT(r);
|
|
7967
|
+
int c = ca_obj_order_cmp(v, query_val);
|
|
8009
7968
|
if ( c < 0 ) lo = mid + 1;
|
|
8010
7969
|
else if ( c > 0 ) hi = mid;
|
|
8011
7970
|
else { result = mid; break; }
|
|
@@ -8188,7 +8147,6 @@ MkKernel.search :linear_section_binary,
|
|
|
8188
8147
|
no_match: "NAN",
|
|
8189
8148
|
no_match_check: "isnan(op[0])",
|
|
8190
8149
|
mask_self: :raise,
|
|
8191
|
-
mask_query: :undef,
|
|
8192
8150
|
body: <<~C,
|
|
8193
8151
|
result = NAN;
|
|
8194
8152
|
if ( slab_n <= 1 ) {
|
|
@@ -8280,7 +8238,6 @@ MkKernel.search :linear_section_linear,
|
|
|
8280
8238
|
no_match: "NAN",
|
|
8281
8239
|
no_match_check: "isnan(op[0])",
|
|
8282
8240
|
mask_self: :raise,
|
|
8283
|
-
mask_query: :undef,
|
|
8284
8241
|
body: <<~C,
|
|
8285
8242
|
result = NAN;
|
|
8286
8243
|
if ( slab_n <= 1 ) {
|
|
@@ -8323,7 +8280,6 @@ MkKernel.search :linear_fetch,
|
|
|
8323
8280
|
no_match: "NAN",
|
|
8324
8281
|
no_match_check: "isnan(op[0])",
|
|
8325
8282
|
mask_self: :raise,
|
|
8326
|
-
mask_query: :undef,
|
|
8327
8283
|
body: <<~C,
|
|
8328
8284
|
result = NAN;
|
|
8329
8285
|
if ( slab_n >= 1 && query_val >= 0.0 && query_val <= (double)(slab_n - 1) ) {
|
|
@@ -8588,7 +8544,9 @@ MkKernel.monfunc :round,
|
|
|
8588
8544
|
source: MkKernel::ALL_NUMERIC + [:object],
|
|
8589
8545
|
expr: {
|
|
8590
8546
|
int: "(#2) = (#1);",
|
|
8591
|
-
|
|
8547
|
+
# Half away from zero, as Float#round. floor(x + 0.5) would round in
|
|
8548
|
+
# the addition: 0.49999999999999994 + 0.5 is 1.0.
|
|
8549
|
+
float: "(#2) = round(#1);",
|
|
8592
8550
|
object: '(#2) = rb_funcall((#1), rb_intern("round"), 0);',
|
|
8593
8551
|
}
|
|
8594
8552
|
|
|
@@ -9259,13 +9217,13 @@ MkKernel.triop :fms,
|
|
|
9259
9217
|
# UNDEF-fill case can now be expressed as `out = a.clip(lo, hi);
|
|
9260
9218
|
# out[a.lt(lo) | a.gt(hi)] = UNDEF`). Pinned in test.
|
|
9261
9219
|
#
|
|
9262
|
-
# Bound at the C level as `
|
|
9220
|
+
# Bound at the C level as `clamp_between` (made private by lazy.rb). The
|
|
9263
9221
|
# public `CArray#clip` is a Ruby wrapper in lib/carray/math.rb that
|
|
9264
9222
|
# restores the rich pre-3.0 API surface (nil-bound one-sided clip +
|
|
9265
9223
|
# optional fill). `clip!` is retired in 3.0 (view-by-default
|
|
9266
9224
|
# convention) so the bang form is suppressed via `bang: false`.
|
|
9267
9225
|
MkKernel.triop :clip,
|
|
9268
|
-
op: "
|
|
9226
|
+
op: "clamp_between",
|
|
9269
9227
|
bang: false,
|
|
9270
9228
|
source: MkKernel::ALL_NUMERIC + [:object],
|
|
9271
9229
|
expr: {
|