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
data/ext/carray_sort.c
ADDED
|
@@ -0,0 +1,829 @@
|
|
|
1
|
+
/* ---------------------------------------------------------------------------
|
|
2
|
+
|
|
3
|
+
Sort surface: sort / sort_copy (value & view), sort_addr / axis2addr
|
|
4
|
+
(address sort). The typed textbook sort kernels live in
|
|
5
|
+
carray_sort_kernel.c; partition / partition_copy in carray_partition.c.
|
|
6
|
+
|
|
7
|
+
---------------------------------------------------------------------------- */
|
|
8
|
+
|
|
9
|
+
#include "ruby.h"
|
|
10
|
+
#include "carray.h"
|
|
11
|
+
#include "ca_for_buffer.h" /* CA_WITH_BUFFER (scoped attach/detach) */
|
|
12
|
+
#include "ca_kernel_iterator.h" /* CA_FOR_EACH_FIBER_INOUT (sort_copy) */
|
|
13
|
+
#include "ca_obj_face.h" /* CA_FACE_LIFT_IF_FACE */
|
|
14
|
+
#include "ca_sort_kernels.h" /* ca_sort_quick_* / ca_sort_merge_* / ca_partition_nan_* */
|
|
15
|
+
#include "ca_compare.h" /* ca_elem_cmp[] -- shared element comparators */
|
|
16
|
+
#include <math.h>
|
|
17
|
+
#include <float.h>
|
|
18
|
+
|
|
19
|
+
/* ----------------------------------------------------------------- */
|
|
20
|
+
|
|
21
|
+
/* CA_FIXLEN comparator: direct memcmp over the packed byte width.
|
|
22
|
+
* The shared ca_elem_cmp table (ca_compare.h) leaves FIXLEN unsupported
|
|
23
|
+
* because a 2-arg comparator cannot carry the width; numeric / object
|
|
24
|
+
* data_types go through ca_elem_cmp[data_type].
|
|
25
|
+
*
|
|
26
|
+
* Called by sort_addr_cmp only. */
|
|
27
|
+
static int
|
|
28
|
+
cmp_fixlen_bytes (char *a, char *b, ca_size_t bytes)
|
|
29
|
+
{
|
|
30
|
+
return memcmp(a, b, (size_t) bytes);
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
/* ----------------------------------------------------------------- */
|
|
34
|
+
|
|
35
|
+
struct cmp_base {
|
|
36
|
+
int n;
|
|
37
|
+
CArray **ca;
|
|
38
|
+
int masked_last; /* 1 (default, :last) or 0 (:first) */
|
|
39
|
+
};
|
|
40
|
+
|
|
41
|
+
struct sort_addr_key {
|
|
42
|
+
ca_size_t i;
|
|
43
|
+
struct cmp_base *base;
|
|
44
|
+
};
|
|
45
|
+
|
|
46
|
+
/* Multi-key comparator for CArray.sort_addr: compares keys in priority
|
|
47
|
+
* order, original index breaks ties (stable). Masked cells are an
|
|
48
|
+
* incomparable sentinel clustered at base->masked_last's end (same
|
|
49
|
+
* role NaN plays for float dtypes, and the same masked_position:
|
|
50
|
+
* contract as the sort/partition family's :sentinel kernel mode --
|
|
51
|
+
* see MASKED_POSITION rev1 in mkkernel.rb's MkKernel.sort doc).
|
|
52
|
+
*
|
|
53
|
+
* Called as a qsort/mergesort callback in rb_ca_s_sort_addr. */
|
|
54
|
+
static int
|
|
55
|
+
sort_addr_cmp (struct sort_addr_key *a, struct sort_addr_key *b)
|
|
56
|
+
{
|
|
57
|
+
struct cmp_base *base = a->base;
|
|
58
|
+
int n = base->n;
|
|
59
|
+
CArray **ca = base->ca;
|
|
60
|
+
ca_size_t ia = a->i;
|
|
61
|
+
ca_size_t ib = b->i;
|
|
62
|
+
int result;
|
|
63
|
+
int i;
|
|
64
|
+
for (i=0; i<n; i++) {
|
|
65
|
+
int8_t data_type = ca[i]->data_type;
|
|
66
|
+
char *ptr = ca[i]->ptr;
|
|
67
|
+
boolean8_t *m = ( ca[i]->mask ) ? (boolean8_t *) ca[i]->mask->ptr : NULL;
|
|
68
|
+
ca_size_t bytes = ca[i]->bytes;
|
|
69
|
+
if ( ( ! m ) ||
|
|
70
|
+
( ( ! m[ia] ) && ( ! m[ib] ) ) ) {
|
|
71
|
+
if ( data_type == CA_FIXLEN ) {
|
|
72
|
+
result = cmp_fixlen_bytes(ptr + ia*bytes,
|
|
73
|
+
ptr + ib*bytes, bytes);
|
|
74
|
+
}
|
|
75
|
+
else {
|
|
76
|
+
result = ca_elem_cmp[data_type](ptr + ia*bytes,
|
|
77
|
+
ptr + ib*bytes);
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
else if ( ( ! m[ia] ) && ( m[ib] ) ) {
|
|
81
|
+
result = base->masked_last ? -1 : 1;
|
|
82
|
+
}
|
|
83
|
+
else if ( ( m[ia] ) && ( ! m[ib] ) ) {
|
|
84
|
+
result = base->masked_last ? 1 : -1;
|
|
85
|
+
}
|
|
86
|
+
else {
|
|
87
|
+
result = 0;
|
|
88
|
+
}
|
|
89
|
+
if ( result ) {
|
|
90
|
+
return result;
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
return ( ia > ib ) ? 1 : -1; /* for stable sort */
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
/* CArray.sort_addr(*args, masked_position: :last) — multi-key lex
|
|
97
|
+
* sort. Returns a 1-D CA_SIZE array of indices that sorts the
|
|
98
|
+
* arguments in priority order (a > b > c):
|
|
99
|
+
*
|
|
100
|
+
* idx = CArray.sort_addr(a, b, c)
|
|
101
|
+
* a[idx]; b[idx]; c[idx]
|
|
102
|
+
*
|
|
103
|
+
* All arguments must have the same element count. Masked cells are
|
|
104
|
+
* an incomparable sentinel clustered at masked_position: (:last
|
|
105
|
+
* default, or :first); ties are broken by original index (stable). */
|
|
106
|
+
static VALUE
|
|
107
|
+
rb_ca_s_sort_addr (int argc, VALUE *argv, VALUE self)
|
|
108
|
+
{
|
|
109
|
+
volatile VALUE out;
|
|
110
|
+
CArray *co;
|
|
111
|
+
struct cmp_base *base;
|
|
112
|
+
struct sort_addr_key *data;
|
|
113
|
+
ca_size_t elements;
|
|
114
|
+
ca_size_t *q;
|
|
115
|
+
int j;
|
|
116
|
+
ca_size_t i;
|
|
117
|
+
|
|
118
|
+
VALUE ropt = rb_pop_options(&argc, &argv);
|
|
119
|
+
VALUE vmasked_position = Qnil;
|
|
120
|
+
rb_scan_options(ropt, "masked_position", &vmasked_position);
|
|
121
|
+
int masked_last = 1;
|
|
122
|
+
if ( !NIL_P(vmasked_position) ) {
|
|
123
|
+
static ID sym_first = 0, sym_last = 0;
|
|
124
|
+
if ( !sym_first ) sym_first = rb_intern("first");
|
|
125
|
+
if ( !sym_last ) sym_last = rb_intern("last");
|
|
126
|
+
ID mp_id = SYM2ID(vmasked_position);
|
|
127
|
+
if ( mp_id == sym_last ) masked_last = 1;
|
|
128
|
+
else if ( mp_id == sym_first ) masked_last = 0;
|
|
129
|
+
else {
|
|
130
|
+
rb_raise(rb_eArgError,
|
|
131
|
+
"sort_addr: unknown masked_position %s (expected :first or :last)",
|
|
132
|
+
rb_id2name(mp_id));
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
if ( argc <= 0 ) {
|
|
137
|
+
rb_raise(rb_eArgError, "no arg given");
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
rb_check_carray_object(argv[0]);
|
|
141
|
+
elements = NUM2SIZE(rb_ca_elements(argv[0]));
|
|
142
|
+
|
|
143
|
+
for (j=0; j<argc; j++) {
|
|
144
|
+
CArray *ca_j;
|
|
145
|
+
rb_check_carray_object(argv[j]);
|
|
146
|
+
if ( elements != NUM2SIZE(rb_ca_elements(argv[j])) ) {
|
|
147
|
+
rb_raise(rb_eArgError, "elements mismatch");
|
|
148
|
+
}
|
|
149
|
+
/* Face gate: descend a Face to its storage and build attach / comparator
|
|
150
|
+
/ template output on that plain storage (this also removes the SEGV
|
|
151
|
+
from a face-lifted NULL-ptr index output). A fixlen storage sorts by
|
|
152
|
+
memcmp (the default order for fixlen, as for a plain fixlen array); a
|
|
153
|
+
numeric storage requires ORDERABLE so the numeric order equals the
|
|
154
|
+
surface order, else it raises. */
|
|
155
|
+
TypedData_Get_Struct(argv[j], CArray, &carray_data_type, ca_j);
|
|
156
|
+
if ( ca_is_face(ca_j) ) {
|
|
157
|
+
int orderable = ca_test_flag(ca_j, CA_FLAG_FACE_ORDERABLE_STORAGE);
|
|
158
|
+
VALUE stripped = rb_ca_strip_face_value(argv[j]);
|
|
159
|
+
CArray *sc;
|
|
160
|
+
TypedData_Get_Struct(stripped, CArray, &carray_data_type, sc);
|
|
161
|
+
if ( sc->data_type != CA_FIXLEN && ! orderable ) {
|
|
162
|
+
rb_raise(rb_eArgError,
|
|
163
|
+
"sort_addr: Face-typed input (%s) is not orderable by "
|
|
164
|
+
"storage; use ca.parent to descend to storage",
|
|
165
|
+
rb_obj_classname(argv[j]));
|
|
166
|
+
}
|
|
167
|
+
argv[j] = stripped;
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
base = xmalloc(sizeof(struct cmp_base));
|
|
172
|
+
base->n = argc;
|
|
173
|
+
base->masked_last = masked_last;
|
|
174
|
+
base->ca = xmalloc(sizeof(CArray *)*base->n);
|
|
175
|
+
|
|
176
|
+
for (j=0; j<argc; j++) {
|
|
177
|
+
CArray *ca;
|
|
178
|
+
TypedData_Get_Struct(argv[j], CArray, &carray_data_type, ca);
|
|
179
|
+
base->ca[j] = ca;
|
|
180
|
+
ca_attach(ca);
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
data = xmalloc(sizeof(struct sort_addr_key)*elements);
|
|
184
|
+
for (i=0; i<elements; i++) {
|
|
185
|
+
data[i].i = i;
|
|
186
|
+
data[i].base = base;
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
#ifdef HAVE_MERGESORT
|
|
190
|
+
mergesort(data, elements, sizeof(struct sort_addr_key),
|
|
191
|
+
(int (*)(const void*,const void*)) sort_addr_cmp);
|
|
192
|
+
#else
|
|
193
|
+
qsort(data, elements, sizeof(struct sort_addr_key),
|
|
194
|
+
(int (*)(const void*,const void*)) sort_addr_cmp);
|
|
195
|
+
#endif
|
|
196
|
+
|
|
197
|
+
out = rb_ca_template_with_type(argv[0], INT2NUM(CA_SIZE), INT2NUM(0));
|
|
198
|
+
TypedData_Get_Struct(out, CArray, &carray_data_type, co);
|
|
199
|
+
q = (ca_size_t *) co->ptr;
|
|
200
|
+
|
|
201
|
+
for (i=0; i<elements; i++) {
|
|
202
|
+
*q = data[i].i;
|
|
203
|
+
q++;
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
for (j=0; j<argc; j++) {
|
|
207
|
+
ca_detach(base->ca[j]);
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
xfree(data);
|
|
211
|
+
xfree(base->ca);
|
|
212
|
+
xfree(base);
|
|
213
|
+
|
|
214
|
+
return out;
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
/* Internal sort_addr_ki kernel entries (C-level only, no Ruby binding;
|
|
218
|
+
* declared in the generated carray_kernels.c). The _quick and _stable
|
|
219
|
+
* variants implement the kind: dispatch; _ki itself is a 2-arg alias of
|
|
220
|
+
* _quick retained for older callers. The _mp ("masked position") twins
|
|
221
|
+
* take an explicit masked_last so `sort` / `sort_copy` can pass the
|
|
222
|
+
* masked_position: kwarg through (see MASKED_POSITION rev1 in
|
|
223
|
+
* mkkernel.rb's MkKernel.sort doc). */
|
|
224
|
+
extern VALUE rb_ca_sort_addr_ki (VALUE self, VALUE vaxis);
|
|
225
|
+
extern VALUE rb_ca_sort_addr_ki_quick (VALUE self, VALUE vaxis);
|
|
226
|
+
extern VALUE rb_ca_sort_addr_ki_stable (VALUE self, VALUE vaxis);
|
|
227
|
+
extern VALUE rb_ca_sort_addr_ki_quick_mp (VALUE self, VALUE vaxis, int masked_last);
|
|
228
|
+
extern VALUE rb_ca_sort_addr_ki_stable_mp (VALUE self, VALUE vaxis, int masked_last);
|
|
229
|
+
extern VALUE rb_ca_remap_new (VALUE cary, VALUE rmapper); /* ca_obj_remap.c (sort view) */
|
|
230
|
+
|
|
231
|
+
/* sort_addr(axis: nil, kind: :quick, masked_position: :last) — returns
|
|
232
|
+
* view-flat addresses that index a sort. Two modes:
|
|
233
|
+
*
|
|
234
|
+
* axis: nil (no kwarg) — flat lex form, equivalent to
|
|
235
|
+
* CArray.sort_addr(self, masked_position:).
|
|
236
|
+
* Shape is preserved (NOT flattened -- this is
|
|
237
|
+
* the legacy "1 key" case of the class method's
|
|
238
|
+
* multi-key lex sort, pinned by
|
|
239
|
+
* test_sort_addr_no_arg_2d_preserves_shape).
|
|
240
|
+
* kind: has no effect (the flat lex path uses
|
|
241
|
+
* qsort/mergesort with its own comparator).
|
|
242
|
+
* axis: k — per-fiber view-flat addresses along axis k.
|
|
243
|
+
* Output shape == self.shape. Dispatches to
|
|
244
|
+
* sort_addr_ki (mkkernel `:sort` kind).
|
|
245
|
+
*
|
|
246
|
+
* kind: selects the sort algorithm for the axis: path:
|
|
247
|
+
* :quick (default) — introsort with mergesort escape
|
|
248
|
+
* :stable — bottom-up mergesort
|
|
249
|
+
*
|
|
250
|
+
* Both kinds are algorithmically stable (pair sort with index tie-
|
|
251
|
+
* break); kind: chooses the performance characteristic only.
|
|
252
|
+
*
|
|
253
|
+
* masked_position: (:last default, or :first) picks which end masked
|
|
254
|
+
* cells cluster to. Effective on BOTH modes: the axis: path forwards
|
|
255
|
+
* to sort_addr_ki's masked_last-aware _mp entries; the no-axis path
|
|
256
|
+
* forwards to CArray.sort_addr's own masked_last-aware comparator
|
|
257
|
+
* (sort_addr_cmp). See {sort}'s doc for the underlying incomparable-
|
|
258
|
+
* sentinel contract.
|
|
259
|
+
*/
|
|
260
|
+
|
|
261
|
+
/* C-callable entry: skip rb_scan_args (which depends on call-frame
|
|
262
|
+
* keyword-splat state, only set by full Ruby method dispatch).
|
|
263
|
+
* axis = Qnil -> flat (= CArray.sort_addr(self, masked_position:))
|
|
264
|
+
* axis = Integer -> per-fiber axis path
|
|
265
|
+
* stable: 0 (quick, default) / non-zero (stable)
|
|
266
|
+
* masked_last: 1 (default, :last) / 0 (:first)
|
|
267
|
+
*
|
|
268
|
+
* Called by the Ruby binding rb_ca_sort_addr below, and externally
|
|
269
|
+
* by rb_ca_sort_by_key in carray_order.c (always with axis given, so
|
|
270
|
+
* the flat branch below is unreached from that caller). */
|
|
271
|
+
VALUE
|
|
272
|
+
rb_ca_sort_addr_c (VALUE self, VALUE axis, int stable, int masked_last)
|
|
273
|
+
{
|
|
274
|
+
if ( NIL_P(axis) ) {
|
|
275
|
+
/* Legacy flat: equivalent to CArray.sort_addr(self, masked_position:).
|
|
276
|
+
kind: ignored for now (the flat path uses qsort/mergesort
|
|
277
|
+
internally). rb_ca_s_sort_addr's rb_pop_options handles a trailing
|
|
278
|
+
Hash the same way regardless of call-frame keyword-splat state (it
|
|
279
|
+
type-checks the last positional arg, not rb_keyword_given_p), so
|
|
280
|
+
this raw C call is safe unlike the rb_scan_args "1:" pattern used
|
|
281
|
+
elsewhere in this file. */
|
|
282
|
+
VALUE kw = rb_hash_new();
|
|
283
|
+
rb_hash_aset(kw, ID2SYM(rb_intern("masked_position")),
|
|
284
|
+
masked_last ? ID2SYM(rb_intern("last")) : ID2SYM(rb_intern("first")));
|
|
285
|
+
VALUE flat_argv[2] = { self, kw };
|
|
286
|
+
return rb_ca_s_sort_addr(2, flat_argv, rb_cCArray);
|
|
287
|
+
}
|
|
288
|
+
return stable ? rb_ca_sort_addr_ki_stable_mp(self, axis, masked_last)
|
|
289
|
+
: rb_ca_sort_addr_ki_quick_mp (self, axis, masked_last);
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
/* Ruby binding entry: parses (axis: / kind: / masked_position:) kwargs,
|
|
293
|
+
then forwards. */
|
|
294
|
+
static VALUE
|
|
295
|
+
rb_ca_sort_addr (int argc, VALUE *argv, VALUE self)
|
|
296
|
+
{
|
|
297
|
+
VALUE opts = Qnil;
|
|
298
|
+
VALUE axis = Qnil;
|
|
299
|
+
VALUE kind = Qnil;
|
|
300
|
+
VALUE vmasked_position = Qnil;
|
|
301
|
+
|
|
302
|
+
rb_scan_args(argc, argv, "0:", &opts);
|
|
303
|
+
rb_scan_options(opts, "axis,kind,masked_position", &axis, &kind, &vmasked_position);
|
|
304
|
+
|
|
305
|
+
int do_stable = 0;
|
|
306
|
+
if ( ! NIL_P(kind) ) {
|
|
307
|
+
static ID sym_quick = 0, sym_stable = 0;
|
|
308
|
+
if ( ! sym_quick ) sym_quick = rb_intern("quick");
|
|
309
|
+
if ( ! sym_stable ) sym_stable = rb_intern("stable");
|
|
310
|
+
ID kind_id = SYM2ID(kind);
|
|
311
|
+
if ( kind_id == sym_quick ) do_stable = 0;
|
|
312
|
+
else if ( kind_id == sym_stable ) do_stable = 1;
|
|
313
|
+
else {
|
|
314
|
+
rb_raise(rb_eArgError,
|
|
315
|
+
"sort_addr: unknown kind %s (expected :quick or :stable)",
|
|
316
|
+
rb_id2name(kind_id));
|
|
317
|
+
}
|
|
318
|
+
}
|
|
319
|
+
|
|
320
|
+
int masked_last = 1;
|
|
321
|
+
if ( ! NIL_P(vmasked_position) ) {
|
|
322
|
+
static ID sym_first = 0, sym_last = 0;
|
|
323
|
+
if ( !sym_first ) sym_first = rb_intern("first");
|
|
324
|
+
if ( !sym_last ) sym_last = rb_intern("last");
|
|
325
|
+
ID mp_id = SYM2ID(vmasked_position);
|
|
326
|
+
if ( mp_id == sym_last ) masked_last = 1;
|
|
327
|
+
else if ( mp_id == sym_first ) masked_last = 0;
|
|
328
|
+
else {
|
|
329
|
+
rb_raise(rb_eArgError,
|
|
330
|
+
"sort_addr: unknown masked_position %s (expected :first or :last)",
|
|
331
|
+
rb_id2name(mp_id));
|
|
332
|
+
}
|
|
333
|
+
}
|
|
334
|
+
|
|
335
|
+
return rb_ca_sort_addr_c(self, axis, do_stable, masked_last);
|
|
336
|
+
}
|
|
337
|
+
|
|
338
|
+
/* axis2addr(indices, axis: 0) — converts per-fiber axis-local indices
|
|
339
|
+
* into row-major view-flat addresses into self. For each cell at coord
|
|
340
|
+
* c = (c_0, ..., c_(n-1)) in `indices`:
|
|
341
|
+
*
|
|
342
|
+
* addr[c] = sum_{j != axis} c_j * stride_j + indices[c] * stride_axis
|
|
343
|
+
*
|
|
344
|
+
* where strides are row-major over self.shape
|
|
345
|
+
* (stride_j = product of self.dim[j+1..n-1]).
|
|
346
|
+
*
|
|
347
|
+
* This is the canonical converter between the two axis-position
|
|
348
|
+
* representations the *_index / *_addr kernel families produce:
|
|
349
|
+
*
|
|
350
|
+
* a.min_index(axis: k) — axis-local scalar per fiber
|
|
351
|
+
* a.min_addr(axis: k) — view-flat address per fiber
|
|
352
|
+
* flat_addrs = key.axis2addr(key.min_index(axis: k), axis: k)
|
|
353
|
+
* # == key.min_addr(axis: k)
|
|
354
|
+
*
|
|
355
|
+
* Sits underneath `take_along_axis`: the heavy "axis-local -> view-
|
|
356
|
+
* flat" arithmetic lives here, and `take_along_axis` is a one-liner
|
|
357
|
+
* on top of `flatten[axis2addr(...)]`.
|
|
358
|
+
*
|
|
359
|
+
* Shape rule: indices.ndim == self.ndim, indices.dim[j] == self.dim[j]
|
|
360
|
+
* for all j != axis; indices.dim[axis] is free (the output along axis
|
|
361
|
+
* can be any length).
|
|
362
|
+
*
|
|
363
|
+
* indices data_type: any integer kind (zero-copy if already CA_SIZE).
|
|
364
|
+
* Negative indices: Python-style (-1 = last), normalized internally.
|
|
365
|
+
* OOB indices: raises RangeError. Default axis: 0; negative axis
|
|
366
|
+
* Python-style.
|
|
367
|
+
*
|
|
368
|
+
* Returns: CArray of CA_SIZE, same shape as indices.
|
|
369
|
+
*/
|
|
370
|
+
|
|
371
|
+
/* C-callable entry: skip rb_scan_args (call-frame state dependency).
|
|
372
|
+
* vaxis = Qnil treated as axis 0; Integer is taken as-is (negative
|
|
373
|
+
* axis normalized internally).
|
|
374
|
+
*
|
|
375
|
+
* Called by the Ruby binding rb_ca_axis2addr below, and externally
|
|
376
|
+
* by rb_ca_take_along_axis_c / rb_ca_put_along_axis in
|
|
377
|
+
* carray_order.c. */
|
|
378
|
+
VALUE
|
|
379
|
+
rb_ca_axis2addr_c (VALUE self, VALUE vindices, VALUE vaxis)
|
|
380
|
+
{
|
|
381
|
+
rb_check_carray_object(vindices);
|
|
382
|
+
|
|
383
|
+
CArray *ca, *idx_ca, *out_ca;
|
|
384
|
+
TypedData_Get_Struct(self, CArray, &carray_data_type, ca);
|
|
385
|
+
TypedData_Get_Struct(vindices, CArray, &carray_data_type, idx_ca);
|
|
386
|
+
|
|
387
|
+
/* axis normalization + range check. */
|
|
388
|
+
int axis_raw = NIL_P(vaxis) ? 0 : NUM2INT(vaxis);
|
|
389
|
+
int axis = (axis_raw < 0) ? ((int) ca->ndim + axis_raw) : axis_raw;
|
|
390
|
+
if ( axis < 0 || axis >= ca->ndim ) {
|
|
391
|
+
rb_raise(rb_eIndexError,
|
|
392
|
+
"axis2addr: axis %d out of range for ndim %d",
|
|
393
|
+
axis_raw, (int) ca->ndim);
|
|
394
|
+
}
|
|
395
|
+
|
|
396
|
+
/* Shape rule: indices.ndim == self.ndim, dims match except at axis. */
|
|
397
|
+
if ( idx_ca->ndim != ca->ndim ) {
|
|
398
|
+
rb_raise(rb_eArgError,
|
|
399
|
+
"axis2addr: indices.ndim (%d) must equal self.ndim (%d)",
|
|
400
|
+
(int) idx_ca->ndim, (int) ca->ndim);
|
|
401
|
+
}
|
|
402
|
+
for ( int8_t j = 0; j < ca->ndim; j++ ) {
|
|
403
|
+
if ( j == axis ) continue;
|
|
404
|
+
if ( idx_ca->dim[j] != ca->dim[j] ) {
|
|
405
|
+
rb_raise(rb_eArgError,
|
|
406
|
+
"axis2addr: indices.dim[%d] (%lld) must equal "
|
|
407
|
+
"self.dim[%d] (%lld)",
|
|
408
|
+
(int) j, (long long) idx_ca->dim[j],
|
|
409
|
+
(int) j, (long long) ca->dim[j]);
|
|
410
|
+
}
|
|
411
|
+
}
|
|
412
|
+
|
|
413
|
+
/* indices data_type: integer kind accepted; cast to CA_SIZE if needed
|
|
414
|
+
(zero-copy when already CA_SIZE via to_type identity). */
|
|
415
|
+
if ( ! ca_is_integer_type(idx_ca) ) {
|
|
416
|
+
rb_raise(rb_eArgError,
|
|
417
|
+
"axis2addr: indices data_type must be integer kind (got %d)",
|
|
418
|
+
(int) idx_ca->data_type);
|
|
419
|
+
}
|
|
420
|
+
volatile VALUE vidx_cast = vindices;
|
|
421
|
+
CArray *idx_cast = idx_ca;
|
|
422
|
+
if ( idx_ca->data_type != CA_SIZE ) {
|
|
423
|
+
vidx_cast = rb_funcall(vindices, rb_intern("to_type"), 1,
|
|
424
|
+
INT2NUM(CA_SIZE));
|
|
425
|
+
TypedData_Get_Struct(vidx_cast, CArray, &carray_data_type, idx_cast);
|
|
426
|
+
}
|
|
427
|
+
|
|
428
|
+
/* Row-major view-flat strides for self (in cells). */
|
|
429
|
+
ca_size_t strides[CA_RANK_MAX];
|
|
430
|
+
ca_size_t s = 1;
|
|
431
|
+
for ( int8_t j = (int8_t)(ca->ndim - 1); j >= 0; j-- ) {
|
|
432
|
+
strides[j] = s;
|
|
433
|
+
s *= ca->dim[j];
|
|
434
|
+
}
|
|
435
|
+
ca_size_t axis_size = ca->dim[axis];
|
|
436
|
+
|
|
437
|
+
/* Allocate output: same shape as indices, data_type CA_SIZE. */
|
|
438
|
+
volatile VALUE vout =
|
|
439
|
+
rb_ca_template_with_type(vidx_cast, INT2NUM(CA_SIZE), INT2NUM(0));
|
|
440
|
+
TypedData_Get_Struct(vout, CArray, &carray_data_type, out_ca);
|
|
441
|
+
|
|
442
|
+
/* out_ca is a freshly allocated entity (ptr already valid), so it needs
|
|
443
|
+
no attach/sync/detach. idx_cast may still be a view (CA_SIZE identity
|
|
444
|
+
branch above), so its contig buffer is delivered via CA_WITH_BUFFER,
|
|
445
|
+
which aliases when contig / materialises a view into scratch and scopes
|
|
446
|
+
the attach/detach to the block. */
|
|
447
|
+
ca_size_t *out_ptr = (ca_size_t *) out_ca->ptr;
|
|
448
|
+
|
|
449
|
+
ca_size_t coord[CA_RANK_MAX];
|
|
450
|
+
int ndim = idx_cast->ndim;
|
|
451
|
+
|
|
452
|
+
/* OOB is reported after the block: rb_raise from inside CA_WITH_BUFFER
|
|
453
|
+
would longjmp past the scoped ca_detach and leak the attach (= doc
|
|
454
|
+
constraint "restructure to break"). Record the offender, break, raise
|
|
455
|
+
once the buffer lifecycle has closed. */
|
|
456
|
+
ca_size_t bad_k = -1;
|
|
457
|
+
ca_size_t bad_raw = 0;
|
|
458
|
+
|
|
459
|
+
ca_size_t *idx_ptr;
|
|
460
|
+
ca_size_t n;
|
|
461
|
+
CA_WITH_BUFFER(idx_cast, ca_size_t, idx_ptr, n) {
|
|
462
|
+
for ( int8_t j = 0; j < ndim; j++ ) coord[j] = 0;
|
|
463
|
+
for ( ca_size_t k = 0; k < n; k++ ) {
|
|
464
|
+
/* Negative normalize + OOB check (raises on out-of-bounds). */
|
|
465
|
+
ca_size_t raw = idx_ptr[k];
|
|
466
|
+
ca_size_t norm = (raw < 0) ? (raw + axis_size) : raw;
|
|
467
|
+
if ( norm < 0 || norm >= axis_size ) {
|
|
468
|
+
bad_k = k; bad_raw = raw;
|
|
469
|
+
break;
|
|
470
|
+
}
|
|
471
|
+
/* Compute flat addr: sum c_j * stride_j (with c_axis = norm). */
|
|
472
|
+
ca_size_t addr = 0;
|
|
473
|
+
for ( int8_t j = 0; j < ndim; j++ ) {
|
|
474
|
+
if ( j == axis ) {
|
|
475
|
+
addr += norm * strides[j];
|
|
476
|
+
} else {
|
|
477
|
+
addr += coord[j] * strides[j];
|
|
478
|
+
}
|
|
479
|
+
}
|
|
480
|
+
out_ptr[k] = addr;
|
|
481
|
+
/* Advance coord row-major (last axis ticks fastest). */
|
|
482
|
+
for ( int8_t j = (int8_t)(ndim - 1); j >= 0; j-- ) {
|
|
483
|
+
if ( ++coord[j] < idx_cast->dim[j] ) break;
|
|
484
|
+
coord[j] = 0;
|
|
485
|
+
}
|
|
486
|
+
}
|
|
487
|
+
}
|
|
488
|
+
|
|
489
|
+
if ( bad_k >= 0 ) {
|
|
490
|
+
rb_raise(rb_eRangeError,
|
|
491
|
+
"axis2addr: indices[%lld] = %lld out of range [0, %lld) "
|
|
492
|
+
"(after negative normalize)",
|
|
493
|
+
(long long) bad_k, (long long) bad_raw, (long long) axis_size);
|
|
494
|
+
}
|
|
495
|
+
|
|
496
|
+
return vout;
|
|
497
|
+
}
|
|
498
|
+
|
|
499
|
+
/* Ruby binding entry: parses (indices, axis:) -> forwards to _c. */
|
|
500
|
+
static VALUE
|
|
501
|
+
rb_ca_axis2addr (int argc, VALUE *argv, VALUE self)
|
|
502
|
+
{
|
|
503
|
+
VALUE vindices;
|
|
504
|
+
VALUE opts = Qnil;
|
|
505
|
+
VALUE vaxis = Qnil;
|
|
506
|
+
|
|
507
|
+
rb_scan_args(argc, argv, "1:", &vindices, &opts);
|
|
508
|
+
rb_scan_options(opts, "axis", &vaxis);
|
|
509
|
+
return rb_ca_axis2addr_c(self, vindices, vaxis);
|
|
510
|
+
}
|
|
511
|
+
|
|
512
|
+
|
|
513
|
+
/* ===== sort / sort_copy (value & view surface, sibling of the
|
|
514
|
+
sort_addr / axis2addr block above). =============== */
|
|
515
|
+
|
|
516
|
+
/* sort(axis: nil, kind: :quick, masked_position: :last) — returns a
|
|
517
|
+
* CARemap view of self whose elements are sorted along the given axis.
|
|
518
|
+
* When axis: is omitted, self is first flattened to 1-D and the entire
|
|
519
|
+
* array is sorted (so the result is a 1-D view regardless of self.ndim).
|
|
520
|
+
*
|
|
521
|
+
* kind: selects the sort algorithm. Both kinds are algorithmically
|
|
522
|
+
* stable (pair sort with fiber-local index tie-break), so the order
|
|
523
|
+
* is identical for equal values; the choice is a performance
|
|
524
|
+
* characteristic:
|
|
525
|
+
*
|
|
526
|
+
* :quick (default) — portable textbook introsort with mergesort
|
|
527
|
+
* escape. Faster on random data.
|
|
528
|
+
* :stable — portable textbook bottom-up mergesort with
|
|
529
|
+
* insertion pre-pass and sorted-skip merge.
|
|
530
|
+
* Slightly slower on random data but predictable
|
|
531
|
+
* worst case.
|
|
532
|
+
*
|
|
533
|
+
* Mask handling: masked cells are an incomparable sentinel, the same
|
|
534
|
+
* role NaN plays for float dtypes. They are excluded from the value
|
|
535
|
+
* comparison and clustered at one end of each fiber; masked_position:
|
|
536
|
+
* picks which end (:last, default, or :first). Relative order within
|
|
537
|
+
* the masked cluster is unspecified (same contract as the < / > regions
|
|
538
|
+
* of partition). Since `sort` gathers through CARemap, each masked
|
|
539
|
+
* cell's mask bit shows through at its new (clustered) position -- no
|
|
540
|
+
* separate output mask handling is needed here.
|
|
541
|
+
*
|
|
542
|
+
* Dispatch:
|
|
543
|
+
* CA_FIXLEN flows through sort_addr_ki's fixlen dialect (memcmp
|
|
544
|
+
* lexicographic order) + ca_remap_new, same view path as
|
|
545
|
+
* numeric. kind: has no effect.
|
|
546
|
+
* CA_OBJECT flows through sort_addr_ki's object dialect (Ruby `<=>`
|
|
547
|
+
* via rb_funcall) + ca_remap_new, same view path as
|
|
548
|
+
* numeric, both no-axis and axis: forms.
|
|
549
|
+
*/
|
|
550
|
+
static VALUE
|
|
551
|
+
rb_ca_sorted_view (int argc, VALUE *argv, VALUE self)
|
|
552
|
+
{
|
|
553
|
+
VALUE rkw = Qnil;
|
|
554
|
+
VALUE vaxis = Qnil;
|
|
555
|
+
VALUE vkind = Qnil;
|
|
556
|
+
VALUE vmasked_position = Qnil;
|
|
557
|
+
|
|
558
|
+
/* Parse kwargs: `sort` accepts `axis:`, `kind:`, `masked_position:`. */
|
|
559
|
+
rb_scan_args(argc, argv, "0:", &rkw);
|
|
560
|
+
rb_scan_options(rkw, "axis,kind,masked_position", &vaxis, &vkind, &vmasked_position);
|
|
561
|
+
|
|
562
|
+
/* Resolve kind: -> do_stable. :quick (default) = introsort; :stable
|
|
563
|
+
= bottom-up mergesort. Both share the same pair layout and produce
|
|
564
|
+
identical orderings; only the algorithm differs. */
|
|
565
|
+
int do_stable = 0;
|
|
566
|
+
if ( !NIL_P(vkind) ) {
|
|
567
|
+
static ID sym_quick = 0, sym_stable = 0;
|
|
568
|
+
if ( !sym_quick ) sym_quick = rb_intern("quick");
|
|
569
|
+
if ( !sym_stable ) sym_stable = rb_intern("stable");
|
|
570
|
+
ID kind_id = SYM2ID(vkind);
|
|
571
|
+
if ( kind_id == sym_quick ) do_stable = 0;
|
|
572
|
+
else if ( kind_id == sym_stable ) do_stable = 1;
|
|
573
|
+
else {
|
|
574
|
+
rb_raise(rb_eArgError,
|
|
575
|
+
"sort: unknown kind %s (expected :quick or :stable)",
|
|
576
|
+
rb_id2name(kind_id));
|
|
577
|
+
}
|
|
578
|
+
}
|
|
579
|
+
|
|
580
|
+
/* Resolve masked_position: -> masked_last. :last (default) or :first. */
|
|
581
|
+
int masked_last = 1;
|
|
582
|
+
if ( !NIL_P(vmasked_position) ) {
|
|
583
|
+
static ID sym_first = 0, sym_last = 0;
|
|
584
|
+
if ( !sym_first ) sym_first = rb_intern("first");
|
|
585
|
+
if ( !sym_last ) sym_last = rb_intern("last");
|
|
586
|
+
ID mp_id = SYM2ID(vmasked_position);
|
|
587
|
+
if ( mp_id == sym_last ) masked_last = 1;
|
|
588
|
+
else if ( mp_id == sym_first ) masked_last = 0;
|
|
589
|
+
else {
|
|
590
|
+
rb_raise(rb_eArgError,
|
|
591
|
+
"sort: unknown masked_position %s (expected :first or :last)",
|
|
592
|
+
rb_id2name(mp_id));
|
|
593
|
+
}
|
|
594
|
+
}
|
|
595
|
+
|
|
596
|
+
CArray *ca;
|
|
597
|
+
TypedData_Get_Struct(self, CArray, &carray_data_type, ca);
|
|
598
|
+
|
|
599
|
+
/* Build target view (flatten for no-arg, identity for axis: kwarg)
|
|
600
|
+
before mask handling: rb_ca_flatten re-propagates the mask field
|
|
601
|
+
from the parent, so any mask stripping must happen on the post-
|
|
602
|
+
flatten target. */
|
|
603
|
+
VALUE target;
|
|
604
|
+
VALUE vaxis_use;
|
|
605
|
+
if ( NIL_P(vaxis) ) {
|
|
606
|
+
target = rb_ca_flatten(self);
|
|
607
|
+
vaxis_use = INT2NUM(0);
|
|
608
|
+
} else {
|
|
609
|
+
target = self;
|
|
610
|
+
vaxis_use = vaxis;
|
|
611
|
+
}
|
|
612
|
+
|
|
613
|
+
/* sort_addr_ki_{quick,stable}_mp returns a CA_SIZE same-shape array of
|
|
614
|
+
view-flat addresses into target, with masked cells clustered at
|
|
615
|
+
masked_position: (mask field present but nothing actually masked is
|
|
616
|
+
handled gracefully too -- the split degenerates to a no-op). Feed
|
|
617
|
+
directly to ca_remap_new to produce the sorted view; the remap gather
|
|
618
|
+
carries target's mask bits through, so masked cells land at their
|
|
619
|
+
clustered position still marked masked -- no extra output-mask step
|
|
620
|
+
needed. */
|
|
621
|
+
volatile VALUE sigma_addr = do_stable
|
|
622
|
+
? rb_ca_sort_addr_ki_stable_mp(target, vaxis_use, masked_last)
|
|
623
|
+
: rb_ca_sort_addr_ki_quick_mp (target, vaxis_use, masked_last);
|
|
624
|
+
{
|
|
625
|
+
VALUE obj = rb_ca_remap_new(target, sigma_addr);
|
|
626
|
+
CA_FACE_LIFT_IF_FACE(obj, self, ca);
|
|
627
|
+
return obj;
|
|
628
|
+
}
|
|
629
|
+
}
|
|
630
|
+
|
|
631
|
+
/* sort_copy(axis: nil, kind: :quick, masked_position: :last) — eager
|
|
632
|
+
* counterpart to {sort}. Returns a fresh entity CArray of the same
|
|
633
|
+
* shape and data_type as self, with elements sorted along the given
|
|
634
|
+
* axis (no-arg flattens to 1-D, same convention as sort).
|
|
635
|
+
*
|
|
636
|
+
* Implementation: per-fiber gather + sort + scatter via
|
|
637
|
+
* CA_FOR_EACH_FIBER_INOUT. Bypasses the sort_addr_ki + ca_remap_new
|
|
638
|
+
* view chain that sort uses: a single gather + sort + scatter per
|
|
639
|
+
* fiber, no pair struct, no view layer. This fast path is numeric-only
|
|
640
|
+
* and mask-free (CA_KERNEL_NO_MASK below).
|
|
641
|
+
*
|
|
642
|
+
* CA_FIXLEN and masked input both delegate to {sort} + copy instead of
|
|
643
|
+
* duplicating the fixlen dialect / mask-position split in this per-
|
|
644
|
+
* fiber loop: masked_position: is forwarded unchanged. Masked cells
|
|
645
|
+
* keep their masked-ness (the view's remap gather carries the mask bit
|
|
646
|
+
* through, and .copy materializes it), clustered at masked_position:
|
|
647
|
+
* within each fiber -- same contract as {sort}.
|
|
648
|
+
*/
|
|
649
|
+
static VALUE
|
|
650
|
+
rb_ca_sort_copy (int argc, VALUE *argv, VALUE self)
|
|
651
|
+
{
|
|
652
|
+
VALUE rkw = Qnil;
|
|
653
|
+
VALUE vaxis = Qnil;
|
|
654
|
+
VALUE vkind = Qnil;
|
|
655
|
+
VALUE vmasked_position = Qnil;
|
|
656
|
+
|
|
657
|
+
rb_scan_args(argc, argv, "0:", &rkw);
|
|
658
|
+
rb_scan_options(rkw, "axis,kind,masked_position", &vaxis, &vkind, &vmasked_position);
|
|
659
|
+
|
|
660
|
+
/* Resolve kind:: :quick (default) = portable textbook quicksort,
|
|
661
|
+
:stable = portable textbook bottom-up mergesort. */
|
|
662
|
+
int do_stable = 0;
|
|
663
|
+
if ( !NIL_P(vkind) ) {
|
|
664
|
+
static ID sym_quick = 0, sym_stable = 0;
|
|
665
|
+
if ( !sym_quick ) sym_quick = rb_intern("quick");
|
|
666
|
+
if ( !sym_stable ) sym_stable = rb_intern("stable");
|
|
667
|
+
ID kind_id = SYM2ID(vkind);
|
|
668
|
+
if ( kind_id == sym_quick ) do_stable = 0;
|
|
669
|
+
else if ( kind_id == sym_stable ) do_stable = 1;
|
|
670
|
+
else {
|
|
671
|
+
rb_raise(rb_eArgError,
|
|
672
|
+
"sort_copy: unknown kind %s (expected :quick or :stable)",
|
|
673
|
+
rb_id2name(kind_id));
|
|
674
|
+
}
|
|
675
|
+
}
|
|
676
|
+
|
|
677
|
+
CArray *ca;
|
|
678
|
+
TypedData_Get_Struct(self, CArray, &carray_data_type, ca);
|
|
679
|
+
|
|
680
|
+
/* CA_FIXLEN and masked input: the per-fiber path below covers
|
|
681
|
+
unmasked numeric dtypes only. Delegate to {sort} (which handles
|
|
682
|
+
both the fixlen dialect and the masked_position split) + copy to
|
|
683
|
+
get the same shape/class contract as the fast path. */
|
|
684
|
+
if ( ca_is_fixlen_type(ca) || ca_has_mask(ca) ) {
|
|
685
|
+
VALUE sv_kw = rb_hash_new();
|
|
686
|
+
if ( !NIL_P(vaxis) ) rb_hash_aset(sv_kw, ID2SYM(rb_intern("axis")), vaxis);
|
|
687
|
+
if ( !NIL_P(vkind) ) rb_hash_aset(sv_kw, ID2SYM(rb_intern("kind")), vkind);
|
|
688
|
+
if ( !NIL_P(vmasked_position) ) rb_hash_aset(sv_kw, ID2SYM(rb_intern("masked_position")), vmasked_position);
|
|
689
|
+
VALUE sv_argv[1] = { sv_kw };
|
|
690
|
+
int sv_argc = ( NIL_P(vaxis) && NIL_P(vkind) && NIL_P(vmasked_position) ) ? 0 : 1;
|
|
691
|
+
VALUE obj = rb_ca_copy(rb_ca_sorted_view(sv_argc, sv_argv, self));
|
|
692
|
+
CA_FACE_LIFT_IF_FACE(obj, self, ca);
|
|
693
|
+
return obj;
|
|
694
|
+
}
|
|
695
|
+
|
|
696
|
+
/* Build target view (flatten for no-arg, identity for axis: kwarg).
|
|
697
|
+
Unmasked past this point (guarded above), so no mask handling is
|
|
698
|
+
needed here -- see the analogous block in rb_ca_sorted_view. */
|
|
699
|
+
VALUE target;
|
|
700
|
+
VALUE vaxis_use;
|
|
701
|
+
if ( NIL_P(vaxis) ) {
|
|
702
|
+
target = rb_ca_flatten(self);
|
|
703
|
+
vaxis_use = INT2NUM(0);
|
|
704
|
+
} else {
|
|
705
|
+
target = self;
|
|
706
|
+
vaxis_use = vaxis;
|
|
707
|
+
}
|
|
708
|
+
|
|
709
|
+
CArray *cat;
|
|
710
|
+
TypedData_Get_Struct(target, CArray, &carray_data_type, cat);
|
|
711
|
+
|
|
712
|
+
/* Normalize axis (negative -> +ndim, range check). */
|
|
713
|
+
int axis = NUM2INT(vaxis_use);
|
|
714
|
+
if ( axis < 0 ) axis += cat->ndim;
|
|
715
|
+
if ( axis < 0 || axis >= cat->ndim ) {
|
|
716
|
+
rb_raise(rb_eArgError, "sort_copy: axis %d out of range for ndim %d",
|
|
717
|
+
NUM2INT(vaxis_use), cat->ndim);
|
|
718
|
+
}
|
|
719
|
+
|
|
720
|
+
/* data_type check: ALL_NUMERIC only (CA_INT8..CA_FLOAT64).
|
|
721
|
+
Complex / object are rejected here; CA_OBJECT goes through the
|
|
722
|
+
axis: lift in rb_ca_sorted_view, and complex sort semantics
|
|
723
|
+
differ enough that we do not pick a default. */
|
|
724
|
+
if ( cat->data_type < CA_INT8 || cat->data_type > CA_FLOAT64 ) {
|
|
725
|
+
rb_raise(rb_eCADataTypeError,
|
|
726
|
+
"sort_copy: data_type %d not supported "
|
|
727
|
+
"(expected one of: i8, u8, i16, u16, i32, u32, i64, u64, f32, f64)",
|
|
728
|
+
cat->data_type);
|
|
729
|
+
}
|
|
730
|
+
|
|
731
|
+
/* Allocate output: same shape and data_type as target
|
|
732
|
+
(bytes=0 = preserve native bytes). */
|
|
733
|
+
volatile VALUE vout = rb_ca_template_with_type(target,
|
|
734
|
+
INT2NUM(cat->data_type),
|
|
735
|
+
INT2NUM(0));
|
|
736
|
+
CArray *cao;
|
|
737
|
+
TypedData_Get_Struct(vout, CArray, &carray_data_type, cao);
|
|
738
|
+
|
|
739
|
+
/* Per-fiber sort via the CA_FOR_EACH_FIBER_INOUT catalog macro.
|
|
740
|
+
The kernel_iterator engine guarantees pi/po contig delivery
|
|
741
|
+
(aliasing the parent when possible, materialising into per-fiber
|
|
742
|
+
or whole-view scratch otherwise), so the author body is stride-
|
|
743
|
+
free: copy contig input -> contig output, sort in place. */
|
|
744
|
+
ca_size_t fiber_n_for_check = (ca_size_t) cat->dim[axis];
|
|
745
|
+
ca_size_t bytes = (ca_size_t) cat->bytes;
|
|
746
|
+
ca_iter_state st_in, st_out;
|
|
747
|
+
char *pi, *po;
|
|
748
|
+
ca_size_t n;
|
|
749
|
+
|
|
750
|
+
/* Per-fiber aux buffer for the stable path: allocated once outside
|
|
751
|
+
the fiber loop and reused across fibers (all fibers share the
|
|
752
|
+
same axis length and data_type). */
|
|
753
|
+
void *aux = NULL;
|
|
754
|
+
if ( do_stable ) {
|
|
755
|
+
aux = xmalloc((size_t) cat->dim[axis] * (size_t) bytes);
|
|
756
|
+
}
|
|
757
|
+
CA_FOR_EACH_FIBER_INOUT(st_in, st_out, cat, cao, (int8_t) axis,
|
|
758
|
+
CA_KERNEL_NO_MASK, pi, po, n) {
|
|
759
|
+
(void) fiber_n_for_check;
|
|
760
|
+
memcpy(po, pi, (size_t) n * (size_t) bytes);
|
|
761
|
+
if ( do_stable ) {
|
|
762
|
+
switch ( cat->data_type ) {
|
|
763
|
+
case CA_INT8: ca_sort_merge_i8 ((int8_t *) po, (int8_t *) aux, n); break;
|
|
764
|
+
case CA_UINT8: ca_sort_merge_u8 ((uint8_t *) po, (uint8_t *) aux, n); break;
|
|
765
|
+
case CA_INT16: ca_sort_merge_i16((int16_t *) po, (int16_t *) aux, n); break;
|
|
766
|
+
case CA_UINT16: ca_sort_merge_u16((uint16_t *) po, (uint16_t *) aux, n); break;
|
|
767
|
+
case CA_INT32: ca_sort_merge_i32((int32_t *) po, (int32_t *) aux, n); break;
|
|
768
|
+
case CA_UINT32: ca_sort_merge_u32((uint32_t *) po, (uint32_t *) aux, n); break;
|
|
769
|
+
case CA_INT64: ca_sort_merge_i64((int64_t *) po, (int64_t *) aux, n); break;
|
|
770
|
+
case CA_UINT64: ca_sort_merge_u64((uint64_t *) po, (uint64_t *) aux, n); break;
|
|
771
|
+
case CA_FLOAT32: {
|
|
772
|
+
ca_size_t fin = ca_partition_nan_f32((float32_t *) po, n);
|
|
773
|
+
ca_sort_merge_f32((float32_t *) po, (float32_t *) aux, fin);
|
|
774
|
+
break;
|
|
775
|
+
}
|
|
776
|
+
case CA_FLOAT64: {
|
|
777
|
+
ca_size_t fin = ca_partition_nan_f64((double *) po, n);
|
|
778
|
+
ca_sort_merge_f64((double *) po, (double *) aux, fin);
|
|
779
|
+
break;
|
|
780
|
+
}
|
|
781
|
+
default:
|
|
782
|
+
rb_raise(rb_eCADataTypeError,
|
|
783
|
+
"sort_copy: BUG: unexpected data_type %d", cat->data_type);
|
|
784
|
+
}
|
|
785
|
+
} else {
|
|
786
|
+
switch ( cat->data_type ) {
|
|
787
|
+
case CA_INT8: ca_sort_quick_i8 ((int8_t *) po, n); break;
|
|
788
|
+
case CA_UINT8: ca_sort_quick_u8 ((uint8_t *) po, n); break;
|
|
789
|
+
case CA_INT16: ca_sort_quick_i16((int16_t *) po, n); break;
|
|
790
|
+
case CA_UINT16: ca_sort_quick_u16((uint16_t *) po, n); break;
|
|
791
|
+
case CA_INT32: ca_sort_quick_i32((int32_t *) po, n); break;
|
|
792
|
+
case CA_UINT32: ca_sort_quick_u32((uint32_t *) po, n); break;
|
|
793
|
+
case CA_INT64: ca_sort_quick_i64((int64_t *) po, n); break;
|
|
794
|
+
case CA_UINT64: ca_sort_quick_u64((uint64_t *) po, n); break;
|
|
795
|
+
case CA_FLOAT32: {
|
|
796
|
+
ca_size_t fin = ca_partition_nan_f32((float32_t *) po, n);
|
|
797
|
+
ca_sort_quick_f32((float32_t *) po, fin);
|
|
798
|
+
break;
|
|
799
|
+
}
|
|
800
|
+
case CA_FLOAT64: {
|
|
801
|
+
ca_size_t fin = ca_partition_nan_f64((double *) po, n);
|
|
802
|
+
ca_sort_quick_f64((double *) po, fin);
|
|
803
|
+
break;
|
|
804
|
+
}
|
|
805
|
+
default:
|
|
806
|
+
rb_raise(rb_eCADataTypeError,
|
|
807
|
+
"sort_copy: BUG: unexpected data_type %d", cat->data_type);
|
|
808
|
+
}
|
|
809
|
+
}
|
|
810
|
+
}
|
|
811
|
+
if ( aux ) xfree(aux);
|
|
812
|
+
|
|
813
|
+
return vout;
|
|
814
|
+
}
|
|
815
|
+
|
|
816
|
+
|
|
817
|
+
void
|
|
818
|
+
Init_carray_sort (void)
|
|
819
|
+
{
|
|
820
|
+
rb_define_singleton_method(rb_cCArray, "sort_addr", rb_ca_s_sort_addr, -1);
|
|
821
|
+
rb_define_method(rb_cCArray, "sort_addr", rb_ca_sort_addr, -1);
|
|
822
|
+
rb_define_method(rb_cCArray, "axis2addr", rb_ca_axis2addr, -1);
|
|
823
|
+
|
|
824
|
+
/* `sort` returns a CARemap view (no-axis flattens to 1-D);
|
|
825
|
+
`sort_copy` is the eager entity counterpart. Both accept axis:
|
|
826
|
+
and kind: kwargs -- see the function comments above. */
|
|
827
|
+
rb_define_method(rb_cCArray, "sort", rb_ca_sorted_view, -1);
|
|
828
|
+
rb_define_method(rb_cCArray, "sort_copy", rb_ca_sort_copy, -1);
|
|
829
|
+
}
|