diff --git a/libc/mem/alg.h b/libc/mem/alg.h index 2522ab53b..271cdc313 100644 --- a/libc/mem/alg.h +++ b/libc/mem/alg.h @@ -2,22 +2,22 @@ #define COSMOPOLITAN_LIBC_ALG_ALG_H_ #if !(__ASSEMBLER__ + __LINKER__ + 0) COSMOPOLITAN_C_START_ -/*───────────────────────────────────────────────────────────────────────────│─╗ -│ cosmopolitan § algorithms ─╬─│┼ -╚────────────────────────────────────────────────────────────────────────────│*/ void *bsearch(const void *, const void *, size_t, size_t, - int cmp(const void *, const void *)) + int (*)(const void *, const void *)) paramsnonnull() dontthrow nosideeffect; void *bsearch_r(const void *, const void *, size_t, size_t, - int cmp(const void *, const void *, void *), void *) + int (*)(const void *, const void *, void *), void *) paramsnonnull((1, 2, 5)) dontthrow nosideeffect; void djbsort(int32_t *, size_t); void qsort(void *, size_t, size_t, int (*)(const void *, const void *)) paramsnonnull(); void qsort_r(void *, size_t, size_t, - int cmp(const void *, const void *, void *), void *arg) + int (*)(const void *, const void *, void *), void *) paramsnonnull((1, 4)); +void smoothsort(void *, size_t, size_t, int (*)(const void *, const void *)); +void smoothsort_r(void *, size_t, size_t, + int (*)(const void *, const void *, void *), void *); int tarjan(int, const int (*)[2], int, int[], int[], int *) paramsnonnull((2, 4)) nocallback dontthrow; diff --git a/libc/str/qsort.c b/libc/str/qsort.c index 7134fe2fa..31adbd9a4 100644 --- a/libc/str/qsort.c +++ b/libc/str/qsort.c @@ -1,241 +1,37 @@ /*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│ │vi: set net ft=c ts=2 sts=2 sw=2 fenc=utf-8 :vi│ -╚──────────────────────────────────────────────────────────────────────────────╝ -│ Copyright (C) 2011 by Valentin Ochs │ +╞══════════════════════════════════════════════════════════════════════════════╡ +│ Copyright 2022 Justine Alexandra Roberts Tunney │ │ │ -│ Permission is hereby granted, free of charge, to any person obtaining a copy │ -│ of this software and associated documentation files (the "Software"), to │ -│ deal in the Software without restriction, including without limitation the │ -│ rights to use, copy, modify, merge, publish, distribute, sublicense, and/or │ -│ sell copies of the Software, and to permit persons to whom the Software is │ -│ furnished to do so, subject to the following conditions: │ +│ Permission to use, copy, modify, and/or distribute this software for │ +│ any purpose with or without fee is hereby granted, provided that the │ +│ above copyright notice and this permission notice appear in all copies. │ │ │ -│ The above copyright notice and this permission notice shall be included in │ -│ all copies or substantial portions of the Software. │ -│ │ -│ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR │ -│ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, │ -│ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE │ -│ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER │ -│ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING │ -│ FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS │ -│ IN THE SOFTWARE. │ -└─────────────────────────────────────────────────────────────────────────────*/ +│ THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL │ +│ WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED │ +│ WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE │ +│ AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL │ +│ DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR │ +│ PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER │ +│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │ +│ PERFORMANCE OF THIS SOFTWARE. │ +╚─────────────────────────────────────────────────────────────────────────────*/ #include "libc/mem/alg.h" -#include "libc/assert.h" -#include "libc/nexgen32e/bsf.h" -#include "libc/str/str.h" - -asm(".ident\t\"\\n\\n\ -Smoothsort (MIT License)\\n\ -Copyright 2011 Valentin Ochs\\n\ -Discovered by Edsger Dijkstra\""); -asm(".include \"libc/disclaimer.inc\""); - -typedef int (*cmpfun)(const void *, const void *, void *); - -struct SmoothSort { - size_t lp[12 * sizeof(size_t)]; - unsigned char *ar[14 * sizeof(size_t) + 1]; - unsigned char tmp[256]; -}; - -static inline int ntz(unsigned long x) { - return __builtin_ctzl(x); -} - -static inline int pntz(size_t p[2]) { - int r = ntz(p[0] - 1); - if (r != 0 || (r = CHAR_BIT * sizeof(size_t) + ntz(p[1])) != - CHAR_BIT * sizeof(size_t)) { - return r; - } - return 0; -} - -// smoothsort_shl() and smoothsort_shr() need n > 0 -static inline void smoothsort_shl(size_t p[2], int n) { - if (n >= CHAR_BIT * sizeof(size_t)) { - n -= CHAR_BIT * sizeof(size_t); - p[1] = p[0]; - p[0] = 0; - } - p[1] <<= n; - p[1] |= p[0] >> (sizeof(size_t) * CHAR_BIT - n); - p[0] <<= n; -} - -static inline void smoothsort_shr(size_t p[2], int n) { - if (n >= CHAR_BIT * sizeof(size_t)) { - n -= CHAR_BIT * sizeof(size_t); - p[0] = p[1]; - p[1] = 0; - } - p[0] >>= n; - p[0] |= p[1] << (sizeof(size_t) * CHAR_BIT - n); - p[1] >>= n; -} - -static void smoothsort_cycle(struct SmoothSort *s, size_t width, int n) { - size_t l; - int i; - if (n < 2) { - return; - } - s->ar[n] = s->tmp; - while (width) { - l = sizeof(s->tmp) < width ? sizeof(s->tmp) : width; - memcpy(s->ar[n], s->ar[0], l); - for (i = 0; i < n; i++) { - memcpy(s->ar[i], s->ar[i + 1], l); - s->ar[i] += l; - } - width -= l; - } -} - -static void smoothsort_sift(struct SmoothSort *s, unsigned char *head, - size_t width, cmpfun cmp, void *arg, int pshift) { - unsigned char *rt, *lf; - int i = 1; - s->ar[0] = head; - while (pshift > 1) { - rt = head - width; - lf = head - width - s->lp[pshift - 2]; - if (cmp(s->ar[0], lf, arg) >= 0 && cmp(s->ar[0], rt, arg) >= 0) { - break; - } - if (cmp(lf, rt, arg) >= 0) { - s->ar[i++] = lf; - head = lf; - pshift -= 1; - } else { - s->ar[i++] = rt; - head = rt; - pshift -= 2; - } - } - smoothsort_cycle(s, width, i); -} - -static void smoothsort_trinkle(struct SmoothSort *s, unsigned char *head, - size_t width, cmpfun cmp, void *arg, - size_t pp[2], int pshift, int trusty) { - unsigned char *stepson, *rt, *lf; - size_t p[2]; - int i = 1; - int trail; - p[0] = pp[0]; - p[1] = pp[1]; - s->ar[0] = head; - while (p[0] != 1 || p[1] != 0) { - stepson = head - s->lp[pshift]; - if (cmp(stepson, s->ar[0], arg) <= 0) { - break; - } - if (!trusty && pshift > 1) { - rt = head - width; - lf = head - width - s->lp[pshift - 2]; - if (cmp(rt, stepson, arg) >= 0 || cmp(lf, stepson, arg) >= 0) { - break; - } - } - s->ar[i++] = stepson; - head = stepson; - trail = pntz(p); - smoothsort_shr(p, trail); - pshift += trail; - trusty = 0; - } - if (!trusty) { - smoothsort_cycle(s, width, i); - smoothsort_sift(s, head, width, cmp, arg, pshift); - } -} - -static void smoothsort(struct SmoothSort *s, void *base, size_t nel, - size_t width, cmpfun cmp, void *arg) { - size_t i, size = width * nel; - unsigned char *head, *high; - size_t p[2] = {1, 0}; - int pshift = 1; - int trail; - if (!size) return; - head = base; - high = head + size - width; - // precompute Leonardo numbers, scaled by element width - for (s->lp[0] = s->lp[1] = width, i = 2; - (s->lp[i] = s->lp[i - 2] + s->lp[i - 1] + width) < size; i++) { - } - while (head < high) { - if ((p[0] & 3) == 3) { - smoothsort_sift(s, head, width, cmp, arg, pshift); - smoothsort_shr(p, 2); - pshift += 2; - } else { - if (s->lp[pshift - 1] >= high - head) { - smoothsort_trinkle(s, head, width, cmp, arg, p, pshift, 0); - } else { - smoothsort_sift(s, head, width, cmp, arg, pshift); - } - if (pshift == 1) { - smoothsort_shl(p, 1); - pshift = 0; - } else { - smoothsort_shl(p, pshift - 1); - pshift = 1; - } - } - p[0] |= 1; - head += width; - } - smoothsort_trinkle(s, head, width, cmp, arg, p, pshift, 0); - while (pshift != 1 || p[0] != 1 || p[1] != 0) { - if (pshift <= 1) { - trail = pntz(p); - smoothsort_shr(p, trail); - pshift += trail; - } else { - smoothsort_shl(p, 2); - pshift -= 2; - p[0] ^= 7; - smoothsort_shr(p, 1); - smoothsort_trinkle(s, head - s->lp[pshift] - width, width, cmp, arg, p, - pshift + 1, 1); - smoothsort_shl(p, 1); - p[0] |= 1; - smoothsort_trinkle(s, head - width, width, cmp, arg, p, pshift, 1); - } - head -= width; - } -} /** * Sorts array. * + * This implementation uses the Quicksort routine from Bentley & + * McIlroy's "Engineering a Sort Function", 1992, Bell Labs. + * * @param base points to an array to sort in-place * @param count is the item count * @param width is the size of each item * @param cmp is a callback returning <0, 0, or >0 - * @param arg will optionally be passed as the third argument to cmp - * @see qsort() + * @see smoothsort() + * @see djbsort() */ -void qsort_r(void *base, size_t count, size_t width, cmpfun cmp, void *arg) { - struct SmoothSort s; - smoothsort(&s, base, count, width, cmp, arg); -} - -/** - * Sorts array. - * - * @param base points to an array to sort in-place - * @param count is the item count - * @param width is the size of each item - * @param cmp is a callback returning <0, 0, or >0 - * @see longsort(), djbsort() - */ -void qsort(void *base, size_t count, size_t width, - int cmp(const void *, const void *)) { - struct SmoothSort s; - smoothsort(&s, base, count, width, (cmpfun)cmp, 0); +void qsort(void *a, size_t n, size_t es, + int (*cmp)(const void *, const void *)) { + qsort_r(a, n, es, (void *)cmp, 0); } diff --git a/libc/str/qsort_r.c b/libc/str/qsort_r.c new file mode 100644 index 000000000..e69b7e7fc --- /dev/null +++ b/libc/str/qsort_r.c @@ -0,0 +1,178 @@ +/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│ +│vi: set net ft=c ts=2 sts=2 sw=2 fenc=utf-8 :vi│ +╞══════════════════════════════════════════════════════════════════════════════╡ +│ Copyright (c) 1992, 1993 │ +│ The Regents of the University of California. All rights reserved. │ +│ │ +│ Redistribution and use in source and binary forms, with or without │ +│ modification, are permitted provided that the following conditions │ +│ are met: │ +│ 1. Redistributions of source code must retain the above copyright │ +│ notice, this list of conditions and the following disclaimer. │ +│ 2. Redistributions in binary form must reproduce the above copyright │ +│ notice, this list of conditions and the following disclaimer in the │ +│ documentation and/or other materials provided with the distribution. │ +│ 3. Neither the name of the University nor the names of its contributors │ +│ may be used to endorse or promote products derived from this software │ +│ without specific prior written permission. │ +│ │ +│ THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND │ +│ ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE │ +│ IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE │ +│ ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE │ +│ FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL │ +│ DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS │ +│ OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) │ +│ HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT │ +│ LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY │ +│ OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF │ +│ SUCH DAMAGE. │ +╚─────────────────────────────────────────────────────────────────────────────*/ +#include "libc/macros.internal.h" +#include "libc/mem/alg.h" +#include "libc/str/str.h" + +asm(".ident\t\"\\n\\n\ +NetBSD qsort (BSD-3)\\n\ +Copyright 1992 The Regents of the University of California\""); +asm(".include \"libc/disclaimer.inc\""); + +#define SWAPINIT(a, es) \ + swaptype = ((char *)a - (char *)0) % sizeof(long) || es % sizeof(long) ? 2 \ + : es == sizeof(long) ? 0 \ + : 1; + +#define swapcode(TYPE, parmi, parmj, n) \ + size_t i = (n) / sizeof(TYPE); \ + TYPE *pi = (TYPE *)(void *)(parmi); \ + TYPE *pj = (TYPE *)(void *)(parmj); \ + do { \ + TYPE t = *pi; \ + *pi++ = *pj; \ + *pj++ = t; \ + } while (--i > 0) + +#define swap(a, b) \ + if (swaptype == 0) { \ + long t = *(long *)(void *)(a); \ + *(long *)(void *)(a) = *(long *)(void *)(b); \ + *(long *)(void *)(b) = t; \ + } else \ + swapfunc(a, b, es, swaptype) + +#define vecswap(a, b, n) \ + if ((n) > 0) swapfunc((a), (b), (size_t)(n), swaptype) + +static inline void swapfunc(char *a, char *b, size_t n, int swaptype) { + if (swaptype <= 1) { + swapcode(long, a, b, n); + } else { + swapcode(char, a, b, n); + } +} + +static inline char *med3(char *a, char *b, char *c, + int (*cmp)(const void *, const void *, void *), + void *z) { + if (cmp(a, b, z) < 0) { + return cmp(b, c, z) < 0 ? b : (cmp(a, c, z) < 0 ? c : a); + } else { + return cmp(b, c, z) > 0 ? b : (cmp(a, c, z) < 0 ? a : c); + } +} + +/** + * Sorts array. + * + * This implementation uses the Quicksort routine from Bentley & + * McIlroy's "Engineering a Sort Function", 1992, Bell Labs. This + * implementation is also used on systems like NetBSD and MacOS. + * + * @param a is base which points to an array to sort in-place + * @param n is item count + * @param es is width of each item + * @param cmp is a callback returning <0, 0, or >0 + * @param arg will optionally be passed as the third argument to cmp + * @see smoothsort_r() + */ +void qsort_r(void *a, size_t n, size_t es, + int (*cmp)(const void *, const void *, void *), void *arg) { + size_t d, r, s; + int swaptype, cmp_result; + char *pa, *pb, *pc, *pd, *pl, *pm, *pn; + +loop: + SWAPINIT(a, es); + if (n < 7) { + for (pm = (char *)a + es; pm < (char *)a + n * es; pm += es) + for (pl = pm; pl > (char *)a && cmp(pl - es, pl, arg) > 0; pl -= es) + swap(pl, pl - es); + return; + } + + pm = (char *)a + (n / 2) * es; + if (n > 7) { + pl = (char *)a; + pn = (char *)a + (n - 1) * es; + if (n > 40) { + d = (n / 8) * es; + pl = med3(pl, pl + d, pl + 2 * d, cmp, arg); + pm = med3(pm - d, pm, pm + d, cmp, arg); + pn = med3(pn - 2 * d, pn - d, pn, cmp, arg); + } + pm = med3(pl, pm, pn, cmp, arg); + } + swap(a, pm); + pa = pb = (char *)a + es; + pc = pd = (char *)a + (n - 1) * es; + + for (;;) { + while (pb <= pc && (cmp_result = cmp(pb, a, arg)) <= 0) { + if (cmp_result == 0) { + swap(pa, pb); + pa += es; + } + pb += es; + } + while (pb <= pc && (cmp_result = cmp(pc, a, arg)) >= 0) { + if (cmp_result == 0) { + swap(pc, pd); + pd -= es; + } + pc -= es; + } + if (pb > pc) break; + swap(pb, pc); + pb += es; + pc -= es; + } + + pn = (char *)a + n * es; + r = MIN(pa - (char *)a, pb - pa); + vecswap(a, pb - r, r); + r = MIN((size_t)(pd - pc), pn - pd - es); + vecswap(pb, pn - r, r); + + /* + * To save stack space we sort the smaller side of the partition first + * using recursion and eliminate tail recursion for the larger side. + */ + r = pb - pa; + s = pd - pc; + if (r < s) { + /* Recurse for 1st side, iterate for 2nd side. */ + if (s > es) { + if (r > es) qsort_r(a, r / es, es, cmp, arg); + a = pn - s; + n = s / es; + goto loop; + } + } else { + /* Recurse for 2nd side, iterate for 1st side. */ + if (r > es) { + if (s > es) qsort_r(pn - s, s / es, es, cmp, arg); + n = r / es; + goto loop; + } + } +} diff --git a/libc/str/smoothsort.c b/libc/str/smoothsort.c new file mode 100644 index 000000000..42a7ed770 --- /dev/null +++ b/libc/str/smoothsort.c @@ -0,0 +1,244 @@ +/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│ +│vi: set net ft=c ts=2 sts=2 sw=2 fenc=utf-8 :vi│ +╚──────────────────────────────────────────────────────────────────────────────╝ +│ Copyright (C) 2011 by Valentin Ochs │ +│ │ +│ Permission is hereby granted, free of charge, to any person obtaining a copy │ +│ of this software and associated documentation files (the "Software"), to │ +│ deal in the Software without restriction, including without limitation the │ +│ rights to use, copy, modify, merge, publish, distribute, sublicense, and/or │ +│ sell copies of the Software, and to permit persons to whom the Software is │ +│ furnished to do so, subject to the following conditions: │ +│ │ +│ The above copyright notice and this permission notice shall be included in │ +│ all copies or substantial portions of the Software. │ +│ │ +│ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR │ +│ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, │ +│ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE │ +│ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER │ +│ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING │ +│ FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS │ +│ IN THE SOFTWARE. │ +└─────────────────────────────────────────────────────────────────────────────*/ +#include "libc/assert.h" +#include "libc/mem/alg.h" +#include "libc/nexgen32e/bsf.h" +#include "libc/str/str.h" + +asm(".ident\t\"\\n\\n\ +Smoothsort (MIT License)\\n\ +Copyright 2011 Valentin Ochs\\n\ +Discovered by Edsger Dijkstra\""); +asm(".include \"libc/disclaimer.inc\""); + +typedef int (*cmpfun)(const void *, const void *, void *); + +struct SmoothSort { + size_t lp[12 * sizeof(size_t)]; + unsigned char *ar[14 * sizeof(size_t) + 1]; + unsigned char tmp[256]; +}; + +static inline int ntz(unsigned long x) { + return __builtin_ctzl(x); +} + +static inline int pntz(size_t p[2]) { + int r = ntz(p[0] - 1); + if (r != 0 || (r = CHAR_BIT * sizeof(size_t) + ntz(p[1])) != + CHAR_BIT * sizeof(size_t)) { + return r; + } + return 0; +} + +// SmoothSort_shl() and SmoothSort_shr() need n > 0 +static inline void SmoothSort_shl(size_t p[2], int n) { + if (n >= CHAR_BIT * sizeof(size_t)) { + n -= CHAR_BIT * sizeof(size_t); + p[1] = p[0]; + p[0] = 0; + } + p[1] <<= n; + p[1] |= p[0] >> (sizeof(size_t) * CHAR_BIT - n); + p[0] <<= n; +} + +static inline void SmoothSort_shr(size_t p[2], int n) { + if (n >= CHAR_BIT * sizeof(size_t)) { + n -= CHAR_BIT * sizeof(size_t); + p[0] = p[1]; + p[1] = 0; + } + p[0] >>= n; + p[0] |= p[1] << (sizeof(size_t) * CHAR_BIT - n); + p[1] >>= n; +} + +static void SmoothSort_cycle(struct SmoothSort *s, size_t width, int n) { + size_t l; + int i; + if (n < 2) { + return; + } + s->ar[n] = s->tmp; + while (width) { + l = sizeof(s->tmp) < width ? sizeof(s->tmp) : width; + memcpy(s->ar[n], s->ar[0], l); + for (i = 0; i < n; i++) { + memcpy(s->ar[i], s->ar[i + 1], l); + s->ar[i] += l; + } + width -= l; + } +} + +static void SmoothSort_sift(struct SmoothSort *s, unsigned char *head, + size_t width, cmpfun cmp, void *arg, int pshift) { + unsigned char *rt, *lf; + int i = 1; + s->ar[0] = head; + while (pshift > 1) { + rt = head - width; + lf = head - width - s->lp[pshift - 2]; + if (cmp(s->ar[0], lf, arg) >= 0 && cmp(s->ar[0], rt, arg) >= 0) { + break; + } + if (cmp(lf, rt, arg) >= 0) { + s->ar[i++] = lf; + head = lf; + pshift -= 1; + } else { + s->ar[i++] = rt; + head = rt; + pshift -= 2; + } + } + SmoothSort_cycle(s, width, i); +} + +static void SmoothSort_trinkle(struct SmoothSort *s, unsigned char *head, + size_t width, cmpfun cmp, void *arg, + size_t pp[2], int pshift, int trusty) { + unsigned char *stepson, *rt, *lf; + size_t p[2]; + int i = 1; + int trail; + p[0] = pp[0]; + p[1] = pp[1]; + s->ar[0] = head; + while (p[0] != 1 || p[1] != 0) { + stepson = head - s->lp[pshift]; + if (cmp(stepson, s->ar[0], arg) <= 0) { + break; + } + if (!trusty && pshift > 1) { + rt = head - width; + lf = head - width - s->lp[pshift - 2]; + if (cmp(rt, stepson, arg) >= 0 || cmp(lf, stepson, arg) >= 0) { + break; + } + } + s->ar[i++] = stepson; + head = stepson; + trail = pntz(p); + SmoothSort_shr(p, trail); + pshift += trail; + trusty = 0; + } + if (!trusty) { + SmoothSort_cycle(s, width, i); + SmoothSort_sift(s, head, width, cmp, arg, pshift); + } +} + +static void SmoothSort(struct SmoothSort *s, void *base, size_t nel, + size_t width, cmpfun cmp, void *arg) { + size_t i, size = width * nel; + unsigned char *head, *high; + size_t p[2] = {1, 0}; + int pshift = 1; + int trail; + if (!size) return; + head = base; + high = head + size - width; + // precompute Leonardo numbers, scaled by element width + for (s->lp[0] = s->lp[1] = width, i = 2; + (s->lp[i] = s->lp[i - 2] + s->lp[i - 1] + width) < size; i++) { + } + while (head < high) { + if ((p[0] & 3) == 3) { + SmoothSort_sift(s, head, width, cmp, arg, pshift); + SmoothSort_shr(p, 2); + pshift += 2; + } else { + if (s->lp[pshift - 1] >= high - head) { + SmoothSort_trinkle(s, head, width, cmp, arg, p, pshift, 0); + } else { + SmoothSort_sift(s, head, width, cmp, arg, pshift); + } + if (pshift == 1) { + SmoothSort_shl(p, 1); + pshift = 0; + } else { + SmoothSort_shl(p, pshift - 1); + pshift = 1; + } + } + p[0] |= 1; + head += width; + } + SmoothSort_trinkle(s, head, width, cmp, arg, p, pshift, 0); + while (pshift != 1 || p[0] != 1 || p[1] != 0) { + if (pshift <= 1) { + trail = pntz(p); + SmoothSort_shr(p, trail); + pshift += trail; + } else { + SmoothSort_shl(p, 2); + pshift -= 2; + p[0] ^= 7; + SmoothSort_shr(p, 1); + SmoothSort_trinkle(s, head - s->lp[pshift] - width, width, cmp, arg, p, + pshift + 1, 1); + SmoothSort_shl(p, 1); + p[0] |= 1; + SmoothSort_trinkle(s, head - width, width, cmp, arg, p, pshift, 1); + } + head -= width; + } +} + +/** + * Sorts array. + * + * @param base points to an array to sort in-place + * @param count is the item count + * @param width is the size of each item + * @param cmp is a callback returning <0, 0, or >0 + * @param arg will optionally be passed as the third argument to cmp + * @see smoothsort() + * @see qsort() + */ +void smoothsort_r(void *base, size_t count, size_t width, cmpfun cmp, + void *arg) { + struct SmoothSort s; + SmoothSort(&s, base, count, width, cmp, arg); +} + +/** + * Sorts array. + * + * @param base points to an array to sort in-place + * @param count is the item count + * @param width is the size of each item + * @param cmp is a callback returning <0, 0, or >0 + * @see smoothsort_r() + * @see qsort() + */ +void smoothsort(void *base, size_t count, size_t width, + int cmp(const void *, const void *)) { + struct SmoothSort s; + SmoothSort(&s, base, count, width, (cmpfun)cmp, 0); +} diff --git a/test/libc/str/qsort_test.c b/test/libc/str/qsort_r_test.c similarity index 72% rename from test/libc/str/qsort_test.c rename to test/libc/str/qsort_r_test.c index 58308489e..17fa5faca 100644 --- a/test/libc/str/qsort_test.c +++ b/test/libc/str/qsort_r_test.c @@ -16,13 +16,14 @@ │ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ -#include "libc/mem/alg.h" #include "libc/intrin/bits.h" #include "libc/macros.internal.h" +#include "libc/mem/alg.h" #include "libc/mem/mem.h" #include "libc/nexgen32e/bsr.h" -#include "libc/stdio/rand.h" #include "libc/runtime/gc.internal.h" +#include "libc/stdio/rand.h" +#include "libc/stdio/stdio.h" #include "libc/str/str.h" #include "libc/testlib/ezbench.h" #include "libc/testlib/testlib.h" @@ -50,11 +51,39 @@ TEST(qsort, test) { } BENCH(qsort, bench) { + size_t i; size_t n = 1000; long *p1 = gc(malloc(n * sizeof(long))); long *p2 = gc(malloc(n * sizeof(long))); - rngset(p1, n * sizeof(long), 0, 0); - EZBENCH2("qsort", memcpy(p2, p1, n * sizeof(long)), + + printf("\n"); + for (i = 0; i < n; ++i) p1[i] = i + ((lemur64() % 3) - 1); + EZBENCH2("qsort nearly", memcpy(p2, p1, n * sizeof(long)), qsort(p2, n, sizeof(long), CompareLong)); - EZBENCH2("longsort", memcpy(p2, p1, n * sizeof(long)), longsort(p2, n)); + EZBENCH2("smoothsort nearly", memcpy(p2, p1, n * sizeof(long)), + smoothsort(p2, n, sizeof(long), CompareLong)); + + printf("\n"); + for (i = 0; i < n; ++i) p1[i] = n - i; + EZBENCH2("qsort reverse", memcpy(p2, p1, n * sizeof(long)), + qsort(p2, n, sizeof(long), CompareLong)); + EZBENCH2("smoothsort reverse", memcpy(p2, p1, n * sizeof(long)), + smoothsort(p2, n, sizeof(long), CompareLong)); + + printf("\n"); + rngset(p1, n * sizeof(long), 0, 0); + EZBENCH2("qsort random", memcpy(p2, p1, n * sizeof(long)), + qsort(p2, n, sizeof(long), CompareLong)); + EZBENCH2("smoothsort random", memcpy(p2, p1, n * sizeof(long)), + smoothsort(p2, n, sizeof(long), CompareLong)); + + printf("\n"); + for (i = 0; i < n / 2; ++i) { + p1[i] = i; + p1[n - i - 1] = i; + } + EZBENCH2("qsort 2n", memcpy(p2, p1, n * sizeof(long)), + qsort(p2, n, sizeof(long), CompareLong)); + EZBENCH2("smoothsort 2n", memcpy(p2, p1, n * sizeof(long)), + smoothsort(p2, n, sizeof(long), CompareLong)); }