Files
ww/lib/sort/sort.ww
Hojun-Cho 90dc6369c9 comments: drop retired lint markers, re-cite migrated carriers
peel-ok/sizelint-ok/primsize-ok annotations lose their tools; sites
keep the WHY in plain words. Citations of retired carriers move to
their fixture or @test successors (949_errtype_compare -> r949_*,
900_stdlib -> library owners).
2026-08-07 23:21:04 +09:00

109 lines
3.8 KiB
Plaintext

// sort — operations on sorted slices: binary search + insertion-point
// bisection. Faithful port of Hare's sort::{search,lbisect,rbisect} and
// the cmpfunc type (ref/hare/sort/{search,bisect,types}.ha). The
// powersort sort()/insort()/shuffle() surface is out of scope here.
//
// Hare splits this across types.ha/search.ha/bisect.ha; ww merges a
// module into one file (same convention as lib/types/types.ww merging
// Hare's limits.ha + arch+x86_64.ha). test/wcc/963_sort_run.c compiles
// and executes this sole-source package directly.
//
// Divergences from the Hare source, all forced by ww's surface (not
// behavioural — rule-10 align-down):
// • cmp is a fn-VALUE param (`cmp: cmpfunc`), not Hare's pointer
// (`cmp: *cmpfunc`). ww renders functions-in-an-interface by value
// exactly as lib/io.ww's stream vtable does (`read: fn(...)`); the
// `*cmpfunc` pointer form is not callable (no fn-ptr auto-deref) and
// `&fn` is `*fn(...)`, not assignable to the named alias `*cmpfunc`.
// • no `const` qualifier (ww has none; lib/io.ww drops it likewise).
// • Hare walks bytes through `in: *[*]u8` + `&ba[i*sz]`; ww has no
// unbounded-array `[*]`, so the base is `*u8` reinterpreted to
// `uintptr` and elements are `base + i*sz` (the stride idiom proven
// by test/lang/opaque_assign_cast_test.ww).
// • `len()` yields i32 in ww (slice length is i32 today), so the
// count is cast `: size`; Hare's len() is already size.
// • ww `for` is single-condition only (no `for (init; cond; post)`),
// so Hare's `for (cond; afterthought)` becomes a body-tail step.
package sort;
// ref/hare/sort/types.ha:11 — comparator: <0 / 0 / >0 for a < / == / > b.
export type cmpfunc = fn(a: *opaque, b: *opaque) int;
// ref/hare/sort/search.ha:6-27 — binary search; element index, or void.
export fn search(in: []opaque, sz: size, key: *opaque, cmp: cmpfunc) (size | void) = {
let base: uintptr = (in: *u8): uintptr;
let nmemb: size = len(in): size;
for (nmemb > 0) {
let v: *opaque = (base + (nmemb / 2 * sz): uintptr): *opaque;
let r: int = cmp(key, v);
if (r < 0) {
nmemb = nmemb / 2;
} else if (r > 0) {
base = (v: uintptr) + (sz: uintptr);
nmemb = nmemb - (nmemb / 2 + 1);
} else {
let offs: uintptr = (v: uintptr) - ((in: *u8): uintptr);
return (offs / (sz: uintptr)): size;
};
};
return void;
};
// ref/hare/sort/bisect.ha:7-33 — insertion index before the first
// occurrence of an equal element.
export fn lbisect(in: []opaque, sz: size, elem: *opaque, cmp: cmpfunc) size = {
let min: size = 0;
let max: size = len(in): size;
let base: uintptr = (in: *u8): uintptr;
for (min < max) {
let i: size = (max - min) / 2 + min;
let v: *opaque = (base + (i * sz): uintptr): *opaque;
let r: int = cmp(elem, v);
if (r < 0) {
max = i;
} else if (r > 0) {
min = i + 1;
} else {
if (i == 0) { return 0; };
for (i > 0) {
let vp: *opaque = (base + ((i - 1) * sz): uintptr): *opaque;
let rr: int = cmp(elem, vp);
if (rr != 0) { break; };
i = i - 1;
};
return i;
};
};
return max;
};
// ref/hare/sort/bisect.ha:38-65 — insertion index after the last
// occurrence of an equal element.
export fn rbisect(in: []opaque, sz: size, elem: *opaque, cmp: cmpfunc) size = {
let nmemb: size = len(in): size;
let min: size = 0;
let max: size = nmemb;
let base: uintptr = (in: *u8): uintptr;
for (min < max) {
let i: size = (max - min) / 2 + min;
let v: *opaque = (base + (i * sz): uintptr): *opaque;
let r: int = cmp(elem, v);
if (r < 0) {
max = i;
} else if (r > 0) {
min = i + 1;
} else {
i = i + 1;
for (i < nmemb) {
let vp: *opaque = (base + (i * sz): uintptr): *opaque;
let rr: int = cmp(elem, vp);
if (rr != 0) { break; };
i = i + 1;
};
return i;
};
};
return max;
};