/** Sort array using smoothsort. * * Sort @a N elements from array @a base starting with index @a r with smoothsort. * * @param base pointer to array * @param r lowest index to sort * @param N number of elements to sort * @param less comparison function returning nonzero if m[a] < m[b] * @param swap swapper function exchanging elements m[a] and m[b] */ void su_smoothsort(void *base, size_t r, size_t N, int (*less)(void *m, size_t a, size_t b), void (*swap)(void *m, size_t a, size_t b)) { stretch s = { 1, 1, 1 }; size_t q; array array_i; array* const array = &array_i; array->less = less; array->swap = swap; array->m = base; assert(less && swap); if (base == NULL || N <= 1 || less == NULL || swap == NULL) return; DEBUG(("\nsmoothsort(%p, %zu)\n", array, nmemb)); for (q = 1; q != N; q++, r++, s.p++) { DEBUG(("loop0 q=%zu, b=%u, p=%s \n", q, s.b, binary(s.p))); if ((s.p & 7) == 3) { sift(array, r, s), stretch_up(&s), stretch_up(&s); } else /* if ((s.p & 3) == 1) */ { assert((s.p & 3) == 1); if (q + s.c < N) sift(array, r, s); else trinkle(array, r, s); while (stretch_down(&s, 0) > 1) ; } } trinkle(array, r, s); for (; q > 1; q--) { s.p--; DEBUG(("loop1 q=%zu: b=%u p=%s\n", q, s.b, binary(s.p))); if (s.b <= 1) { while ((s.p & 1) == 0) stretch_up(&s); --r; } else /* if b >= 3 */ { if (s.p) semitrinkle(array, r - (s.b - s.c), s); stretch_down(&s, 1); semitrinkle(array, --r, s); stretch_down(&s, 1); } } }
/** Trinkles the stretches when the adjacent stretches are already trusty. * * @param array description of array to sort * @param r root of the stretch * @param stretch description of stretches to trinkle */ static void semitrinkle(array const *array, size_t r, stretch s) { size_t r1 = r - s.c; DEBUG(("semitrinkle(%p, %zu, (%u, %s))\n", array, r, s.b, binary(s.p))); if (array->less(array->m, r, r1)) { DEBUG(("\tswap(%p @%zu <=> @%zu b=%u)\n", array, r, r1, s.b)); array->swap(array->m, r, r1); trinkle(array, r1, s); } }
void qsort(void *base, size_t nel, size_t width, cmpfun cmp) { size_t lp[12*sizeof(size_t)]; 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(lp[0]=lp[1]=width, i=2; (lp[i]=lp[i-2]+lp[i-1]+width) < size; i++); while(head < high) { if((p[0] & 3) == 3) { sift(head, width, cmp, pshift, lp); shr(p, 2); pshift += 2; } else { if(lp[pshift - 1] >= high - head) { trinkle(head, width, cmp, p, pshift, 0, lp); } else { sift(head, width, cmp, pshift, lp); } if(pshift == 1) { shl(p, 1); pshift = 0; } else { shl(p, pshift - 1); pshift = 1; } } p[0] |= 1; head += width; } trinkle(head, width, cmp, p, pshift, 0, lp); while(pshift != 1 || p[0] != 1 || p[1] != 0) { if(pshift <= 1) { trail = pntz(p); shr(p, trail); pshift += trail; } else { shl(p, 2); pshift -= 2; p[0] ^= 7; shr(p, 1); trinkle(head - lp[pshift] - width, width, cmp, p, pshift + 1, 1, lp); shl(p, 1); p[0] |= 1; trinkle(head - width, width, cmp, p, pshift, 1, lp); } head -= width; } }