carray 2.0.0 → 3.0.0
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/.yardopts +5 -25
- data/CHANGELOG.md +16 -0
- data/LICENSE +1 -1
- data/NEWS.md +3 -0
- data/README.md +128 -44
- data/carray.gemspec +22 -24
- data/ext/ca_array_pool.c +91 -0
- data/ext/ca_axis_descriptor.h +186 -0
- data/ext/ca_axis_dispatch.c +924 -0
- data/ext/ca_axis_group.c +1208 -0
- data/ext/ca_bincmp_dispatch.c +76 -0
- data/ext/ca_bincmp_dispatch.h +85 -0
- data/ext/ca_binop_dispatch.c +125 -0
- data/ext/ca_binop_dispatch.h +159 -0
- data/ext/ca_categorical_iterator.c +1375 -0
- data/ext/ca_compare.c +94 -0
- data/ext/ca_compare.h +26 -0
- data/ext/ca_composite_dispatch.c +414 -0
- data/ext/ca_composite_dispatch.h +116 -0
- data/ext/ca_for_buffer.h +96 -0
- data/ext/ca_for_each_element.h +241 -0
- data/ext/ca_group_iter.c +304 -0
- data/ext/ca_iter_substrate.h +325 -0
- data/ext/ca_kernel_iterator.c +4321 -0
- data/ext/ca_kernel_iterator.h +2603 -0
- data/ext/ca_moncmp_dispatch.c +37 -0
- data/ext/ca_moncmp_dispatch.h +62 -0
- data/ext/ca_monop_dispatch.c +200 -0
- data/ext/ca_monop_dispatch.h +235 -0
- data/ext/ca_obj_array.c +355 -359
- data/ext/ca_obj_bincmp.c +809 -0
- data/ext/ca_obj_binop.c +892 -0
- data/ext/ca_obj_bitarray.c +369 -164
- data/ext/ca_obj_bitfield.c +294 -234
- data/ext/ca_obj_block.c +189 -711
- data/ext/ca_obj_byte_swap.c +766 -0
- data/ext/ca_obj_const_string.c +965 -0
- data/ext/ca_obj_face.c +670 -0
- data/ext/ca_obj_face.h +247 -0
- data/ext/ca_obj_fake.c +228 -100
- data/ext/ca_obj_farray.c +54 -441
- data/ext/ca_obj_field.c +82 -529
- data/ext/ca_obj_fixlen_string.c +306 -0
- data/ext/ca_obj_grid.c +858 -440
- data/ext/ca_obj_meld.c +1034 -0
- data/ext/ca_obj_moncmp.c +569 -0
- data/ext/ca_obj_monop.c +1111 -0
- data/ext/ca_obj_object.c +774 -298
- data/ext/ca_obj_record.c +468 -0
- data/ext/ca_obj_reduce.c +97 -82
- data/ext/ca_obj_refer.c +569 -459
- data/ext/ca_obj_remap.c +475 -0
- data/ext/ca_obj_repeat.c +92 -477
- data/ext/ca_obj_roll.c +616 -0
- data/ext/ca_obj_select.c +344 -296
- data/ext/ca_obj_select_axis.c +1296 -0
- data/ext/ca_obj_shift.c +230 -792
- data/ext/ca_obj_source.c +78 -0
- data/ext/ca_obj_stack.c +1173 -0
- data/ext/ca_obj_stride.c +2501 -0
- data/ext/ca_obj_string.c +268 -0
- data/ext/ca_obj_tile.c +614 -0
- data/ext/ca_obj_time.c +546 -0
- data/ext/ca_obj_timedelta.c +435 -0
- data/ext/ca_obj_transpose.c +62 -516
- data/ext/ca_obj_triop.c +746 -0
- data/ext/ca_obj_unbound_repeat.c +208 -241
- data/ext/ca_obj_window.c +1131 -563
- data/ext/ca_op_byte_swap.c +175 -0
- data/ext/ca_op_ipower.c +319 -0
- data/ext/ca_op_powi.h +88 -0
- data/ext/ca_sort_kernels.h +132 -0
- data/ext/ca_sweep_engine.c +430 -0
- data/ext/ca_sweep_engine.h +157 -0
- data/ext/ca_transform_common.c +228 -0
- data/ext/ca_triop_dispatch.c +55 -0
- data/ext/ca_triop_dispatch.h +62 -0
- data/ext/carray.h +795 -402
- data/ext/carray_access.c +831 -711
- data/ext/carray_attribute.c +98 -330
- data/ext/carray_bincount.c +255 -0
- data/ext/carray_broadcast.c +283 -0
- data/ext/carray_call_cfunc.c +1360 -828
- data/ext/carray_call_cfunc.h +160 -0
- data/ext/carray_cast.c +1212 -301
- data/ext/carray_cast_func.rb +81 -40
- data/ext/carray_class.c +53 -63
- data/ext/carray_config.h +28 -0
- data/ext/carray_conversion.c +350 -346
- data/ext/carray_copy.c +156 -268
- data/ext/carray_core.c +1342 -199
- data/ext/carray_count.c +312 -0
- data/ext/carray_data_type.c +43 -19
- data/ext/carray_element.c +585 -213
- data/ext/carray_factorize.c +2542 -0
- data/ext/carray_generate.c +230 -559
- data/ext/carray_histogram.c +490 -0
- data/ext/carray_hold.c +228 -0
- data/ext/carray_index_classifier.c +1035 -0
- data/ext/carray_index_classifier.h +27 -0
- data/ext/carray_internal.h +120 -0
- data/ext/carray_kernels_bincmp.c +4445 -0
- data/ext/carray_kernels_binop.c +10979 -0
- data/ext/carray_kernels_init.c +36 -0
- data/ext/carray_kernels_map.c +3466 -0
- data/ext/carray_kernels_moncmp.c +2096 -0
- data/ext/carray_kernels_monop.c +18312 -0
- data/ext/carray_kernels_reduce_aggregate.c +25836 -0
- data/ext/carray_kernels_reduce_boolean.c +329 -0
- data/ext/carray_kernels_reduce_cumulative.c +14592 -0
- data/ext/carray_kernels_reduce_extreme.c +16947 -0
- data/ext/carray_kernels_reduce_variance.c +3909 -0
- data/ext/carray_kernels_scan.c +3692 -0
- data/ext/carray_kernels_search.c +32137 -0
- data/ext/carray_kernels_sort.c +10625 -0
- data/ext/carray_kernels_triop.c +1391 -0
- data/ext/carray_lazy.c +567 -0
- data/ext/carray_loop.c +88 -200
- data/ext/carray_mask.c +848 -154
- data/ext/carray_math_kernel.h +120 -0
- data/ext/carray_mathfunc.c +10 -241
- data/ext/carray_median_percentile.c +1257 -0
- data/ext/carray_memory_view.c +1625 -0
- data/ext/carray_operator.c +1526 -318
- data/ext/carray_order.c +664 -1394
- data/ext/carray_partition.c +416 -0
- data/ext/carray_random.c +518 -0
- data/ext/carray_scatter.c +357 -0
- data/ext/carray_slab.c +1219 -0
- data/ext/carray_slab.h +84 -0
- data/ext/carray_sort.c +829 -0
- data/ext/carray_sort_kernel.c +620 -0
- data/ext/carray_struct.c +695 -0
- data/ext/carray_test.c +343 -229
- data/ext/carray_undef.c +34 -17
- data/ext/carray_utils.c +175 -74
- data/ext/extconf.rb +216 -55
- data/ext/mk_call_cfunc.rb +480 -0
- data/ext/mkkernel.rb +8842 -0
- data/ext/ruby_carray.c +202 -101
- data/ext/version.h +4 -14
- data/ext/version.rb +5 -13
- data/lib/carray/arrow_tensor.rb +401 -0
- data/lib/carray/attribute.rb +166 -0
- data/lib/carray/autoload_carray.rb +220 -0
- data/lib/carray/autoload_method_extension.rb +44 -0
- data/lib/carray/axis_group.rb +711 -0
- data/lib/carray/basics.rb +481 -0
- data/lib/carray/bincount_nd.rb +358 -0
- data/lib/carray/block_iterator.rb +604 -0
- data/lib/carray/boolean_reduce.rb +109 -0
- data/lib/carray/categorical.rb +561 -0
- data/lib/carray/categorical_iterator.rb +1062 -0
- data/lib/carray/complex.rb +150 -0
- data/lib/carray/conditional.rb +216 -0
- data/lib/carray/const_string.rb +228 -0
- data/lib/carray/construct.rb +139 -328
- data/lib/carray/core_extensions.rb +240 -0
- data/lib/carray/data_type_extension.rb +233 -0
- data/lib/carray/fixlen_string.rb +95 -0
- data/lib/carray/frame/concat.rb +132 -0
- data/lib/carray/frame/convert.rb +95 -0
- data/lib/carray/frame/csv_parser.rb +211 -0
- data/lib/carray/frame/frame.rb +649 -0
- data/lib/carray/frame/group.rb +186 -0
- data/lib/carray/frame/io.rb +164 -0
- data/lib/carray/frame/join.rb +248 -0
- data/lib/carray/frame/records.rb +99 -0
- data/lib/carray/frame/sort.rb +113 -0
- data/lib/carray/frame/verbs.rb +299 -0
- data/lib/carray/frame.rb +16 -0
- data/lib/carray/histogram.rb +512 -0
- data/lib/carray/inspect.rb +37 -20
- data/lib/carray/iterator.rb +57 -349
- data/lib/carray/lazy.rb +889 -0
- data/lib/carray/mask_gap_fill.rb +200 -0
- data/lib/carray/math.rb +78 -342
- data/lib/carray/meld_reduce.rb +289 -0
- data/lib/carray/methods/align_addr.rb +116 -0
- data/lib/carray/methods/bin.rb +128 -0
- data/lib/carray/methods/bincount.rb +87 -0
- data/lib/carray/methods/bit_string.rb +92 -0
- data/lib/carray/methods/broadcast.rb +63 -0
- data/lib/carray/methods/choose.rb +39 -0
- data/lib/carray/methods/composition.rb +280 -0
- data/lib/carray/methods/gather_nd.rb +206 -0
- data/lib/carray/methods/index.rb +39 -0
- data/lib/carray/methods/insert_block.rb +99 -0
- data/lib/carray/methods/is_in.rb +141 -0
- data/lib/carray/methods/join.rb +90 -0
- data/lib/carray/methods/locate_addr.rb +47 -0
- data/lib/carray/methods/mask_duplicates.rb +41 -0
- data/lib/carray/methods/meshgrid.rb +91 -0
- data/lib/carray/methods/mode.rb +126 -0
- data/lib/carray/methods/nunique.rb +46 -0
- data/lib/carray/methods/resize.rb +56 -0
- data/lib/carray/methods/snap.rb +156 -0
- data/lib/carray/methods/string_format.rb +57 -0
- data/lib/carray/methods/unique.rb +47 -0
- data/lib/carray/methods/value_counts.rb +71 -0
- data/lib/carray/mkmf.rb +124 -101
- data/lib/carray/runtime.rb +108 -0
- data/lib/carray/serialize.rb +478 -167
- data/lib/carray/slab_iterator.rb +292 -0
- data/lib/carray/stack.rb +291 -0
- data/lib/carray/string.rb +56 -180
- data/lib/carray/string_operation_extension.rb +289 -0
- data/lib/carray/struct.rb +335 -323
- data/lib/carray/struct_builder.rb +697 -0
- data/lib/carray/table.rb +41 -2
- data/lib/carray/time.rb +2255 -38
- data/lib/carray/window_iterator.rb +655 -0
- data/lib/carray.rb +55 -57
- metadata +163 -130
- data/Rakefile +0 -51
- data/TODO.md +0 -18
- data/ext/ca_iter_block.c +0 -257
- data/ext/ca_iter_dimension.c +0 -299
- data/ext/ca_iter_window.c +0 -214
- data/ext/ca_obj_mapping.c +0 -644
- data/ext/carray_iterator.c +0 -641
- data/ext/carray_math.rb +0 -850
- data/ext/carray_numeric.c +0 -259
- data/ext/carray_sort_addr.c +0 -254
- data/ext/carray_stat.c +0 -2100
- data/ext/carray_stat_proc.rb +0 -1999
- data/ext/mkmath.rb +0 -741
- data/ext/ruby_ccomplex.c +0 -509
- data/ext/ruby_float_func.c +0 -86
- data/lib/carray/array.rb +0 -8
- data/lib/carray/autoload/autoload_base.rb +0 -19
- data/lib/carray/autoload/autoload_gem_cairo.rb +0 -9
- data/lib/carray/autoload/autoload_gem_ffi.rb +0 -9
- data/lib/carray/autoload/autoload_gem_gnuplot.rb +0 -2
- data/lib/carray/autoload/autoload_gem_io_csv.rb +0 -14
- data/lib/carray/autoload/autoload_gem_io_pg.rb +0 -6
- data/lib/carray/autoload/autoload_gem_io_sqlite3.rb +0 -12
- data/lib/carray/autoload/autoload_gem_narray.rb +0 -10
- data/lib/carray/autoload/autoload_gem_numo_narray.rb +0 -15
- data/lib/carray/autoload/autoload_gem_opencv.rb +0 -16
- data/lib/carray/autoload/autoload_gem_random.rb +0 -8
- data/lib/carray/autoload/autoload_gem_rmagick.rb +0 -23
- data/lib/carray/autoload/autoload_gem_zimg.rb +0 -3
- data/lib/carray/autoload/autoload_io_imagemagick.rb +0 -6
- data/lib/carray/autoload/autoload_math_histogram.rb +0 -5
- data/lib/carray/autoload/autoload_math_recurrence.rb +0 -6
- data/lib/carray/autoload/autoload_object_iterator.rb +0 -1
- data/lib/carray/autoload/autoload_object_link.rb +0 -1
- data/lib/carray/autoload/autoload_object_pack.rb +0 -2
- data/lib/carray/autoload.rb +0 -141
- data/lib/carray/basic.rb +0 -191
- data/lib/carray/broadcast.rb +0 -101
- data/lib/carray/compose.rb +0 -315
- data/lib/carray/convert.rb +0 -115
- data/lib/carray/info.rb +0 -110
- data/lib/carray/io/imagemagick.rb +0 -235
- data/lib/carray/mask.rb +0 -102
- data/lib/carray/math/histogram.rb +0 -177
- data/lib/carray/math/recurrence.rb +0 -93
- data/lib/carray/object/ca_obj_iterator.rb +0 -50
- data/lib/carray/object/ca_obj_link.rb +0 -50
- data/lib/carray/object/ca_obj_pack.rb +0 -99
- data/lib/carray/obsolete.rb +0 -256
- data/lib/carray/ordering.rb +0 -181
- data/lib/carray/testing.rb +0 -51
- data/lib/carray/transform.rb +0 -109
- data/misc/Methods.ja.md +0 -182
- data/misc/NOTE +0 -51
- data/spec/Classes/CABitfield_spec.rb +0 -58
- data/spec/Classes/CABlockIterator_spec.rb +0 -114
- data/spec/Classes/CABlock_spec.rb +0 -205
- data/spec/Classes/CAField_spec.rb +0 -39
- data/spec/Classes/CAGrid_spec.rb +0 -75
- data/spec/Classes/CAMap_spec.rb +0 -0
- data/spec/Classes/CAMapping_spec.rb +0 -105
- data/spec/Classes/CAObject_attribute_spec.rb +0 -33
- data/spec/Classes/CAObject_spec.rb +0 -33
- data/spec/Classes/CARefer_spec.rb +0 -93
- data/spec/Classes/CARepeat_spec.rb +0 -65
- data/spec/Classes/CASelect_spec.rb +0 -22
- data/spec/Classes/CAShift_spec.rb +0 -16
- data/spec/Classes/CAStruct_spec.rb +0 -71
- data/spec/Classes/CATranspose_spec.rb +0 -60
- data/spec/Classes/CAUnboudRepeat_spec.rb +0 -102
- data/spec/Classes/CAWindow_spec.rb +0 -54
- data/spec/Classes/CAWrap_spec.rb +0 -8
- data/spec/Classes/CArray_spec.rb +0 -184
- data/spec/Classes/CScalar_spec.rb +0 -55
- data/spec/Classes/ex1.rb +0 -46
- data/spec/Features/feature_130_spec.rb +0 -19
- data/spec/Features/feature_attributes_spec.rb +0 -280
- data/spec/Features/feature_boolean_spec.rb +0 -98
- data/spec/Features/feature_broadcast.rb +0 -116
- data/spec/Features/feature_cast_function.rb +0 -19
- data/spec/Features/feature_cast_spec.rb +0 -33
- data/spec/Features/feature_class_spec.rb +0 -84
- data/spec/Features/feature_complex_spec.rb +0 -42
- data/spec/Features/feature_composite_spec.rb +0 -124
- data/spec/Features/feature_convert_spec.rb +0 -46
- data/spec/Features/feature_copy_spec.rb +0 -123
- data/spec/Features/feature_creation_spec.rb +0 -84
- data/spec/Features/feature_element_spec.rb +0 -144
- data/spec/Features/feature_extream_spec.rb +0 -54
- data/spec/Features/feature_generate_spec.rb +0 -74
- data/spec/Features/feature_index_spec.rb +0 -69
- data/spec/Features/feature_mask_spec.rb +0 -580
- data/spec/Features/feature_math_spec.rb +0 -97
- data/spec/Features/feature_order_spec.rb +0 -146
- data/spec/Features/feature_ref_store_spec.rb +0 -209
- data/spec/Features/feature_serialization_spec.rb +0 -125
- data/spec/Features/feature_stat_spec.rb +0 -397
- data/spec/Features/feature_virtual_spec.rb +0 -48
- data/spec/Features/method_eq_spec.rb +0 -81
- data/spec/Features/method_is_nan_spec.rb +0 -12
- data/spec/Features/method_map_spec.rb +0 -54
- data/spec/Features/method_max_with.rb +0 -20
- data/spec/Features/method_min_with.rb +0 -19
- data/spec/Features/method_ne_spec.rb +0 -18
- data/spec/Features/method_project_spec.rb +0 -188
- data/spec/Features/method_ref_spec.rb +0 -27
- data/spec/Features/method_round_spec.rb +0 -11
- data/spec/Features/method_s_linspace_spec.rb +0 -48
- data/spec/Features/method_s_span_spec.rb +0 -14
- data/spec/Features/method_seq_spec.rb +0 -47
- data/spec/Features/method_sort_with.rb +0 -43
- data/spec/Features/method_sorted_with.rb +0 -29
- data/spec/Features/method_span_spec.rb +0 -42
- data/spec/Features/method_wrap_readonly_spec.rb +0 -43
- data/spec/UnitTest/test_CAVirtual.rb +0 -214
- data/spec/spec_all.rb +0 -10
- data/utils/ca_ase.rb +0 -21
- data/utils/ca_methods.rb +0 -15
- data/utils/cast_checker.rb +0 -30
- data/utils/convert_test.rb +0 -73
- data/utils/extract_yard.rb +0 -22
- data/utils/guess_shape.rb +0 -76
- data/utils/monkey_patch_methods.rb +0 -62
- data/utils/remove_resource_fork.sh +0 -5
|
@@ -0,0 +1,620 @@
|
|
|
1
|
+
/* ---------------------------------------------------------------------------
|
|
2
|
+
|
|
3
|
+
Portable textbook sort kernels: per-dtype quicksort + bottom-up
|
|
4
|
+
mergesort with inline comparison (no function-pointer indirection
|
|
5
|
+
like libc qsort / mergesort). All 10 numeric dtypes plus paired
|
|
6
|
+
forms for argsort.
|
|
7
|
+
|
|
8
|
+
Internal C API only (no Ruby surface, no Init function). Declared
|
|
9
|
+
in ca_sort_kernels.h. Callers: rb_ca_sort_copy (carray_sort.c),
|
|
10
|
+
rb_ca_partition_copy_c (carray_partition.c), and the mkkernel-
|
|
11
|
+
emitted sort_addr_ki kernels in carray_kernels.c.
|
|
12
|
+
|
|
13
|
+
Algorithms:
|
|
14
|
+
|
|
15
|
+
* quicksort (ca_sort_quick_<dtype>)
|
|
16
|
+
median-of-3 pivot, Hoare partition, strict `<`, insertion-sort
|
|
17
|
+
base (threshold 16), smaller-side recursion / larger-side
|
|
18
|
+
iterate (stack O(log n)). Depth-limit escape to mergesort for
|
|
19
|
+
worst-case O(n log n).
|
|
20
|
+
|
|
21
|
+
* mergesort (ca_sort_merge_<dtype>)
|
|
22
|
+
Bottom-up, ping-pong (cur / next) per pass with no inter-pass
|
|
23
|
+
memcpy. Strict `<` (stable: equal -> take-left). Insertion-
|
|
24
|
+
sort pre-pass that converts the input into R=16-wide sorted
|
|
25
|
+
blocks before the merge loop. Single-compare sorted-skip
|
|
26
|
+
(per-merge `!(cur[mid] < cur[mid-1])` test, true -> memcpy and
|
|
27
|
+
skip the merge loop; no run-state machine, distinct from
|
|
28
|
+
Timsort galloping).
|
|
29
|
+
|
|
30
|
+
* paired forms (ca_sort_quick_pair_<dtype> / ca_sort_merge_pair_<dtype>)
|
|
31
|
+
Same algorithms over (value, index) pairs for argsort
|
|
32
|
+
(sort_index / sort_addr). Stable tie-break by index is built
|
|
33
|
+
into the comparison, so quicksort is effectively stable for
|
|
34
|
+
argsort even though the algorithm itself is not.
|
|
35
|
+
|
|
36
|
+
* NaN partitioning (ca_partition_nan_<dtype> + _pair_<dtype>)
|
|
37
|
+
float32 / float64 only. One-pass Hoare-style partition that
|
|
38
|
+
separates `[finite | NaN]` regions so the downstream sort
|
|
39
|
+
kernel can operate on the finite slice with plain `<`.
|
|
40
|
+
|
|
41
|
+
* value-level quickselect (ca_partition_quick_<dtype>)
|
|
42
|
+
median-of-3 + Hoare + insertion base + one-sided recursion
|
|
43
|
+
into the side containing kth (quickselect property, expected
|
|
44
|
+
O(n)). Depth-limit escape to mergesort matches the sort
|
|
45
|
+
kernels.
|
|
46
|
+
|
|
47
|
+
---------------------------------------------------------------------------*/
|
|
48
|
+
|
|
49
|
+
#include "carray.h"
|
|
50
|
+
#include "ca_sort_kernels.h"
|
|
51
|
+
#include <string.h>
|
|
52
|
+
#include <math.h>
|
|
53
|
+
|
|
54
|
+
#define CA_SORT_INSERTION_THRESHOLD 16
|
|
55
|
+
|
|
56
|
+
/* ---------------------------------------------------------------------------
|
|
57
|
+
DEFINE_SORT_MERGE(TYPE, SUFFIX)
|
|
58
|
+
|
|
59
|
+
Emits public `void ca_sort_merge_<SUFFIX>(TYPE *a, TYPE *aux,
|
|
60
|
+
ca_size_t n)`.
|
|
61
|
+
`aux` must be a caller-provided buffer of `n * sizeof(TYPE)` bytes.
|
|
62
|
+
--------------------------------------------------------------------------- */
|
|
63
|
+
|
|
64
|
+
#define DEFINE_SORT_MERGE(TYPE, SUFFIX) \
|
|
65
|
+
\
|
|
66
|
+
/* insertion-sort pre-pass on a[lo..hi-1] (half-open). R-wide blocks. \
|
|
67
|
+
Writes only into `a` (no buffer juggling). Body is intentionally \
|
|
68
|
+
identical to the quicksort insertion base. */ \
|
|
69
|
+
static void \
|
|
70
|
+
ca_sort_merge_##SUFFIX##_inspass (TYPE *a, ca_size_t n) \
|
|
71
|
+
{ \
|
|
72
|
+
ca_size_t lo = 0; \
|
|
73
|
+
while ( lo < n ) { \
|
|
74
|
+
ca_size_t hi_excl = lo + CA_SORT_INSERTION_THRESHOLD; \
|
|
75
|
+
if ( hi_excl > n ) hi_excl = n; \
|
|
76
|
+
for ( ca_size_t k = lo + 1; k < hi_excl; k++ ) { \
|
|
77
|
+
TYPE v = a[k]; \
|
|
78
|
+
ca_size_t m = k; \
|
|
79
|
+
while ( m > lo && v < a[m - 1] ) { \
|
|
80
|
+
a[m] = a[m - 1]; \
|
|
81
|
+
m--; \
|
|
82
|
+
} \
|
|
83
|
+
a[m] = v; \
|
|
84
|
+
} \
|
|
85
|
+
lo = hi_excl; \
|
|
86
|
+
} \
|
|
87
|
+
} \
|
|
88
|
+
\
|
|
89
|
+
void \
|
|
90
|
+
ca_sort_merge_##SUFFIX (TYPE *a, TYPE *aux, ca_size_t n) \
|
|
91
|
+
{ \
|
|
92
|
+
if ( n <= 1 ) return; \
|
|
93
|
+
\
|
|
94
|
+
/* Pre-pass: convert a[] into R=16-wide sorted blocks in place. */ \
|
|
95
|
+
ca_sort_merge_##SUFFIX##_inspass(a, n); \
|
|
96
|
+
\
|
|
97
|
+
TYPE *cur = a; \
|
|
98
|
+
TYPE *next = aux; \
|
|
99
|
+
\
|
|
100
|
+
/* Start width = R (= pre-pass output run width). */ \
|
|
101
|
+
for ( ca_size_t width = CA_SORT_INSERTION_THRESHOLD; \
|
|
102
|
+
width < n; \
|
|
103
|
+
width <<= 1 ) { \
|
|
104
|
+
for ( ca_size_t lo = 0; lo < n; lo += (width << 1) ) { \
|
|
105
|
+
ca_size_t mid = lo + width; \
|
|
106
|
+
ca_size_t hi = lo + (width << 1); \
|
|
107
|
+
/* CAREFUL: tail run clamp must run before the drain `while` \
|
|
108
|
+
loops below; without these the drains over-walk the buffer \
|
|
109
|
+
when n is not a power of 2 times width. */ \
|
|
110
|
+
if ( mid > n ) mid = n; \
|
|
111
|
+
if ( hi > n ) hi = n; \
|
|
112
|
+
\
|
|
113
|
+
/* Single-compare sorted-skip: if left tail <= right head, the merge \
|
|
114
|
+
is the identity in [lo, hi). Strict `<` here so equal heads also \
|
|
115
|
+
skip (stable: take-left). Cheap memcpy keeps invariant that \
|
|
116
|
+
`next[lo..hi)` is the merged output. */ \
|
|
117
|
+
if ( mid < hi && !(cur[mid] < cur[mid - 1]) ) { \
|
|
118
|
+
memcpy(next + lo, cur + lo, (size_t)(hi - lo) * sizeof(TYPE)); \
|
|
119
|
+
continue; \
|
|
120
|
+
} \
|
|
121
|
+
\
|
|
122
|
+
ca_size_t i = lo, j = mid, k = lo; \
|
|
123
|
+
/* strict `<` -> equal-key left-take -> stable. */ \
|
|
124
|
+
while ( i < mid && j < hi ) { \
|
|
125
|
+
if ( cur[j] < cur[i] ) next[k++] = cur[j++]; \
|
|
126
|
+
else next[k++] = cur[i++]; \
|
|
127
|
+
} \
|
|
128
|
+
/* Drain residual tail (paired with the clamp above). */ \
|
|
129
|
+
while ( i < mid ) next[k++] = cur[i++]; \
|
|
130
|
+
while ( j < hi ) next[k++] = cur[j++]; \
|
|
131
|
+
} \
|
|
132
|
+
/* ping-pong: swap roles for next pass. */ \
|
|
133
|
+
{ TYPE *t = cur; cur = next; next = t; } \
|
|
134
|
+
} \
|
|
135
|
+
\
|
|
136
|
+
/* CAREFUL: parity check by POINTER IDENTITY, never by pass counter. \
|
|
137
|
+
The pre-pass + width loop may leave `cur` pointing at either `a` or \
|
|
138
|
+
`aux` depending on dataset size and the sorted-skip short-circuit; \
|
|
139
|
+
a pass-count parity check is unreliable. Single fix-up memcpy when \
|
|
140
|
+
the result landed in `aux`. */ \
|
|
141
|
+
if ( cur != a ) { \
|
|
142
|
+
memcpy(a, cur, (size_t) n * sizeof(TYPE)); \
|
|
143
|
+
} \
|
|
144
|
+
} \
|
|
145
|
+
struct ca_sort_merge_##SUFFIX##_eat_semicolon
|
|
146
|
+
|
|
147
|
+
|
|
148
|
+
/* ---------------------------------------------------------------------------
|
|
149
|
+
DEFINE_SORT_QUICK(TYPE, SUFFIX)
|
|
150
|
+
|
|
151
|
+
Emits public `void ca_sort_quick_<SUFFIX>(TYPE *a, ca_size_t n)`.
|
|
152
|
+
Algorithm: median-of-3 pivot + Hoare partition + insertion-sort
|
|
153
|
+
base (threshold 16) + smaller-side recursion / larger-side
|
|
154
|
+
iterate (stack O(log n)).
|
|
155
|
+
|
|
156
|
+
Worst-case guarantee: a recursion depth counter starts at
|
|
157
|
+
2*floor(log2(n)) + 2. Once a branch exceeds that limit the
|
|
158
|
+
remaining subrange escapes to ca_sort_merge_<SUFFIX>, which is
|
|
159
|
+
O(n log n) regardless of input shape (= avoids heapsort by
|
|
160
|
+
reusing the existing merge path).
|
|
161
|
+
--------------------------------------------------------------------- */
|
|
162
|
+
|
|
163
|
+
#define DEFINE_SORT_QUICK(TYPE, SUFFIX) \
|
|
164
|
+
\
|
|
165
|
+
static void \
|
|
166
|
+
ca_sort_quick_##SUFFIX##_range (TYPE *a, ca_size_t lo, ca_size_t hi, \
|
|
167
|
+
int depth_limit) \
|
|
168
|
+
{ \
|
|
169
|
+
while ( hi - lo > CA_SORT_INSERTION_THRESHOLD ) { \
|
|
170
|
+
if ( depth_limit <= 0 ) { \
|
|
171
|
+
/* Depth-limit escape: mergesort the remaining subrange. \
|
|
172
|
+
The surrounding quicksort loop has already partitioned the \
|
|
173
|
+
higher subranges, so only this [lo, hi] window needs the aux \
|
|
174
|
+
buffer (local malloc/free). */ \
|
|
175
|
+
ca_size_t n_remain = hi - lo + 1; \
|
|
176
|
+
TYPE *aux = (TYPE *) xmalloc((size_t) n_remain * sizeof(TYPE)); \
|
|
177
|
+
ca_sort_merge_##SUFFIX(a + lo, aux, n_remain); \
|
|
178
|
+
xfree(aux); \
|
|
179
|
+
return; \
|
|
180
|
+
} \
|
|
181
|
+
depth_limit--; \
|
|
182
|
+
ca_size_t mid = lo + (hi - lo) / 2; \
|
|
183
|
+
/* median-of-3: order a[lo] <= a[mid] <= a[hi] */ \
|
|
184
|
+
if ( a[mid] < a[lo] ) { TYPE t = a[lo]; a[lo] = a[mid]; a[mid] = t; } \
|
|
185
|
+
if ( a[hi] < a[lo] ) { TYPE t = a[lo]; a[lo] = a[hi]; a[hi] = t; } \
|
|
186
|
+
if ( a[hi] < a[mid] ) { TYPE t = a[mid]; a[mid] = a[hi]; a[hi] = t; } \
|
|
187
|
+
TYPE pivot = a[mid]; \
|
|
188
|
+
/* Hoare partition */ \
|
|
189
|
+
ca_size_t i = lo, j = hi; \
|
|
190
|
+
for (;;) { \
|
|
191
|
+
while ( a[i] < pivot ) i++; \
|
|
192
|
+
while ( pivot < a[j] ) j--; \
|
|
193
|
+
if ( i >= j ) break; \
|
|
194
|
+
{ TYPE t = a[i]; a[i] = a[j]; a[j] = t; } \
|
|
195
|
+
i++; \
|
|
196
|
+
j--; \
|
|
197
|
+
} \
|
|
198
|
+
/* smaller side recurse, larger side iterate (stack O(log n)) */ \
|
|
199
|
+
if ( j - lo < hi - (j + 1) ) { \
|
|
200
|
+
ca_sort_quick_##SUFFIX##_range(a, lo, j, depth_limit); \
|
|
201
|
+
lo = j + 1; \
|
|
202
|
+
} else { \
|
|
203
|
+
ca_sort_quick_##SUFFIX##_range(a, j + 1, hi, depth_limit); \
|
|
204
|
+
hi = j; \
|
|
205
|
+
} \
|
|
206
|
+
} \
|
|
207
|
+
/* insertion-sort base case */ \
|
|
208
|
+
for ( ca_size_t k = lo + 1; k <= hi; k++ ) { \
|
|
209
|
+
TYPE v = a[k]; \
|
|
210
|
+
ca_size_t m = k; \
|
|
211
|
+
while ( m > lo && v < a[m - 1] ) { \
|
|
212
|
+
a[m] = a[m - 1]; \
|
|
213
|
+
m--; \
|
|
214
|
+
} \
|
|
215
|
+
a[m] = v; \
|
|
216
|
+
} \
|
|
217
|
+
} \
|
|
218
|
+
\
|
|
219
|
+
void \
|
|
220
|
+
ca_sort_quick_##SUFFIX (TYPE *a, ca_size_t n) \
|
|
221
|
+
{ \
|
|
222
|
+
if ( n <= 1 ) return; \
|
|
223
|
+
/* depth_limit = 2 * floor(log2(n)) + 2, computed via integer \
|
|
224
|
+
bit-length (cheap and exact for ca_size_t). */ \
|
|
225
|
+
int depth_limit = 0; \
|
|
226
|
+
ca_size_t m = n; \
|
|
227
|
+
while ( m > 0 ) { depth_limit++; m >>= 1; } \
|
|
228
|
+
depth_limit *= 2; \
|
|
229
|
+
ca_sort_quick_##SUFFIX##_range(a, 0, n - 1, depth_limit); \
|
|
230
|
+
} \
|
|
231
|
+
struct ca_sort_quick_##SUFFIX##_eat_semicolon
|
|
232
|
+
|
|
233
|
+
|
|
234
|
+
/* ---------------------------------------------------------------------------
|
|
235
|
+
PAIR sort macros (argsort kernels).
|
|
236
|
+
|
|
237
|
+
The pair form sorts an array of (value, index) structs by value,
|
|
238
|
+
used by sort_index / sort_addr to obtain the permutation. Stable
|
|
239
|
+
tie-break is built into the comparison (equal values fall through
|
|
240
|
+
to compare indices), so quicksort is effectively stable for
|
|
241
|
+
argsort even though the algorithm itself is not.
|
|
242
|
+
|
|
243
|
+
Structurally identical to the scalar quick / merge macros above;
|
|
244
|
+
the only differences are (a) pair struct typedef, (b) inline cmp
|
|
245
|
+
that includes the index tie-break, (c) per-cell swap copies the
|
|
246
|
+
whole pair via TYPE temp.
|
|
247
|
+
|
|
248
|
+
Pair memory footprint: sizeof(TYPE) + sizeof(ca_size_t) (8 bytes
|
|
249
|
+
alignment-padded). Caller allocates N pairs once per fiber
|
|
250
|
+
(mkkernel emit_sort_native in carray_kernels.c).
|
|
251
|
+
--------------------------------------------------------------------- */
|
|
252
|
+
|
|
253
|
+
/* Pair struct typedefs live in carray.h so callers (mkkernel emit) can
|
|
254
|
+
allocate them by name. The macro below skips the typedef and only
|
|
255
|
+
defines the algorithm. */
|
|
256
|
+
#define DEFINE_SORT_PAIR(TYPE, SUFFIX) \
|
|
257
|
+
\
|
|
258
|
+
/* ---- pair mergesort (= stable; called by quick escape too) ---- */ \
|
|
259
|
+
\
|
|
260
|
+
static void \
|
|
261
|
+
ca_sort_merge_pair_##SUFFIX##_inspass (ca_pair_##SUFFIX *a, ca_size_t n) \
|
|
262
|
+
{ \
|
|
263
|
+
ca_size_t lo = 0; \
|
|
264
|
+
while ( lo < n ) { \
|
|
265
|
+
ca_size_t hi_excl = lo + CA_SORT_INSERTION_THRESHOLD; \
|
|
266
|
+
if ( hi_excl > n ) hi_excl = n; \
|
|
267
|
+
for ( ca_size_t k = lo + 1; k < hi_excl; k++ ) { \
|
|
268
|
+
ca_pair_##SUFFIX v = a[k]; \
|
|
269
|
+
ca_size_t m = k; \
|
|
270
|
+
/* stable cmp: equal values keep ascending index (= left-take) */ \
|
|
271
|
+
while ( m > lo && \
|
|
272
|
+
( v.v < a[m - 1].v || \
|
|
273
|
+
(!(a[m - 1].v < v.v) && v.i < a[m - 1].i) ) ) { \
|
|
274
|
+
a[m] = a[m - 1]; \
|
|
275
|
+
m--; \
|
|
276
|
+
} \
|
|
277
|
+
a[m] = v; \
|
|
278
|
+
} \
|
|
279
|
+
lo = hi_excl; \
|
|
280
|
+
} \
|
|
281
|
+
} \
|
|
282
|
+
\
|
|
283
|
+
void \
|
|
284
|
+
ca_sort_merge_pair_##SUFFIX (ca_pair_##SUFFIX *a, \
|
|
285
|
+
ca_pair_##SUFFIX *aux, \
|
|
286
|
+
ca_size_t n) \
|
|
287
|
+
{ \
|
|
288
|
+
if ( n <= 1 ) return; \
|
|
289
|
+
ca_sort_merge_pair_##SUFFIX##_inspass(a, n); \
|
|
290
|
+
ca_pair_##SUFFIX *cur = a; \
|
|
291
|
+
ca_pair_##SUFFIX *next = aux; \
|
|
292
|
+
for ( ca_size_t width = CA_SORT_INSERTION_THRESHOLD; \
|
|
293
|
+
width < n; \
|
|
294
|
+
width <<= 1 ) { \
|
|
295
|
+
for ( ca_size_t lo = 0; lo < n; lo += (width << 1) ) { \
|
|
296
|
+
ca_size_t mid = lo + width; \
|
|
297
|
+
ca_size_t hi = lo + (width << 1); \
|
|
298
|
+
if ( mid > n ) mid = n; \
|
|
299
|
+
if ( hi > n ) hi = n; \
|
|
300
|
+
/* sorted-skip: left-tail <= right-head (stable cmp) */ \
|
|
301
|
+
if ( mid < hi ) { \
|
|
302
|
+
TYPE lt = cur[mid - 1].v; \
|
|
303
|
+
TYPE rh = cur[mid].v; \
|
|
304
|
+
ca_size_t li = cur[mid - 1].i; \
|
|
305
|
+
ca_size_t ri = cur[mid].i; \
|
|
306
|
+
int strict_left_smaller = (lt < rh) || \
|
|
307
|
+
(!(rh < lt) && li < ri); \
|
|
308
|
+
int equal = !(lt < rh) && !(rh < lt) && (li == ri); \
|
|
309
|
+
if ( strict_left_smaller || equal ) { \
|
|
310
|
+
memcpy(next + lo, cur + lo, \
|
|
311
|
+
(size_t)(hi - lo) * sizeof(ca_pair_##SUFFIX)); \
|
|
312
|
+
continue; \
|
|
313
|
+
} \
|
|
314
|
+
} \
|
|
315
|
+
ca_size_t i = lo, j = mid, k = lo; \
|
|
316
|
+
while ( i < mid && j < hi ) { \
|
|
317
|
+
/* stable: if cur[j] < cur[i] strictly, take j; else take i */ \
|
|
318
|
+
int take_j = (cur[j].v < cur[i].v) || \
|
|
319
|
+
(!(cur[i].v < cur[j].v) && cur[j].i < cur[i].i); \
|
|
320
|
+
if ( take_j ) next[k++] = cur[j++]; \
|
|
321
|
+
else next[k++] = cur[i++]; \
|
|
322
|
+
} \
|
|
323
|
+
while ( i < mid ) next[k++] = cur[i++]; \
|
|
324
|
+
while ( j < hi ) next[k++] = cur[j++]; \
|
|
325
|
+
} \
|
|
326
|
+
{ ca_pair_##SUFFIX *t = cur; cur = next; next = t; } \
|
|
327
|
+
} \
|
|
328
|
+
if ( cur != a ) { \
|
|
329
|
+
memcpy(a, cur, (size_t) n * sizeof(ca_pair_##SUFFIX)); \
|
|
330
|
+
} \
|
|
331
|
+
} \
|
|
332
|
+
\
|
|
333
|
+
/* ---- pair quicksort (= stable via index tie-break + depth-limit escape) ----*/ \
|
|
334
|
+
\
|
|
335
|
+
static void \
|
|
336
|
+
ca_sort_quick_pair_##SUFFIX##_range (ca_pair_##SUFFIX *a, \
|
|
337
|
+
ca_size_t lo, ca_size_t hi, \
|
|
338
|
+
int depth_limit) \
|
|
339
|
+
{ \
|
|
340
|
+
while ( hi - lo > CA_SORT_INSERTION_THRESHOLD ) { \
|
|
341
|
+
if ( depth_limit <= 0 ) { \
|
|
342
|
+
ca_size_t n_remain = hi - lo + 1; \
|
|
343
|
+
ca_pair_##SUFFIX *aux = \
|
|
344
|
+
(ca_pair_##SUFFIX *) xmalloc((size_t) n_remain * sizeof(ca_pair_##SUFFIX)); \
|
|
345
|
+
ca_sort_merge_pair_##SUFFIX(a + lo, aux, n_remain); \
|
|
346
|
+
xfree(aux); \
|
|
347
|
+
return; \
|
|
348
|
+
} \
|
|
349
|
+
depth_limit--; \
|
|
350
|
+
ca_size_t mid = lo + (hi - lo) / 2; \
|
|
351
|
+
/* median-of-3 by value (tie-break NOT applied here -- approximate \
|
|
352
|
+
median is fine; full stable cmp applies during partition). */ \
|
|
353
|
+
if ( a[mid].v < a[lo].v ) { ca_pair_##SUFFIX t = a[lo]; a[lo] = a[mid]; a[mid] = t; } \
|
|
354
|
+
if ( a[hi].v < a[lo].v ) { ca_pair_##SUFFIX t = a[lo]; a[lo] = a[hi]; a[hi] = t; } \
|
|
355
|
+
if ( a[hi].v < a[mid].v ) { ca_pair_##SUFFIX t = a[mid]; a[mid] = a[hi]; a[hi] = t; } \
|
|
356
|
+
TYPE pivot_v = a[mid].v; \
|
|
357
|
+
ca_size_t pivot_i = a[mid].i; \
|
|
358
|
+
/* Hoare partition with stable cmp (= compare by v, tie-break by i). */ \
|
|
359
|
+
ca_size_t i = lo, j = hi; \
|
|
360
|
+
for (;;) { \
|
|
361
|
+
while ( a[i].v < pivot_v || \
|
|
362
|
+
(!(pivot_v < a[i].v) && a[i].i < pivot_i) ) i++; \
|
|
363
|
+
while ( pivot_v < a[j].v || \
|
|
364
|
+
(!(a[j].v < pivot_v) && pivot_i < a[j].i) ) j--; \
|
|
365
|
+
if ( i >= j ) break; \
|
|
366
|
+
{ ca_pair_##SUFFIX t = a[i]; a[i] = a[j]; a[j] = t; } \
|
|
367
|
+
i++; \
|
|
368
|
+
j--; \
|
|
369
|
+
} \
|
|
370
|
+
if ( j - lo < hi - (j + 1) ) { \
|
|
371
|
+
ca_sort_quick_pair_##SUFFIX##_range(a, lo, j, depth_limit); \
|
|
372
|
+
lo = j + 1; \
|
|
373
|
+
} else { \
|
|
374
|
+
ca_sort_quick_pair_##SUFFIX##_range(a, j + 1, hi, depth_limit); \
|
|
375
|
+
hi = j; \
|
|
376
|
+
} \
|
|
377
|
+
} \
|
|
378
|
+
/* insertion-sort base (stable cmp). */ \
|
|
379
|
+
for ( ca_size_t k = lo + 1; k <= hi; k++ ) { \
|
|
380
|
+
ca_pair_##SUFFIX v = a[k]; \
|
|
381
|
+
ca_size_t m = k; \
|
|
382
|
+
while ( m > lo && \
|
|
383
|
+
( v.v < a[m - 1].v || \
|
|
384
|
+
(!(a[m - 1].v < v.v) && v.i < a[m - 1].i) ) ) { \
|
|
385
|
+
a[m] = a[m - 1]; \
|
|
386
|
+
m--; \
|
|
387
|
+
} \
|
|
388
|
+
a[m] = v; \
|
|
389
|
+
} \
|
|
390
|
+
} \
|
|
391
|
+
\
|
|
392
|
+
void \
|
|
393
|
+
ca_sort_quick_pair_##SUFFIX (ca_pair_##SUFFIX *a, ca_size_t n) \
|
|
394
|
+
{ \
|
|
395
|
+
if ( n <= 1 ) return; \
|
|
396
|
+
int depth_limit = 0; \
|
|
397
|
+
ca_size_t m = n; \
|
|
398
|
+
while ( m > 0 ) { depth_limit++; m >>= 1; } \
|
|
399
|
+
depth_limit *= 2; \
|
|
400
|
+
ca_sort_quick_pair_##SUFFIX##_range(a, 0, n - 1, depth_limit); \
|
|
401
|
+
} \
|
|
402
|
+
struct ca_sort_pair_##SUFFIX##_eat_semicolon
|
|
403
|
+
|
|
404
|
+
|
|
405
|
+
/* ===== Per-dtype instantiations ===========================================
|
|
406
|
+
`<` is well-defined and IEEE-stable for all numeric C types used
|
|
407
|
+
here; NaN handling for f32 / f64 lives in the pre-partition pass
|
|
408
|
+
below. */
|
|
409
|
+
|
|
410
|
+
/* ---------------------------------------------------------------------------
|
|
411
|
+
NaN pre-partition (float dtypes only).
|
|
412
|
+
|
|
413
|
+
One-pass Hoare-style partition that separates `a[0..n)` into
|
|
414
|
+
`[finite | NaN]` regions in place, returning the finite count k
|
|
415
|
+
(the NaN region starts at a[k..n)). Unstable within the finite
|
|
416
|
+
region (original order of finite values not preserved) -- the
|
|
417
|
+
downstream sort kernel reorders them anyway.
|
|
418
|
+
|
|
419
|
+
Loop invariant:
|
|
420
|
+
- a[0 .. i) all finite
|
|
421
|
+
- a[j .. n) all NaN
|
|
422
|
+
- a[i .. j) unprocessed
|
|
423
|
+
Terminates when i == j.
|
|
424
|
+
|
|
425
|
+
`isnan(a[i])` is the only NaN-aware logic. The downstream sort
|
|
426
|
+
kernels treat the finite slice with plain `<`, which is well-
|
|
427
|
+
defined and IEEE-stable for finite operands.
|
|
428
|
+
--------------------------------------------------------------------------- */
|
|
429
|
+
|
|
430
|
+
#define DEFINE_PARTITION_NAN(TYPE, SUFFIX) \
|
|
431
|
+
ca_size_t \
|
|
432
|
+
ca_partition_nan_##SUFFIX (TYPE *a, ca_size_t n) \
|
|
433
|
+
{ \
|
|
434
|
+
ca_size_t i = 0, j = n; \
|
|
435
|
+
while ( i < j ) { \
|
|
436
|
+
if ( isnan(a[i]) ) { \
|
|
437
|
+
j--; \
|
|
438
|
+
TYPE t = a[j]; a[j] = a[i]; a[i] = t; \
|
|
439
|
+
} else { \
|
|
440
|
+
i++; \
|
|
441
|
+
} \
|
|
442
|
+
} \
|
|
443
|
+
return i; \
|
|
444
|
+
} \
|
|
445
|
+
struct ca_partition_nan_##SUFFIX##_eat_semicolon
|
|
446
|
+
|
|
447
|
+
DEFINE_PARTITION_NAN(float32_t, f32);
|
|
448
|
+
DEFINE_PARTITION_NAN(double, f64);
|
|
449
|
+
|
|
450
|
+
/* Pair variant for argsort: separates ca_pair_<dtype> by
|
|
451
|
+
isnan(v.value). Returns the finite count.
|
|
452
|
+
|
|
453
|
+
Stable within both finite and NaN regions (original order
|
|
454
|
+
preserved), so argsort on NaN-containing input keeps NaN indices
|
|
455
|
+
in ascending order.
|
|
456
|
+
|
|
457
|
+
Implementation: 2-pass stable filter via xmalloc scratch (single
|
|
458
|
+
xmalloc + xfree per fiber; negligible overhead vs the sort
|
|
459
|
+
itself). */
|
|
460
|
+
#define DEFINE_PARTITION_NAN_PAIR(TYPE, SUFFIX) \
|
|
461
|
+
ca_size_t \
|
|
462
|
+
ca_partition_nan_pair_##SUFFIX (ca_pair_##SUFFIX *a, ca_size_t n) \
|
|
463
|
+
{ \
|
|
464
|
+
if ( n == 0 ) return 0; \
|
|
465
|
+
ca_pair_##SUFFIX *scratch = \
|
|
466
|
+
(ca_pair_##SUFFIX *) xmalloc((size_t) n * sizeof(ca_pair_##SUFFIX)); \
|
|
467
|
+
ca_size_t fin = 0, nan_pos = 0; \
|
|
468
|
+
for ( ca_size_t k = 0; k < n; k++ ) { \
|
|
469
|
+
if ( !isnan(a[k].v) ) scratch[fin++] = a[k]; \
|
|
470
|
+
} \
|
|
471
|
+
ca_size_t finite_count = fin; \
|
|
472
|
+
for ( ca_size_t k = 0; k < n; k++ ) { \
|
|
473
|
+
if ( isnan(a[k].v) ) scratch[finite_count + nan_pos++] = a[k]; \
|
|
474
|
+
} \
|
|
475
|
+
memcpy(a, scratch, (size_t) n * sizeof(ca_pair_##SUFFIX)); \
|
|
476
|
+
xfree(scratch); \
|
|
477
|
+
return finite_count; \
|
|
478
|
+
} \
|
|
479
|
+
struct ca_partition_nan_pair_##SUFFIX##_eat_semicolon
|
|
480
|
+
|
|
481
|
+
DEFINE_PARTITION_NAN_PAIR(float32_t, f32);
|
|
482
|
+
DEFINE_PARTITION_NAN_PAIR(double, f64);
|
|
483
|
+
|
|
484
|
+
|
|
485
|
+
/* ---------------------------------------------------------------------------
|
|
486
|
+
DEFINE_PARTITION_QUICK(TYPE, SUFFIX) -- value-level quickselect.
|
|
487
|
+
|
|
488
|
+
Emits public `void ca_partition_quick_<SUFFIX>(TYPE *a,
|
|
489
|
+
ca_size_t n, ca_size_t kth)` that reorders `a[0..n)` so that:
|
|
490
|
+
- a[kth] is the kth-smallest element under `<`
|
|
491
|
+
- a[0..kth-1] all compare <= a[kth]
|
|
492
|
+
- a[kth+1..n-1] all compare >= a[kth]
|
|
493
|
+
Order within the two regions is unspecified.
|
|
494
|
+
|
|
495
|
+
Algorithm: median-of-3 pivot + Hoare partition + insertion-sort
|
|
496
|
+
base (threshold 16) + one-sided recursion into the side
|
|
497
|
+
containing kth (true quickselect: expected O(n), stack O(log n)).
|
|
498
|
+
|
|
499
|
+
Worst-case guarantee: depth_limit = 2*floor(log2(n)) + 2 (matches
|
|
500
|
+
the sort kernels). On escape, mergesort the residual window;
|
|
501
|
+
mergesort fully sorts the window, which is strictly stronger than
|
|
502
|
+
the partition contract, so kth ends up correct as a side-effect.
|
|
503
|
+
Cost on escape: O(n log n) on the residual window only.
|
|
504
|
+
--------------------------------------------------------------------- */
|
|
505
|
+
|
|
506
|
+
#define DEFINE_PARTITION_QUICK(TYPE, SUFFIX) \
|
|
507
|
+
\
|
|
508
|
+
static void \
|
|
509
|
+
ca_partition_quick_##SUFFIX##_range (TYPE *a, ca_size_t lo, ca_size_t hi, \
|
|
510
|
+
ca_size_t kth, int depth_limit) \
|
|
511
|
+
{ \
|
|
512
|
+
while ( hi - lo > CA_SORT_INSERTION_THRESHOLD ) { \
|
|
513
|
+
if ( depth_limit <= 0 ) { \
|
|
514
|
+
/* Depth-limit escape: mergesort the residual window. A full \
|
|
515
|
+
sort is strictly stronger than the partition contract, so kth \
|
|
516
|
+
ends up correct as a side-effect. Cheaper than continuing to \
|
|
517
|
+
degrade on adversarial input. */ \
|
|
518
|
+
ca_size_t n_remain = hi - lo + 1; \
|
|
519
|
+
TYPE *aux = (TYPE *) xmalloc((size_t) n_remain * sizeof(TYPE)); \
|
|
520
|
+
ca_sort_merge_##SUFFIX(a + lo, aux, n_remain); \
|
|
521
|
+
xfree(aux); \
|
|
522
|
+
return; \
|
|
523
|
+
} \
|
|
524
|
+
depth_limit--; \
|
|
525
|
+
ca_size_t mid = lo + (hi - lo) / 2; \
|
|
526
|
+
/* median-of-3: order a[lo] <= a[mid] <= a[hi]. */ \
|
|
527
|
+
if ( a[mid] < a[lo] ) { TYPE t = a[lo]; a[lo] = a[mid]; a[mid] = t; } \
|
|
528
|
+
if ( a[hi] < a[lo] ) { TYPE t = a[lo]; a[lo] = a[hi]; a[hi] = t; } \
|
|
529
|
+
if ( a[hi] < a[mid] ) { TYPE t = a[mid]; a[mid] = a[hi]; a[hi] = t; } \
|
|
530
|
+
TYPE pivot = a[mid]; \
|
|
531
|
+
/* Hoare partition. */ \
|
|
532
|
+
ca_size_t i = lo, j = hi; \
|
|
533
|
+
for (;;) { \
|
|
534
|
+
while ( a[i] < pivot ) i++; \
|
|
535
|
+
while ( pivot < a[j] ) j--; \
|
|
536
|
+
if ( i >= j ) break; \
|
|
537
|
+
{ TYPE t = a[i]; a[i] = a[j]; a[j] = t; } \
|
|
538
|
+
i++; \
|
|
539
|
+
j--; \
|
|
540
|
+
} \
|
|
541
|
+
/* After partition: \
|
|
542
|
+
a[lo..j] all compare <= pivot \
|
|
543
|
+
a[j+1..hi] all compare >= pivot \
|
|
544
|
+
Recurse only into the side containing kth. \
|
|
545
|
+
If kth <= j: kth is in the left half (which contains pivot \
|
|
546
|
+
boundary), so we tighten hi = j. \
|
|
547
|
+
Else: kth is in the right half, lo = j + 1. \
|
|
548
|
+
This is the quickselect property: expected O(n) work since each \
|
|
549
|
+
step halves the candidate window. */ \
|
|
550
|
+
if ( kth <= j ) { \
|
|
551
|
+
hi = j; \
|
|
552
|
+
} else { \
|
|
553
|
+
lo = j + 1; \
|
|
554
|
+
} \
|
|
555
|
+
} \
|
|
556
|
+
/* insertion-sort the residual base window so a[kth] is exact within \
|
|
557
|
+
[lo, hi]. Cheaper than another quickselect step and gives the \
|
|
558
|
+
partition contract for free across the small window. */ \
|
|
559
|
+
for ( ca_size_t k = lo + 1; k <= hi; k++ ) { \
|
|
560
|
+
TYPE v = a[k]; \
|
|
561
|
+
ca_size_t m = k; \
|
|
562
|
+
while ( m > lo && v < a[m - 1] ) { \
|
|
563
|
+
a[m] = a[m - 1]; \
|
|
564
|
+
m--; \
|
|
565
|
+
} \
|
|
566
|
+
a[m] = v; \
|
|
567
|
+
} \
|
|
568
|
+
} \
|
|
569
|
+
\
|
|
570
|
+
void \
|
|
571
|
+
ca_partition_quick_##SUFFIX (TYPE *a, ca_size_t n, ca_size_t kth) \
|
|
572
|
+
{ \
|
|
573
|
+
if ( n <= 1 ) return; \
|
|
574
|
+
if ( kth >= n ) return; /* out-of-range kth = no-op, caller validates */ \
|
|
575
|
+
int depth_limit = 0; \
|
|
576
|
+
ca_size_t m = n; \
|
|
577
|
+
while ( m > 0 ) { depth_limit++; m >>= 1; } \
|
|
578
|
+
depth_limit *= 2; \
|
|
579
|
+
ca_partition_quick_##SUFFIX##_range(a, 0, n - 1, kth, depth_limit); \
|
|
580
|
+
} \
|
|
581
|
+
struct ca_partition_quick_##SUFFIX##_eat_semicolon
|
|
582
|
+
|
|
583
|
+
|
|
584
|
+
DEFINE_SORT_QUICK(int8_t, i8); DEFINE_SORT_MERGE(int8_t, i8);
|
|
585
|
+
DEFINE_SORT_QUICK(uint8_t, u8); DEFINE_SORT_MERGE(uint8_t, u8);
|
|
586
|
+
DEFINE_SORT_QUICK(int16_t, i16); DEFINE_SORT_MERGE(int16_t, i16);
|
|
587
|
+
DEFINE_SORT_QUICK(uint16_t, u16); DEFINE_SORT_MERGE(uint16_t, u16);
|
|
588
|
+
DEFINE_SORT_QUICK(int32_t, i32); DEFINE_SORT_MERGE(int32_t, i32);
|
|
589
|
+
DEFINE_SORT_QUICK(uint32_t, u32); DEFINE_SORT_MERGE(uint32_t, u32);
|
|
590
|
+
DEFINE_SORT_QUICK(int64_t, i64); DEFINE_SORT_MERGE(int64_t, i64);
|
|
591
|
+
DEFINE_SORT_QUICK(uint64_t, u64); DEFINE_SORT_MERGE(uint64_t, u64);
|
|
592
|
+
DEFINE_SORT_QUICK(float32_t, f32); DEFINE_SORT_MERGE(float32_t, f32);
|
|
593
|
+
DEFINE_SORT_QUICK(double, f64); DEFINE_SORT_MERGE(double, f64);
|
|
594
|
+
|
|
595
|
+
/* Pair sort instantiations for argsort (sort_index / sort_addr). */
|
|
596
|
+
DEFINE_SORT_PAIR(int8_t, i8);
|
|
597
|
+
DEFINE_SORT_PAIR(uint8_t, u8);
|
|
598
|
+
DEFINE_SORT_PAIR(int16_t, i16);
|
|
599
|
+
DEFINE_SORT_PAIR(uint16_t, u16);
|
|
600
|
+
DEFINE_SORT_PAIR(int32_t, i32);
|
|
601
|
+
DEFINE_SORT_PAIR(uint32_t, u32);
|
|
602
|
+
DEFINE_SORT_PAIR(int64_t, i64);
|
|
603
|
+
DEFINE_SORT_PAIR(uint64_t, u64);
|
|
604
|
+
DEFINE_SORT_PAIR(float32_t, f32);
|
|
605
|
+
DEFINE_SORT_PAIR(double, f64);
|
|
606
|
+
|
|
607
|
+
/* Value-level quickselect for partition_copy. */
|
|
608
|
+
DEFINE_PARTITION_QUICK(int8_t, i8);
|
|
609
|
+
DEFINE_PARTITION_QUICK(uint8_t, u8);
|
|
610
|
+
DEFINE_PARTITION_QUICK(int16_t, i16);
|
|
611
|
+
DEFINE_PARTITION_QUICK(uint16_t, u16);
|
|
612
|
+
DEFINE_PARTITION_QUICK(int32_t, i32);
|
|
613
|
+
DEFINE_PARTITION_QUICK(uint32_t, u32);
|
|
614
|
+
DEFINE_PARTITION_QUICK(int64_t, i64);
|
|
615
|
+
DEFINE_PARTITION_QUICK(uint64_t, u64);
|
|
616
|
+
DEFINE_PARTITION_QUICK(float32_t, f32);
|
|
617
|
+
DEFINE_PARTITION_QUICK(double, f64);
|
|
618
|
+
|
|
619
|
+
/* No Init function: this file exposes only C kernels (via ca_sort_kernels.h);
|
|
620
|
+
it registers no Ruby methods. */
|