]> granicus.if.org Git - python/commitdiff
Added new quicksort implementation, tailored to sorting arrays of
authorGuido van Rossum <guido@python.org>
Tue, 10 Dec 1996 23:55:39 +0000 (23:55 +0000)
committerGuido van Rossum <guido@python.org>
Tue, 10 Dec 1996 23:55:39 +0000 (23:55 +0000)
object pointers.  Should be a bit faster than the C library's qsort(),
and doesn't have the prohibition on recursion that Solaris qsort() has
in the threaded version of their C library.

Thanks to discussions with Tim Peters.

Objects/listobject.c

index 21d0166808911a16798bb873300449d646ca784d..33ebe8120971bae88842640c880c8dd9af166a19 100644 (file)
@@ -550,6 +550,231 @@ listappend(self, args)
        return ins(self, (int) self->ob_size, v);
 }
 
+#define NEWSORT
+
+#ifdef NEWSORT
+
+/* New quicksort implementation for arrays of object pointers.
+   Thanks to discussions with Tim Peters. */
+
+/* CMPERROR is returned by our comparison function when an error
+   occurred.  This is the largest negative integer (0x80000000 on a
+   32-bit system). */
+#define CMPERROR (1 << (8*sizeof(int) - 1))
+
+/* Comparison function.  Takes care of calling a user-supplied
+   comparison function (any callable Python object).  Calls the
+   standard comparison function, cmpobject(), if the user-supplied
+   function is NULL. */
+
+static int
+docompare(x, y, compare)
+       object *x;
+       object *y;
+       object *compare;
+{
+       object *args, *res;
+       int i;
+
+       if (compare == NULL)
+               return cmpobject(x, y);
+
+       args = mkvalue("(OO)", x, y);
+       if (args == NULL)
+               return CMPERROR;
+       res = call_object(compare, args);
+       DECREF(args);
+       if (res == NULL)
+               return CMPERROR;
+       if (!is_intobject(res)) {
+               DECREF(res);
+               err_setstr(TypeError, "comparison function should return int");
+               return CMPERROR;
+       }
+       i = getintvalue(res);
+       DECREF(res);
+       if (i < 0)
+               return -1;
+       if (i > 0)
+               return 1;
+       return 0;
+}
+
+/* Straight insertion sort.  More efficient for sorting small arrays. */
+
+static int
+insertionsort(array, size, compare)
+       object **array; /* Start of array to sort */
+       int size;       /* Number of elements to sort */
+       object *compare;/* Comparison function object, or NULL for default */
+{
+       register object **a = array;
+       register int i;
+
+       for (i = 1; i < size; i++) {
+               register object *key = a[i];
+               register int j = i;
+               while (--j >= 0) {
+                       register int k = docompare(a[j], key, compare);
+                       if (k == CMPERROR)
+                               return -1;
+                       if (k <= 0)
+                               break;
+                       a[j+1] = a[j];
+                       a[j] = key; /* For consistency */
+               }
+       }
+
+       return 0;
+}
+
+/* MINSIZE is the smallest array we care to partition; smaller arrays
+   are sorted using a straight insertion sort (above). */
+#define MINSIZE 10
+
+/* STACKSIZE is the size of our work stack.  A rough estimate is that
+   this allows us to sort arrays of MINSIZE * 2**STACKSIZE, or large
+   enough.  (Because of the way we push the biggest partition first,
+   the worst case occurs when all subarrays are always partitioned
+   exactly in two.) */
+#define STACKSIZE 64
+
+/* Quicksort algorithm.  Return -1 if an exception occurred; in this
+   case we leave the array partly sorted but otherwise in good health
+   (i.e. no items have been removed or duplicated). */
+
+static int
+quicksort(array, size, compare)
+       object **array; /* Start of array to sort */
+       int size;       /* Number of elements to sort */
+       object *compare;/* Comparison function object, or NULL for default */
+{
+       register object **a, *tmp, *pivot;
+       register int n, l, r, k;
+       int top, i, j, n2;
+       object **astack[STACKSIZE];
+       int nstack[STACKSIZE];
+
+       /* Start out with the whole array on the work stack */
+       astack[0] = array;
+       nstack[0] = size;
+       top = 1;
+
+       /* Repeat until the work stack is empty */
+       while (--top >= 0) {
+               a = astack[top];
+               n = nstack[top];
+
+               /* If it's a small one, use straight insertion sort */
+               if (n < MINSIZE) {
+                       if (insertionsort(a, n, compare) < 0)
+                               return -1;
+                       continue;
+               }
+
+               /* Choose median of a[0], a[n/2], a[n-1] as pivot */
+               i = n>>1;
+               j = n-1;
+               k = docompare(a[0], a[i], compare);
+               if (k == CMPERROR)
+                       return -1;
+               if (k < 0)
+                       { tmp = a[0]; a[0] = a[i]; a[i] = tmp; }
+               k = docompare(a[j], a[i], compare);
+               if (k == CMPERROR)
+                       return -1;
+               if (k < 0)
+                       { tmp = a[j]; a[j] = a[i]; a[i] = tmp; }
+               k = docompare(a[j], a[0], compare);
+               if (k == CMPERROR)
+                       return -1;
+               if (k < 0)
+                       { tmp = a[j]; a[j] = a[0]; a[0] = tmp; }
+               pivot = a[0];
+
+               /* Partition the array */
+               l = 0;
+               r = n;
+               for (;;) {
+                       /* Move left index to element > pivot */
+                       while (++l < n) {
+                               k = docompare(a[l], pivot, compare);
+                               if (k == CMPERROR)
+                                       return -1;
+                               if (k > 0)
+                                       break;
+                       }
+                       /* Move right index to element < pivot */
+                       while (--r > 0) {
+                               k = docompare(a[r], pivot, compare);
+                               if (k == CMPERROR)
+                                       return -1;
+                               if (k < 0)
+                                       break;
+                       }
+                       /* If they met, we're through */
+                       if (r < l)
+                               break;
+                       /* Swap elements and continue */
+                       { tmp = a[l]; a[l] = a[r]; a[r] = tmp; }
+               }
+
+               /* Move the pivot into the middle */
+               { tmp = a[0]; a[0] = a[r]; a[r] = tmp; }
+
+               /* We have now reached the following conditions:
+                       0 <= r < l <= n
+                       all x in a[0:r] are <= pivot
+                       all x in a[r:l] are == pivot
+                       all x in a[l:n] are >= pivot
+                  The partitions are a[0:r] and a[l:n]
+                */
+
+               /* Push biggest partition first */
+               n2 = n - l;
+               if (r > n2) {
+                       /* First one is bigger */
+                       if (r > 1) {
+                               astack[top] = a;
+                               nstack[top++] = r;
+                               if (n2 > 1) {
+                                       astack[top] = a+l;
+                                       nstack[top++] = n2;
+                               }
+                       }
+               } else {
+                       /* Second one is bigger */
+                       if (n2 > 1) {
+                               astack[top] = a+l;
+                               nstack[top++] = n2;
+                               if (r > 1) {
+                                       astack[top] = a;
+                                       nstack[top++] = r;
+                               }
+                       }
+               }
+
+               /* Should assert top < STACKSIZE-1 */
+       }
+       
+       /* Succes */
+       return 0;
+}
+
+static object *
+listsort(self, compare)
+       listobject *self;
+       object *compare;
+{
+       /* XXX Don't you *dare* changing the list's length in compare()! */
+       if (quicksort(self->ob_item, self->ob_size, compare) < 0)
+               return NULL;
+       INCREF(None);
+       return None;
+}
+
+#else /* !NEWSORT */
+
 static object *comparefunc;
 
 static int
@@ -617,6 +842,8 @@ listsort(self, args)
        return None;
 }
 
+#endif
+
 static object *
 listreverse(self, args)
        listobject *self;