Index: listobject.c =================================================================== RCS file: /cvsroot/python/python/dist/src/Objects/listobject.c,v retrieving revision 2.127 diff -c -c -r2.127 listobject.c *** listobject.c 19 Jul 2002 07:05:44 -0000 2.127 --- listobject.c 26 Jul 2002 15:37:18 -0000 *************** *** 762,768 **** comparison function (any callable Python object). Calls the standard comparison function, PyObject_RichCompareBool(), if the user- supplied function is NULL. ! Returns <0 on error, >0 if x < y, 0 if x >= y. */ static int islt(PyObject *x, PyObject *y, PyObject *compare) --- 762,768 ---- comparison function (any callable Python object). Calls the standard comparison function, PyObject_RichCompareBool(), if the user- supplied function is NULL. ! Returns -1 on error, 1 if x < y, 0 if x >= y. */ static int islt(PyObject *x, PyObject *y, PyObject *compare) *************** *** 1247,1254 **** --- 1247,2161 ---- return -1; } + /* + Return the length of the run beginning at lo, in the slice [lo, hi). lo < hi + is required on entry. "A run" is the longest ascending sequence, with + + lo[0] <= lo[1] <= lo[2] <= ... + + or the longest descending sequence, with + + lo[0] > lo[1] > lo[2] > ... + + Boolean *descending is set to 0 in the former case, or to 1 in the latter. + For its intended use in a stable mergesort, the strictness of the defn of + "descending" is needed so that the caller can safely reverse a descending + sequence without violating stability (strict > ensures there are no equal + elements to get out of order). + + Returns -1 in case of error. + */ + static int + count_run(PyObject **lo, PyObject **hi, PyObject *compare, int *descending) + { + int k; + int n; + + assert(lo < hi); + *descending = 0; + ++lo; + if (lo == hi) + return 1; + + n = 2; + IFLT(*lo, *(lo-1)) { + *descending = 1; + for (lo = lo+1; lo < hi; ++lo, ++n) { + IFLT(*lo, *(lo-1)) + ; + else + break; + } + } + else { + for (lo = lo+1; lo < hi; ++lo, ++n) { + IFLT(*lo, *(lo-1)) + break; + } + } + + return n; + fail: + return -1; + } + + /* + Locate the proper position of key in a sorted vector; if the vector contains + an element equal to key, return the position immediately to the left of + the leftmost equal element. [gallop_right() does the same except returns + the position to the right of the rightmost equal element (if any).] + + "a" is a sorted vector with n elements, starting at a[0]. n must be > 0. + + "hint" is an index at which to begin the search, 0 <= hint < n. The closer + hint is to the final result, the faster this runs. + + The return vaiue is the int k in 0..n such that + + a[k-1] < key <= a[k] + + pretending that *(a-1) is minus infinity and a[n] is plus infinity. IOW, + key belongs at index k; or, IOW, the first k elements of a should precede + key, and the last n-k should follow key. + + Returns -1 on error. See XXX.txt for info on the method. + */ + static int + gallop_left(PyObject *key, PyObject **a, int n, int hint, PyObject *compare) + { + int ofs; + int lastofs; + int k; + + assert(key && a && n > 0 && hint >= 0 && hint < n); + + a += hint; + lastofs = 0; + ofs = 1; + IFLT(*a, key) { + /* a[hint] < key -- gallop right, until + * a[hint + lastofs] < key <= a[hint + ofs] + */ + const int maxofs = n - hint; /* &a[n-1] is highest */ + while (ofs < maxofs) { + IFLT(a[ofs], key) { + lastofs = ofs; + ofs = (ofs << 1) + 1; + if (ofs <= 0) /* int overflow */ + ofs = maxofs; + } + else /* key <= a[hint + ofs] */ + break; + } + if (ofs > maxofs) + ofs = maxofs; + /* Translate back to offsets relative to &a[0]. */ + lastofs += hint; + ofs += hint; + } + else { + /* key <= a[hint] -- gallop left, until + * a[hint - ofs] < key <= a[hint - lastofs] + */ + const int maxofs = hint + 1; /* &a[0] is lowest */ + while (ofs < maxofs) { + IFLT(*(a-ofs), key) + break; + /* key <= a[hint - ofs] */ + lastofs = ofs; + ofs = (ofs << 1) + 1; + if (ofs <= 0) /* int overflow */ + ofs = maxofs; + } + if (ofs > maxofs) + ofs = maxofs; + /* Translate back to positive offsets relative to &a[0]. */ + k = lastofs; + lastofs = hint - ofs; + ofs = hint - k; + } + a -= hint; + + assert(-1 <= lastofs && lastofs < ofs && ofs <= n); + /* Now a[lastofs] < key <= a[ofs], so key belongs somewhere to the + * right of lastofs but no farther right than ofs. Do a binary + * search, with invariant a[lastofs-1] < key <= a[ofs]. + */ + ++lastofs; + while (lastofs < ofs) { + int m = lastofs + ((ofs - lastofs) >> 1); + + IFLT(a[m], key) + lastofs = m+1; /* a[m] < key */ + else + ofs = m; /* key <= a[m] */ + } + assert(lastofs == ofs); /* so a[ofs-1] < key <= a[ofs] */ + return ofs; + + fail: + return -1; + } + + /* + Exactly like gallop_left(), except that if key already exists in a[0:n], + finds the position immediately to the right of the rightmost equal value. + + The return vaiue is the int k in 0..n such that + + a[k-1] <= key < a[k] + + or -1 if error. + + The code duplication is massive, but this is enough different given that + we're sticking to "<" comparisons that it's much harder to follow if + written as one routine with yet another "left or right?" flag. + */ + static int + gallop_right(PyObject *key, PyObject **a, int n, int hint, PyObject *compare) + { + int ofs; + int lastofs; + int k; + + assert(key && a && n > 0 && hint >= 0 && hint < n); + + a += hint; + lastofs = 0; + ofs = 1; + IFLT(key, *a) { + /* key < a[hint] -- gallop left, until + * a[hint - ofs] <= key < a[hint - lastofs] + */ + const int maxofs = hint + 1; /* &a[0] is lowest */ + while (ofs < maxofs) { + IFLT(key, *(a-ofs)) { + lastofs = ofs; + ofs = (ofs << 1) + 1; + if (ofs <= 0) /* int overflow */ + ofs = maxofs; + } + else /* a[hint - ofs] <= key */ + break; + } + if (ofs > maxofs) + ofs = maxofs; + /* Translate back to positive offsets relative to &a[0]. */ + k = lastofs; + lastofs = hint - ofs; + ofs = hint - k; + } + else { + /* a[hint] <= key -- gallop right, until + * a[hint + lastofs] <= key < a[hint + ofs] + */ + const int maxofs = n - hint; /* &a[n-1] is highest */ + while (ofs < maxofs) { + IFLT(key, a[ofs]) + break; + /* a[hint + ofs] <= key */ + lastofs = ofs; + ofs = (ofs << 1) + 1; + if (ofs <= 0) /* int overflow */ + ofs = maxofs; + } + if (ofs > maxofs) + ofs = maxofs; + /* Translate back to offsets relative to &a[0]. */ + lastofs += hint; + ofs += hint; + } + a -= hint; + + assert(-1 <= lastofs && lastofs < ofs && ofs <= n); + /* Now a[lastofs] <= key < a[ofs], so key belongs somewhere to the + * right of lastofs but no farther right than ofs. Do a binary + * search, with invariant a[lastofs-1] <= key < a[ofs]. + */ + ++lastofs; + while (lastofs < ofs) { + int m = lastofs + ((ofs - lastofs) >> 1); + + IFLT(key, a[m]) + ofs = m; /* key < a[m] */ + else + lastofs = m+1; /* a[m] <= key */ + } + assert(lastofs == ofs); /* so a[ofs-1] <= key < a[ofs] */ + return ofs; + + fail: + return -1; + } + + /* The maximum number of entries in a MergeState's pending-runs stack. + * This is enough to sort arrays of size up to about + * MIN_MERGE_SLICE * phi ** MAX_MERGE_PENDING + * where phi ~= 1.618. Assuming MIN_MERGE_SLICE is 32, 85 is ridiculously + * large enough, good for an array with 2**64 elements. + */ + #define MAX_MERGE_PENDING 85 + + /* If a natural run is smaller than MIN_MERGE_SLICE, a small binary insertion + * sort is done on the following array elements to bring it up to + * MIN_MERGE_SLICE. This "evens out" random data, and amortizes the cost + * of processing a run over a reasonably large worst-case nunmber of + * elements. The exact value here appears unimportant -- trying powers of 2 + * from 16 through 128 had scant measurable effect. Larger than 128, the + & data-movement cost in binary insertion sort started to hurt, and under 16 + * the increase in the number of function calls started to hurt. + */ + #define MIN_MERGE_SLICE 32 + + /* If a run wins MIN_GALLOP times in a row, we switch to galloping mode, + * and stay there until both runs win less often than MIN_GALLOP + * consecutive times. See XXX.txt for more info. + */ + #define MIN_GALLOP 8 + + /* One MergeState exists on the stack per invocation of mergesort. It's just + * a convenient way to pass state around among the helper functions. + */ + #define MERGESTATE_TEMP_SIZE 256 /* avoid malloc for small temps */ + typedef struct { + /* The user-supplied comparison function. or NULL if none given. */ + PyObject *compare; + + /* 'a' is temp storage to help with merges. It contains room for + * alloced entries. + */ + PyObject **a; /* may point to static space temparray below */ + int alloced; + + /* A stack of n pending runs yet to be merged. Run #i starts at + * address base[i] and extends for len[i] elements. It's always + * true (so long as the indices are in bounds) that + * + * base[i] + len[i] == base[i+1] + * + * so we could cut the storage for this, but it's a minor amount + * and keeping all the info explicit simplifies the code. + */ + int n; + PyObject **base[MAX_MERGE_PENDING]; + int len[MAX_MERGE_PENDING]; + + PyObject *temparray[MERGESTATE_TEMP_SIZE]; + } MergeState; + + /* Conceptually a MergeState's constructor. */ + static void + merge_init(MergeState *ms, PyObject *compare) + { + assert(ms != NULL); + ms->compare = compare; + ms->a = ms->temparray; + ms->alloced = MERGESTATE_TEMP_SIZE; + ms->n = 0; + } + + /* Free all the temp memory owned by the MergeState. This must be called + * when you're done with a MergeState, and may be called before then if + * you want to free the temp memory early. + */ + static void + merge_freemem(MergeState *ms) + { + assert(ms != NULL); + if (ms->a != ms->temparray) + PyMem_Free(ms->a); + ms->a = ms->temparray; + ms->alloced = MERGESTATE_TEMP_SIZE; + } + + /* Ensure enough temp memory for 'need' array slots is available. + * Returns 0 on success and -1 if the memory can't be gotten. + */ + static int + merge_getmem(MergeState *ms, int need) + { + assert(ms != NULL); + if (need <= ms->alloced) + return 0; + /* Don't realloc! That can cost cycles to copy the old data + * if the block needs to grow, but we don't care what's in the block. + */ + merge_freemem(ms); + ms->a = (PyObject **)PyMem_Malloc(need * sizeof(PyObject*)); + if (ms->a) { + ms->alloced = need; + return 0; + } + PyErr_NoMemory(); + return -1; + } + #define MERGE_GETMEM(MS, NEED) ((NEED) <= (MS)->alloced ? 0 : \ + merge_getmem(MS, NEED)) + + /* Merge the na elements starting at pa with the nb elements starting at pb + * in a stable way, in-place. na and nb must be > 0, and pa + na == pb. + * Must also have that *pb < *pa, and should have na <= nb. See XXX.txt for + * more info. + * Return 0 if successful, -1 if error. + */ + static int + merge_lo(MergeState *ms, PyObject **pa, int na, PyObject **pb, int nb) + { + int k; + PyObject *compare; + PyObject **dest; + int result = -1; /* guilty until proved innocent */ + + assert(ms && pa && pb && na > 0 && nb > 0 && pa + na == pb); + if (MERGE_GETMEM(ms, na) < 0) + return -1; + compare = ms->compare; + memcpy(ms->a, pa, na * sizeof(PyObject*)); + dest = pa; + pa = ms->a; + *dest++ = *pb++; + --nb; + if (nb == 0) + goto Succeed; + + for (;;) { + int acount; /* # of times A won in a row */ + int bcount; /* # of times B won in a row */ + + /* Do the straightforward thing until (if ever) one run + * appears to win consistently. + */ + acount = bcount = 0; + for (;;) { + k = islt(*pb, *pa, compare); + if (k) { + if (k < 0) + goto Fail; + *dest++ = *pb++; + ++bcount; + acount = 0; + --nb; + if (nb == 0) + goto Succeed; + if (bcount >= MIN_GALLOP) + break; + } + else { + *dest++ = *pa++; + ++acount; + bcount = 0; + --na; + if (na == 0) + goto Succeed; + if (acount >= MIN_GALLOP) + break; + } + } + + /* One run is winning so consistently that galloping may + * be a huge win. So try that, and continue galloping until + * (if ever) neither run appears to be winning consistently + * anymore. + */ + do { + k = gallop_right(*pb, pa, na, 0, compare); + acount = k; + if (k) { + if (k < 0) + goto Fail; + memcpy(dest, pa, k * sizeof(PyObject *)); + dest += k; + pa += k; + na -= k; + if (na == 0) + goto Succeed; + } + *dest++ = *pb++; + --nb; + if (nb == 0) + goto Succeed; + + k = gallop_left(*pa, pb, nb, 0, compare); + bcount = k; + if (k) { + if (k < 0) + goto Fail; + memmove(dest, pb, k * sizeof(PyObject *)); + dest += k; + pb += k; + nb -= k; + if (nb == 0) + goto Succeed; + } + *dest++ = *pa++; + --na; + if (na == 0) + goto Succeed; + } while (acount >= MIN_GALLOP || bcount >= MIN_GALLOP); + } + Succeed: + result = 0; + Fail: + if (na) + memcpy(dest, pa, na * sizeof(PyObject*)); + return result; + } + + /* Merge the na elements starting at pa with the nb elements starting at pb + * in a stable way, in-place. na and nb must be > 0, and pa + na == pb. + * Must also have that *pa <= *pb, and should have nb <= na. See XXX.txt for + * more info. + * Return 0 if successful, -1 if error. + */ + static int + merge_hi(MergeState *ms, PyObject **pa, int na, PyObject **pb, int nb) + { + int k; + PyObject *compare; + PyObject **dest; + int result = -1; /* guilty until proved innocent */ + PyObject **basea; + PyObject **baseb; + + assert(ms && pa && pb && na > 0 && nb > 0 && pa + na == pb); + if (MERGE_GETMEM(ms, nb) < 0) + return -1; + compare = ms->compare; + + dest = pb + nb - 1; + memcpy(ms->a, pb, nb * sizeof(PyObject*)); + basea = pa; + baseb = ms->a; + pb = ms->a + nb - 1; + pa += na - 1; + + *dest-- = *pa--; + --na; + if (na == 0) + goto Succeed; + + for (;;) { + int acount; /* # of times A won in a row */ + int bcount; /* # of times B won in a row */ + + /* Do the straightforward thing until (if ever) one run + * appears to win consistently. + */ + acount = bcount = 0; + for (;;) { + k = islt(*pb, *pa, compare); + if (k) { + if (k < 0) + goto Fail; + *dest-- = *pa--; + ++acount; + bcount = 0; + --na; + if (na == 0) + goto Succeed; + if (acount >= MIN_GALLOP) + break; + } + else { + *dest-- = *pb--; + ++bcount; + acount = 0; + --nb; + if (nb == 0) + goto Succeed; + if (bcount >= MIN_GALLOP) + break; + } + } + + /* One run is winning so consistently that galloping may + * be a huge win. So try that, and continue galloping until + * (if ever) neither run appears to be winning consistently + * anymore. + */ + do { + k = gallop_right(*pb, basea, na, na-1, compare); + if (k < 0) + goto Fail; + k = na - k; + acount = k; + if (k) { + dest -= k; + pa -= k; + memmove(dest+1, pa+1, k * sizeof(PyObject *)); + na -= k; + if (na == 0) + goto Succeed; + } + *dest-- = *pb--; + --nb; + if (nb == 0) + goto Succeed; + + k = gallop_left(*pa, baseb, nb, nb-1, compare); + if (k < 0) + goto Fail; + k = nb - k; + bcount = k; + if (k) { + dest -= k; + pb -= k; + memcpy(dest+1, pb+1, k * sizeof(PyObject *)); + nb -= k; + if (nb == 0) + goto Succeed; + } + *dest-- = *pa--; + --na; + if (na == 0) + goto Succeed; + } while (acount >= MIN_GALLOP || bcount >= MIN_GALLOP); + } + Succeed: + result = 0; + Fail: + if (nb) + memcpy(dest-(nb-1), baseb, nb * sizeof(PyObject*)); + return result; + } + + /* Merge the two runs at stack indices i and i+1. + * Returns 0 on success, -1 on error. + */ + static int + merge_at(MergeState *ms, int i) + { + PyObject **pa, **pb; + int na, nb; + int k; + PyObject *compare; + + assert(ms != NULL); + assert(ms->n >= 2); + assert(i >= 0); + assert(i == ms->n - 2 || i == ms->n - 3); + + pa = ms->base[i]; + pb = ms->base[i+1]; + na = ms->len[i]; + nb = ms->len[i+1]; + assert(na > 0 && nb > 0); + assert(pa + na == pb); + + /* Record the length of the combined runs; if i is the 3rd-last + * run now, also slide over the last run (which isn't involved + * in this merge). The current run i+1 goes away in any case. + */ + if (i == ms->n - 3) { + ms->len[i+1] = ms->len[i+2]; + ms->base[i+1] = ms->base[i+2]; + } + ms->len[i] = na + nb; + --ms->n; + compare = ms->compare; + + /* Where does b start in a? Elements in a before that can be + * ignored (already in place(. + */ + k = gallop_right(*pb, pa, na, 0, compare); + if (k < 0) + return -1; + pa += k; + na -= k; + if (na == 0) + return 0; + + /* Where does a end in b? Elements in b after that can be + * ignored (already in place). + */ + nb = gallop_left(pa[na-1], pb, nb, nb-1, compare); + if (nb < 0) + return -1; + if (nb == 0) + return 0; + + /* Merge what remains of the runs, using a temp array with + * min(na, nb) elements. + */ + if (na <= nb) + return merge_lo(ms, pa, na, pb, nb); + else + return merge_hi(ms, pa, na, pb, nb); + } + + /* Examine the stack of runs waiting to be merged, merging adjacent runs + * until the stack invariants are re-established: + * + * 1. len[-3] > len[-2] + len[-1] + * 2. len[-2] > len[-1] + * + * See XXX.txt for more info. + * + * Returns 0 on success, -1 on error. + */ + static int + merge_collapse(MergeState *ms) + { + while (ms->n > 1) { + int n = ms->n - 2; + if (n > 0 && ms->len[n-1] <= ms->len[n] + ms->len[n+1]) { + if (ms->len[n-1] < ms->len[n+1]) + --n; + if (merge_at(ms, n) < 0) + return -1; + } + else if (ms->len[n] <= ms->len[n+1]) { + if (merge_at(ms, n) < 0) + return -1; + } + else + break; + } + return 0; + } + + /* Regardless of invariants, merge all runs on the stack until only one + * remains. This is used at the end of the mergesort. + * + * Returns 0 on success, -1 on error. + */ + static int + merge_force_collapse(MergeState *ms) + { + while (ms->n > 1) { + int n = ms->n - 2; + if (n > 0 && ms->len[n-1] < ms->len[n+1]) + --n; + if (merge_at(ms, n) < 0) + return -1; + } + return 0; + } + static PyTypeObject immutable_list_type; + /* A stable, adaptive mergesort. See XXX.txt for more info. */ + static PyObject * + listmsort(PyListObject *self, PyObject *args) + { + MergeState ms; + PyObject **lo, **hi; + int nremaining; + PyTypeObject *savetype; + PyObject *compare = NULL; + PyObject *result = NULL; + + assert(self != NULL); + if (args != NULL) { + if (!PyArg_ParseTuple(args, "|O:msort", &compare)) + return NULL; + } + merge_init(&ms, compare); + + savetype = self->ob_type; + self->ob_type = &immutable_list_type; + + nremaining = self->ob_size; + if (nremaining < 2) + goto succeed; + + lo = self->ob_item; + hi = lo + nremaining; + do { + int descending; + int n; + + n = count_run(lo, hi, compare, &descending); + if (n < 0) + goto fail; + if (descending) + reverse_slice(lo, lo + n); + if (n < MIN_MERGE_SLICE) { + const int force = nremaining < MIN_MERGE_SLICE ? + nremaining : + MIN_MERGE_SLICE; + if (binarysort(lo, lo + force, lo + n, compare) < 0) + goto fail; + n = force; + } + assert(ms.n < MAX_MERGE_PENDING); + ms.base[ms.n] = lo; + ms.len[ms.n] = n; + ++ms.n; + lo += n; + nremaining -= n; + if (merge_collapse(&ms) < 0) + goto fail; + } while (nremaining); + + if (merge_force_collapse(&ms) < 0) + goto fail; + + succeed: + result = Py_None; + fail: + self->ob_type = savetype; + merge_freemem(&ms); + Py_XINCREF(result); + return result; + } + + /* + def gparent(i): + while (i & 1) == 0: + i >>= 1 + return i >> 1 + */ + static int + heap_gparent(int i) + { + while ((i & 1) == 0) + i >>= 1; + return i >> 1; + } + + /* + def merge(i, j): + if a[i] < a[j]: + a[i], a[j] = a[j], a[i] + reversed[j] = not reversed[j] + */ + #define REVBIT(I) ((reversed[(I) >> 3] >> ((I) & 0x7)) & 1) + #define FLIPREVBIT(I) reversed[(I) >> 3] ^= 1 << ((I) & 0x7) + + #define HEAPMERGE(I, J) \ + do { \ + int _i = (I); \ + int _j = (J); \ + IFLT(a[_i], a[_j]) { \ + PyObject *_t = a[_i]; \ + a[_i] = a[_j]; \ + a[_j] = _t; \ + FLIPREVBIT(_j); \ + } \ + } while (0) + + /* + def mergeforest(m): + x = 1 + while 1: + kid = (x << 1) + reversed[x] + if kid >= m: + break + x = kid + if x == m-1 and x & m > x ^ m: + x >>= 1 + while x: + merge(m-1, x) + x >>= 1 + a[m], a[m-1] = a[m-1], a[m] + else: + while x: + merge(m, x) + x >>= 1 + */ + static int + heap_mergeforest(PyObject **a, int m, unsigned char *reversed, PyObject *compare) + { + int k; + int x = 1; + for (;;) { + int kid = (x << 1) + REVBIT(x); + if (kid >= m) + break; + x = kid; + } + if (x == m-1 && (x & m) > (x ^ m)) { + PyObject *t; + x >>= 1; + while (x) { + HEAPMERGE(m-1, x); + x >>= 1; + } + t = a[m]; + a[m] = a[m-1]; + a[m-1] = t; + } + else { + while (x) { + HEAPMERGE(m, x); + x >>= 1; + } + } + return 0; + fail: + return -1; + } + + + /* + def weakheapify(): + global reversed + reversed = [False] * len(a) + for i in xrange(len(a)-1, 0, -1): + merge(gparent(i), i) + + def weakheapsort(): + weakheapify() + biggest = a[0] + for i in xrange(len(a)-1, 1, -1): + mergeforest(i) + # shift left 1 + a[:len(a)-1] = a[1:] + a[-1] = biggest + */ + + static PyObject * + listhsort(PyObject *v) + { + unsigned char *reversed; + PyObject *compare = NULL; + PyObject *biggest; + int i; + int k; + PyObject *result = NULL; + PyListObject *p = (PyListObject *)v; + PyObject **a; + + if (v == NULL || !PyList_Check(v)) { + PyErr_BadInternalCall(); + return NULL; + } + if (p->ob_size < 2) { + Py_INCREF(Py_None); + return Py_None; + } + a = p->ob_item; + + /* Allocate enough space for 1 bit per array element. */ + reversed = (unsigned char*)PyObject_Malloc((p->ob_size + 7) >> 3); + if (reversed == NULL) + return NULL; + memset(reversed, 0, (p->ob_size + 7) >> 3); + + /* Transform into a weak heap. */ + for (i = p->ob_size - 1; i > 0; --i) + HEAPMERGE(heap_gparent(i), i); + + /* Sort. */ + biggest = a[0]; + for (i = p->ob_size - 1; i > 1; --i) { + if (heap_mergeforest(a, i, reversed, compare) < 0) + goto fail; + } + + /* Shift left 1. */ + memmove(a, a+1, (p->ob_size - 1) * sizeof(PyObject *)); + a[p->ob_size - 1] = biggest; + + result = Py_None; + + fail: + PyObject_Free(reversed); + Py_XINCREF(result); + return result; + } + static PyObject * listsort(PyListObject *self, PyObject *args) { *************** *** 1316,1322 **** return NULL; } ! #undef IFLT int PyList_Sort(PyObject *v) --- 2223,2229 ---- return NULL; } ! #undef ISLT int PyList_Sort(PyObject *v) *************** *** 1657,1662 **** --- 2564,2571 ---- {"count", (PyCFunction)listcount, METH_O, count_doc}, {"reverse", (PyCFunction)listreverse, METH_NOARGS, reverse_doc}, {"sort", (PyCFunction)listsort, METH_VARARGS, sort_doc}, + {"msort", (PyCFunction)listmsort, METH_VARARGS}, + {"hsort", (PyCFunction)listhsort, METH_NOARGS}, {NULL, NULL} /* sentinel */ };