123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244 |
- /* The routine quicksort was extracted from the GNU C Library qsort.c
- written by Douglas C. Schmidt (schmidt@ics.uci.edu)
- and adapted to guile by adding an extra pointer less
- to quicksort by Roland Orre <orre@nada.kth.se>.
- The reason to do this instead of using the library function qsort
- was to avoid dependency of the ANSI-C extensions for local functions
- and also to avoid obscure pool based solutions.
- This sorting routine is not much more efficient than the stable
- version but doesn't consume extra memory.
- */
- #define SWAP(a, b) do { const SCM _tmp = a; a = b; b = _tmp; } while (0)
- /* Order using quicksort. This implementation incorporates four
- optimizations discussed in Sedgewick:
- 1. Non-recursive, using an explicit stack of pointer that store the next
- array partition to sort. To save time, this maximum amount of space
- required to store an array of MAX_SIZE_T is allocated on the stack.
- Assuming a bit width of 32 bits for size_t, this needs only
- 32 * sizeof (stack_node) == 128 bytes. Pretty cheap, actually.
- 2. Chose the pivot element using a median-of-three decision tree. This
- reduces the probability of selecting a bad pivot value and eliminates
- certain extraneous comparisons.
- 3. Only quicksorts NR_ELEMS / MAX_THRESH partitions, leaving insertion sort
- to order the MAX_THRESH items within each partition. This is a big win,
- since insertion sort is faster for small, mostly sorted array segments.
- 4. The larger of the two sub-partitions is always pushed onto the
- stack first, with the algorithm then concentrating on the
- smaller partition. This *guarantees* no more than log (n)
- stack size is needed (actually O(1) in this case)! */
- /* Discontinue quicksort algorithm when partition gets below this size.
- * This particular magic number was chosen to work best on a Sun 4/260. */
- #define MAX_THRESH 4
- /* Inline stack abstraction: The stack size for quicksorting at most as many
- * elements as can be given by a value of type size_t is, as described above,
- * log (MAX_SIZE_T), which is the number of bits of size_t. More accurately,
- * we would only need ceil (log (MAX_SIZE_T / MAX_THRESH)), but this is
- * ignored below. */
- #define STACK_SIZE (8 * sizeof (size_t)) /* assume 8 bit char */
- #define PUSH(low, high) ((void) ((top->lo = (low)), (top->hi = (high)), ++top))
- #define POP(low, high) ((void) (--top, (low = top->lo), (high = top->hi)))
- #define STACK_NOT_EMPTY (stack < top)
- static void
- NAME (SCM *const base_ptr, size_t nr_elems, INC_PARAM
- SCM less)
- {
- /* Stack node declarations used to store unfulfilled partition obligations. */
- typedef struct {
- size_t lo;
- size_t hi;
- } stack_node;
- static const char s_buggy_less[] = "buggy less predicate used when sorting";
- #define ELT(i) base_ptr[(i)*INC]
- if (nr_elems == 0)
- /* Avoid lossage with unsigned arithmetic below. */
- return;
- if (nr_elems > MAX_THRESH)
- {
- size_t lo = 0;
- size_t hi = nr_elems-1;
- stack_node stack[STACK_SIZE];
- stack_node *top = stack + 1;
- while (STACK_NOT_EMPTY)
- {
- size_t left;
- size_t right;
- size_t mid = lo + (hi - lo) / 2;
- SCM pivot;
- /* Select median value from among LO, MID, and HI. Rearrange
- LO and HI so the three values are sorted. This lowers the
- probability of picking a pathological pivot value and
- skips a comparison for both the left and right. */
- SCM_TICK;
-
- if (scm_is_true (scm_call_2 (less, ELT(mid), ELT(lo))))
- SWAP (ELT(mid), ELT(lo));
- if (scm_is_true (scm_call_2 (less, ELT(hi), ELT(mid))))
- SWAP (ELT(mid), ELT(hi));
- else
- goto jump_over;
- if (scm_is_true (scm_call_2 (less, ELT(mid), ELT(lo))))
- SWAP (ELT(mid), ELT(lo));
- jump_over:;
- pivot = ELT(mid);
- left = lo + 1;
- right = hi - 1;
- /* Here's the famous ``collapse the walls'' section of quicksort.
- Gotta like those tight inner loops! They are the main reason
- that this algorithm runs much faster than others. */
- do
- {
- while (scm_is_true (scm_call_2 (less, ELT(left), pivot)))
- {
- left += 1;
- /* The comparison predicate may be buggy */
- if (left > hi)
- scm_misc_error (NULL, s_buggy_less, SCM_EOL);
- }
- while (scm_is_true (scm_call_2 (less, pivot, ELT(right))))
- {
- right -= 1;
- /* The comparison predicate may be buggy */
- if (right < lo)
- scm_misc_error (NULL, s_buggy_less, SCM_EOL);
- }
- if (left < right)
- {
- SWAP (ELT(left), ELT(right));
- left += 1;
- right -= 1;
- }
- else if (left == right)
- {
- left += 1;
- right -= 1;
- break;
- }
- }
- while (left <= right);
- /* Set up pointers for next iteration. First determine whether
- left and right partitions are below the threshold size. If so,
- ignore one or both. Otherwise, push the larger partition's
- bounds on the stack and continue sorting the smaller one. */
- if ((size_t) (right - lo) <= MAX_THRESH)
- {
- if ((size_t) (hi - left) <= MAX_THRESH)
- /* Ignore both small partitions. */
- POP (lo, hi);
- else
- /* Ignore small left partition. */
- lo = left;
- }
- else if ((size_t) (hi - left) <= MAX_THRESH)
- /* Ignore small right partition. */
- hi = right;
- else if ((right - lo) > (hi - left))
- {
- /* Push larger left partition indices. */
- PUSH (lo, right);
- lo = left;
- }
- else
- {
- /* Push larger right partition indices. */
- PUSH (left, hi);
- hi = right;
- }
- }
- }
- /* Once the BASE_PTR array is partially sorted by quicksort the rest is
- completely sorted using insertion sort, since this is efficient for
- partitions below MAX_THRESH size. BASE_PTR points to the beginning of the
- array to sort, and END idexes the very last element in the array (*not*
- one beyond it!). */
- {
- size_t tmp = 0;
- size_t end = nr_elems-1;
- size_t thresh = min (end, MAX_THRESH);
- size_t run;
- /* Find smallest element in first threshold and place it at the
- array's beginning. This is the smallest array element,
- and the operation speeds up insertion sort's inner loop. */
- for (run = tmp + 1; run <= thresh; run += 1)
- if (scm_is_true (scm_call_2 (less, ELT(run), ELT(tmp))))
- tmp = run;
- if (tmp != 0)
- SWAP (ELT(tmp), ELT(0));
- /* Insertion sort, running from left-hand-side up to right-hand-side. */
- run = 1;
- while (++run <= end)
- {
- SCM_TICK;
- tmp = run - 1;
- while (scm_is_true (scm_call_2 (less, ELT(run), ELT(tmp))))
- {
- /* The comparison predicate may be buggy */
- if (tmp == 0)
- scm_misc_error (NULL, s_buggy_less, SCM_EOL);
- tmp -= 1;
- }
- tmp += 1;
- if (tmp != run)
- {
- SCM to_insert = ELT(run);
- size_t hi, lo;
- for (hi = lo = run; --lo >= tmp; hi = lo)
- ELT(hi) = ELT(lo);
- ELT(hi) = to_insert;
- }
- }
- }
- }
- #undef SWAP
- #undef MAX_THRESH
- #undef STACK_SIZE
- #undef PUSH
- #undef POP
- #undef STACK_NOT_EMPTY
- #undef ELT
- #undef NAME
- #undef INC_PARAM
- #undef INC
|