From 6578b939043973526898bafdb1cf83e06127128b Mon Sep 17 00:00:00 2001 From: Chris Jefferson Date: Tue, 9 Feb 2016 19:52:39 +0000 Subject: [PATCH 1/3] Separate out sorting --- lib/basis.gd | 2 - src/listfunc.c | 407 +++++++++++++++---------------------------------- src/sortbase.h | 66 ++++++++ 3 files changed, 187 insertions(+), 288 deletions(-) create mode 100644 src/sortbase.h diff --git a/lib/basis.gd b/lib/basis.gd index 54d72a3b04..57fd8785ce 100644 --- a/lib/basis.gd +++ b/lib/basis.gd @@ -896,7 +896,6 @@ InstallGlobalFunction( "DeclareHandlingByNiceBasis", function( name, info ) od; end ); - ############################################################################# ## #F IsGenericFiniteSpace( ) @@ -1187,4 +1186,3 @@ DeclareGlobalFunction( "BasisWithReplacedLeftModule" ); ############################################################################# ## #E - diff --git a/src/listfunc.c b/src/listfunc.c index 55781cb6ef..f99e8121d7 100644 --- a/src/listfunc.c +++ b/src/listfunc.c @@ -688,67 +688,33 @@ Obj HEAP_SORT_PLIST ( Obj self, Obj list ) } return (Obj) 0; } - -void SORT_LIST ( - Obj list ) -{ - UInt len; /* length of the list */ - UInt h; /* gap width in the shellsort */ - Obj v, w; /* two element of the list */ - UInt i, k; /* loop variables */ - - /* sort the list with a shellsort */ - len = LEN_LIST( list ); - h = 1; - while ( 9*h + 4 < len ) { h = 3*h + 1; } - while ( 0 < h ) { - for ( i = h+1; i <= len; i++ ) { - v = ELMV_LIST( list, i ); - k = i; - w = ELMV_LIST( list, k-h ); - while ( h < k && LT( v, w ) ) { - ASS_LIST( list, k, w ); - k -= h; - if ( h < k ) w = ELMV_LIST( list, k-h ); - } - ASS_LIST( list, k, v ); - } - h = h / 3; - } - if (IS_PLIST(list)) - RESET_FILT_LIST(list, FN_IS_NSORT); -} +// See sortbase.h for a description of these macros. + +#define SORT_FUNC_NAME SORT_LIST +#define SORT_FUNC_ARGS Obj list +#define SORT_CREATE_TEMP(name) Obj name ; +#define SORT_LEN_LIST() LEN_LIST(list) +#define SORT_ASS_LIST_TO_TEMP(t, i) t = ELMV_LIST(list, i) +#define SORT_ASS_TEMP_TO_LIST(i, j) ASS_LIST(list, i, j) +#define SORT_COMP(v, w) LT(v, w) +#define SORT_FILTER_CHECKS() \ + if(IS_PLIST(list)) \ + RESET_FILT_LIST(list, FN_IS_NSORT); -void SortDensePlist ( - Obj list ) -{ - UInt len; /* length of the list */ - UInt h; /* gap width in the shellsort */ - Obj v, w; /* two element of the list */ - UInt i, k; /* loop variables */ +#include "sortbase.h" - /* sort the list with a shellsort */ - len = LEN_PLIST( list ); - h = 1; - while ( 9*h + 4 < len ) { h = 3*h + 1; } - while ( 0 < h ) { - for ( i = h+1; i <= len; i++ ) { - v = ELM_PLIST( list, i ); - k = i; - w = ELM_PLIST( list, k-h ); - while ( h < k && LT( v, w ) ) { - SET_ELM_PLIST( list, k, w ); - k -= h; - if ( h < k ) w = ELM_PLIST( list, k-h ); - } - SET_ELM_PLIST( list, k, v ); - } - h = h / 3; - } - RESET_FILT_LIST(list, FN_IS_NSORT); -} +#define SORT_FUNC_NAME SortDensePlist +#define SORT_FUNC_ARGS Obj list +#define SORT_CREATE_TEMP(name) Obj name ; +#define SORT_LEN_LIST() LEN_PLIST(list) +#define SORT_ASS_LIST_TO_TEMP(t, i) t = ELM_PLIST(list, i) +#define SORT_ASS_TEMP_TO_LIST(i, j) SET_ELM_PLIST(list, i, j) +#define SORT_COMP(v, w) LT(v, w) +#define SORT_FILTER_CHECKS() \ + RESET_FILT_LIST(list, FN_IS_NSORT); +#include "sortbase.h" /**************************************************************************** ** @@ -758,74 +724,33 @@ void SortDensePlist ( ** 'SORT_LISTComp' sorts the list in increasing order, with respect to ** comparison function . */ -void SORT_LISTComp ( - Obj list, - Obj func ) -{ - UInt len; /* length of the list */ - UInt h; /* gap width in the shellsort */ - Obj v, w; /* two element of the list */ - UInt i, k; /* loop variables */ - - /* sort the list with a shellsort */ - len = LEN_LIST( list ); - h = 1; - while ( 9*h + 4 < len ) { h = 3*h + 1; } - while ( 0 < h ) { - for ( i = h+1; i <= len; i++ ) { - v = ELMV_LIST( list, i ); - k = i; - w = ELMV_LIST( list, k-h ); - while ( h < k && CALL_2ARGS( func, v, w ) == True ) { - ASS_LIST( list, k, w ); - k -= h; - if ( h < k ) w = ELMV_LIST( list, k-h ); - } - ASS_LIST( list, k, v ); - } - h = h / 3; - } - /* list is not necc. sorted wrt. \< (any longer) */ - RESET_FILT_LIST(list, FN_IS_SSORT); - RESET_FILT_LIST(list, FN_IS_NSORT); -} - - - - - - -void SortDensePlistComp ( - Obj list, - Obj func ) -{ - UInt len; /* length of the list */ - UInt h; /* gap width in the shellsort */ - Obj v, w; /* two element of the list */ - UInt i, k; /* loop variables */ - - /* sort the list with a shellsort */ - len = LEN_PLIST( list ); - h = 1; - while ( 9*h + 4 < len ) { h = 3*h + 1; } - while ( 0 < h ) { - for ( i = h+1; i <= len; i++ ) { - v = ELM_PLIST( list, i ); - k = i; - w = ELM_PLIST( list, k-h ); - while ( h < k && CALL_2ARGS( func, v, w ) == True ) { - SET_ELM_PLIST( list, k, w ); - k -= h; - if ( h < k ) w = ELM_PLIST( list, k-h ); - } - SET_ELM_PLIST( list, k, v ); - } - h = h / 3; - } - /* list is not necc. sorted wrt. \< (any longer) */ - RESET_FILT_LIST(list, FN_IS_SSORT); - RESET_FILT_LIST(list, FN_IS_NSORT); -} +#define SORT_FUNC_NAME SORT_LISTComp +#define SORT_FUNC_ARGS Obj list, Obj func +#define SORT_CREATE_TEMP(name) Obj name ; +#define SORT_LEN_LIST() LEN_LIST(list) +#define SORT_ASS_LIST_TO_TEMP(t, i) t = ELMV_LIST(list, i) +#define SORT_ASS_TEMP_TO_LIST(i, j) ASS_LIST(list, i, j) +#define SORT_COMP(v, w) CALL_2ARGS(func, v, w) == True +/* list is not necc. sorted wrt. \< (any longer) */ +#define SORT_FILTER_CHECKS() \ + RESET_FILT_LIST(list, FN_IS_SSORT); \ + RESET_FILT_LIST(list, FN_IS_NSORT); + +#include "sortbase.h" + +#define SORT_FUNC_NAME SortDensePlistComp +#define SORT_FUNC_ARGS Obj list, Obj func +#define SORT_CREATE_TEMP(name) Obj name ; +#define SORT_LEN_LIST() LEN_PLIST(list) +#define SORT_ASS_LIST_TO_TEMP(t, i) t = ELM_PLIST(list, i) +#define SORT_ASS_TEMP_TO_LIST(i, j) SET_ELM_PLIST(list, i, j) +#define SORT_COMP(v, w) CALL_2ARGS(func, v, w) == True +/* list is not necc. sorted wrt. \< (any longer) */ +#define SORT_FILTER_CHECKS() \ + RESET_FILT_LIST(list, FN_IS_SSORT); \ + RESET_FILT_LIST(list, FN_IS_NSORT); + +#include "sortbase.h" /**************************************************************************** ** @@ -843,175 +768,85 @@ void SortDensePlistComp ( ** the second list added in. */ -void SORT_PARA_LIST ( - Obj list, - Obj shadow ) -{ - UInt len; /* length of the list */ - UInt h; /* gap width in the shellsort */ - Obj v, w; /* two element of the list */ - Obj vs, ws; /* two element of the shadow list */ - UInt i, k; /* loop variables */ - - /* sort the list with a shellsort */ - len = LEN_LIST( list ); - h = 1; - while ( 9*h + 4 < len ) { h = 3*h + 1; } - while ( 0 < h ) { - for ( i = h+1; i <= len; i++ ) { - v = ELMV_LIST( list, i ); - vs = ELMV_LIST( shadow, i ); - k = i; - w = ELMV_LIST( list, k-h ); - ws = ELMV_LIST( shadow, k-h ); - while ( h < k && LT( v, w ) ) { - ASS_LIST( list, k, w ); - ASS_LIST( shadow, k, ws ); - k -= h; - if ( h < k ) { - w = ELMV_LIST( list, k-h ); - ws = ELMV_LIST( shadow, k-h ); - } - } - ASS_LIST( list, k, v ); - ASS_LIST( shadow, k, vs ); - } - h = h / 3; - } +#define SORT_FUNC_NAME SORT_PARA_LIST +#define SORT_FUNC_ARGS Obj list, Obj shadow +#define SORT_CREATE_TEMP(name) Obj name ; Obj name##s ; +#define SORT_LEN_LIST() LEN_LIST(list) +#define SORT_ASS_LIST_TO_TEMP(t, i) \ + t = ELMV_LIST(list, i); \ + t##s = ELMV_LIST(shadow, i); +#define SORT_ASS_TEMP_TO_LIST(i, t) \ + ASS_LIST(list, i, t); \ + ASS_LIST(shadow, i, t##s); +#define SORT_COMP(v, w) LT( v, w ) /* if list was ssorted, then it still will be, but, we don't know anything else any more */ - RESET_FILT_LIST(list, FN_IS_NSORT); - RESET_FILT_LIST(shadow, FN_IS_SSORT); - RESET_FILT_LIST(shadow, FN_IS_NSORT); -} - -void SortParaDensePlist ( - Obj list, - Obj shadow ) -{ - UInt len; /* length of the list */ - UInt h; /* gap width in the shellsort */ - Obj v, w; /* two element of the list */ - Obj vs, ws; /* two element of the shadow list */ - UInt i, k; /* loop variables */ - - /* sort the list with a shellsort */ - len = LEN_PLIST( list ); - h = 1; - while ( 9*h + 4 < len ) { h = 3*h + 1; } - while ( 0 < h ) { - for ( i = h+1; i <= len; i++ ) { - v = ELM_PLIST( list, i ); - vs = ELM_PLIST( shadow, i ); - k = i; - w = ELM_PLIST( list, k-h ); - ws = ELM_PLIST( shadow, k-h ); - while ( h < k && LT( v, w ) ) { - SET_ELM_PLIST( list, k, w ); - SET_ELM_PLIST( shadow, k, ws ); - k -= h; - if ( h < k ) { - w = ELM_PLIST( list, k-h ); - ws = ELM_PLIST( shadow, k-h ); - } - } - SET_ELM_PLIST( list, k, v ); - SET_ELM_PLIST( shadow, k, vs ); - } - h = h / 3; - } - +#define SORT_FILTER_CHECKS() \ + RESET_FILT_LIST(list, FN_IS_NSORT); \ + RESET_FILT_LIST(shadow, FN_IS_SSORT); \ + RESET_FILT_LIST(shadow, FN_IS_NSORT); + +#include "sortbase.h" + +#define SORT_FUNC_NAME SortParaDensePlist +#define SORT_FUNC_ARGS Obj list, Obj shadow +#define SORT_CREATE_TEMP(name) Obj name ; Obj name##s ; +#define SORT_LEN_LIST() LEN_PLIST(list) +#define SORT_ASS_LIST_TO_TEMP(t, i) \ + t = ELM_PLIST(list, i); \ + t##s = ELM_PLIST(shadow, i); +#define SORT_ASS_TEMP_TO_LIST(i, t) \ + SET_ELM_PLIST(list, i, t); \ + SET_ELM_PLIST(shadow, i, t##s); +#define SORT_COMP(v, w) LT( v, w ) /* if list was ssorted, then it still will be, but, we don't know anything else any more */ - RESET_FILT_LIST(list, FN_IS_NSORT); +#define SORT_FILTER_CHECKS() \ + RESET_FILT_LIST(list, FN_IS_NSORT); \ + RESET_FILT_LIST(shadow, FN_IS_SSORT); \ + RESET_FILT_LIST(shadow, FN_IS_NSORT); + +#include "sortbase.h" + +#define SORT_FUNC_NAME SORT_PARA_LISTComp +#define SORT_FUNC_ARGS Obj list, Obj shadow, Obj func +#define SORT_CREATE_TEMP(name) Obj name ; Obj name##s ; +#define SORT_LEN_LIST() LEN_LIST(list) +#define SORT_ASS_LIST_TO_TEMP(t, i) \ + t = ELMV_LIST(list, i); \ + t##s = ELMV_LIST(shadow, i); +#define SORT_ASS_TEMP_TO_LIST(i, t) \ + ASS_LIST(list, i, t); \ + ASS_LIST(shadow, i, t##s); +#define SORT_COMP(v, w) CALL_2ARGS( func, v, w ) == True +/* list is not necc. sorted wrt. \< (any longer) */ +#define SORT_FILTER_CHECKS() \ + RESET_FILT_LIST(list, FN_IS_SSORT); \ + RESET_FILT_LIST(list, FN_IS_NSORT); \ + RESET_FILT_LIST(shadow, FN_IS_NSORT); \ RESET_FILT_LIST(shadow, FN_IS_SSORT); - RESET_FILT_LIST(shadow, FN_IS_NSORT); -} -void SORT_PARA_LISTComp ( - Obj list, - Obj shadow, - Obj func ) -{ - UInt len; /* length of the list */ - UInt h; /* gap width in the shellsort */ - Obj v, w; /* two element of the list */ - Obj vs, ws; /* two element of the shadow list */ - UInt i, k; /* loop variables */ - - /* sort the list with a shellsort */ - len = LEN_LIST( list ); - h = 1; - while ( 9*h + 4 < len ) { h = 3*h + 1; } - while ( 0 < h ) { - for ( i = h+1; i <= len; i++ ) { - v = ELMV_LIST( list, i ); - vs = ELMV_LIST( shadow, i ); - k = i; - w = ELMV_LIST( list, k-h ); - ws = ELMV_LIST( shadow, k-h ); - while ( h < k && CALL_2ARGS( func, v, w ) == True ) { - ASS_LIST( list, k, w ); - ASS_LIST( shadow, k, ws ); - k -= h; - if ( h < k ) { - w = ELMV_LIST( list, k-h ); - ws = ELMV_LIST( shadow, k-h ); - } - } - ASS_LIST( list, k, v ); - ASS_LIST( shadow, k, vs ); - } - h = h / 3; - } - /* list is not necc. sorted wrt. \< (any longer) */ - RESET_FILT_LIST(list, FN_IS_SSORT); - RESET_FILT_LIST(list, FN_IS_NSORT); - RESET_FILT_LIST(shadow, FN_IS_NSORT); +#include "sortbase.h" + +#define SORT_FUNC_NAME SortParaDensePlistComp +#define SORT_FUNC_ARGS Obj list, Obj shadow, Obj func +#define SORT_CREATE_TEMP(name) Obj name ; Obj name##s ; +#define SORT_LEN_LIST() LEN_PLIST(list) +#define SORT_ASS_LIST_TO_TEMP(t, i) \ + t = ELM_PLIST(list, i); \ + t##s = ELM_PLIST(shadow, i); +#define SORT_ASS_TEMP_TO_LIST(i, t) \ + SET_ELM_PLIST(list, i, t); \ + SET_ELM_PLIST(shadow, i, t##s); +#define SORT_COMP(v, w) CALL_2ARGS( func, v, w ) == True +/* list is not necc. sorted wrt. \< (any longer) */ +#define SORT_FILTER_CHECKS() \ + RESET_FILT_LIST(list, FN_IS_SSORT); \ + RESET_FILT_LIST(list, FN_IS_NSORT); \ + RESET_FILT_LIST(shadow, FN_IS_NSORT); \ RESET_FILT_LIST(shadow, FN_IS_SSORT); -} -void SortParaDensePlistComp ( - Obj list, - Obj shadow, - Obj func ) -{ - UInt len; /* length of the list */ - UInt h; /* gap width in the shellsort */ - Obj v, w; /* two element of the list */ - Obj vs, ws; /* two element of the shadow list */ - UInt i, k; /* loop variables */ - - /* sort the list with a shellsort */ - len = LEN_PLIST( list ); - h = 1; - while ( 9*h + 4 < len ) { h = 3*h + 1; } - while ( 0 < h ) { - for ( i = h+1; i <= len; i++ ) { - v = ELM_PLIST( list, i ); - vs = ELM_PLIST( shadow, i ); - k = i; - w = ELM_PLIST( list, k-h ); - ws = ELM_PLIST( shadow, k-h ); - while ( h < k && CALL_2ARGS( func, v, w ) == True ) { - SET_ELM_PLIST( list, k, w ); - SET_ELM_PLIST( shadow, k, ws ); - k -= h; - if ( h < k ) { - w = ELM_PLIST( list, k-h ); - ws = ELM_PLIST( shadow, k-h ); - } - } - SET_ELM_PLIST( list, k, v ); - SET_ELM_PLIST( shadow, k, vs ); - } - h = h / 3; - } - RESET_FILT_LIST(list, FN_IS_NSORT); - RESET_FILT_LIST(list, FN_IS_SSORT); - RESET_FILT_LIST(shadow, FN_IS_NSORT); - RESET_FILT_LIST(shadow, FN_IS_SSORT); -} +#include "sortbase.h" diff --git a/src/sortbase.h b/src/sortbase.h new file mode 100644 index 0000000000..b23aea8181 --- /dev/null +++ b/src/sortbase.h @@ -0,0 +1,66 @@ +/*************************************************************************** +** +*W sortbase.h GAP source +** +*Y Copyright (C) 2015 The GAP Group +** +** WARNING: This file should NOT be directly included. It is designed +** to build all of the sort variants which GAP uses. +** +** +** This file provides a framework for expressing sort functions in a generic +** way, covering various options (provide comparator, optimised for Plists, +** and do SortParallel +** +** The following macros are used: +** SORT_FUNC_NAME : Name of function +** SORT_FUNC_ARGS : Arguments of function +** SORT_CREATE_TEMP(t) : Create a temp variable named t that can store +** an element of the list +** SORT_LEN_LIST : Get the length of the list to be sorted +** SORT_ASS_LIST_TO_TEMP(t,i) : Copy list element 'i' to temporary 't' +** SORT_ASS_TEMP_TO_LIST(i,t) : Copy temporary 't' to list element 'i' +** SORT_COMP(v,w) : Compare temporaries v and w +** SORT_FILTER_CHECKS : Arbitary code to be called at end of function, +** to fix filters effected by the sorting. +*/ + +void SORT_FUNC_NAME(SORT_FUNC_ARGS) +{ + UInt len; /* length of the list */ + UInt h; /* gap width in the shellsort */ + SORT_CREATE_TEMP(v); + SORT_CREATE_TEMP(w); + UInt i, k; /* loop variables */ + + /* sort the list with a shellsort */ + len = SORT_LEN_LIST(); + h = 1; + while ( 9*h + 4 < len ) { h = 3*h + 1; } + while ( 0 < h ) { + for ( i = h+1; i <= len; i++ ) { + SORT_ASS_LIST_TO_TEMP( v, i ); + k = i; + SORT_ASS_LIST_TO_TEMP( w, k-h ); + while ( h < k && SORT_COMP( v, w ) ) { + SORT_ASS_TEMP_TO_LIST( k, w ); + k -= h; + if ( h < k ) { + SORT_ASS_LIST_TO_TEMP( w, k-h ); + } + } + SORT_ASS_TEMP_TO_LIST( k, v ); + } + h = h / 3; + } + SORT_FILTER_CHECKS(); +} + +#undef SORT_FUNC_NAME +#undef SORT_FUNC_ARGS +#undef SORT_CREATE_TEMP +#undef SORT_LEN_LIST +#undef SORT_ASS_LIST_TO_TEMP +#undef SORT_ASS_TEMP_TO_LIST +#undef SORT_COMP +#undef SORT_FILTER_CHECKS From 25cfbbe55d23227c13b122c07aa5ea77d4114935 Mon Sep 17 00:00:00 2001 From: Chris Jefferson Date: Wed, 10 Feb 2016 07:37:31 +0000 Subject: [PATCH 2/3] Add pdqsort --- src/listfunc.c | 56 ++++---- src/sortbase.h | 277 +++++++++++++++++++++++++++++++++++--- tst/teststandard/sort.tst | 100 ++++++++++++++ 3 files changed, 390 insertions(+), 43 deletions(-) create mode 100644 tst/teststandard/sort.tst diff --git a/src/listfunc.c b/src/listfunc.c index f99e8121d7..7e54df7dee 100644 --- a/src/listfunc.c +++ b/src/listfunc.c @@ -693,10 +693,11 @@ Obj HEAP_SORT_PLIST ( Obj self, Obj list ) #define SORT_FUNC_NAME SORT_LIST #define SORT_FUNC_ARGS Obj list -#define SORT_CREATE_TEMP(name) Obj name ; +#define SORT_ARGS list +#define SORT_CREATE_LOCAL(name) Obj name ; #define SORT_LEN_LIST() LEN_LIST(list) -#define SORT_ASS_LIST_TO_TEMP(t, i) t = ELMV_LIST(list, i) -#define SORT_ASS_TEMP_TO_LIST(i, j) ASS_LIST(list, i, j) +#define SORT_ASS_LIST_TO_LOCAL(t, i) t = ELMV_LIST(list, i) +#define SORT_ASS_LOCAL_TO_LIST(i, j) ASS_LIST(list, i, j) #define SORT_COMP(v, w) LT(v, w) #define SORT_FILTER_CHECKS() \ if(IS_PLIST(list)) \ @@ -706,10 +707,11 @@ Obj HEAP_SORT_PLIST ( Obj self, Obj list ) #define SORT_FUNC_NAME SortDensePlist #define SORT_FUNC_ARGS Obj list -#define SORT_CREATE_TEMP(name) Obj name ; +#define SORT_ARGS list +#define SORT_CREATE_LOCAL(name) Obj name ; #define SORT_LEN_LIST() LEN_PLIST(list) -#define SORT_ASS_LIST_TO_TEMP(t, i) t = ELM_PLIST(list, i) -#define SORT_ASS_TEMP_TO_LIST(i, j) SET_ELM_PLIST(list, i, j) +#define SORT_ASS_LIST_TO_LOCAL(t, i) t = ELM_PLIST(list, i) +#define SORT_ASS_LOCAL_TO_LIST(i, j) SET_ELM_PLIST(list, i, j) #define SORT_COMP(v, w) LT(v, w) #define SORT_FILTER_CHECKS() \ RESET_FILT_LIST(list, FN_IS_NSORT); @@ -726,10 +728,11 @@ Obj HEAP_SORT_PLIST ( Obj self, Obj list ) */ #define SORT_FUNC_NAME SORT_LISTComp #define SORT_FUNC_ARGS Obj list, Obj func -#define SORT_CREATE_TEMP(name) Obj name ; +#define SORT_ARGS list, func +#define SORT_CREATE_LOCAL(name) Obj name ; #define SORT_LEN_LIST() LEN_LIST(list) -#define SORT_ASS_LIST_TO_TEMP(t, i) t = ELMV_LIST(list, i) -#define SORT_ASS_TEMP_TO_LIST(i, j) ASS_LIST(list, i, j) +#define SORT_ASS_LIST_TO_LOCAL(t, i) t = ELMV_LIST(list, i) +#define SORT_ASS_LOCAL_TO_LIST(i, j) ASS_LIST(list, i, j) #define SORT_COMP(v, w) CALL_2ARGS(func, v, w) == True /* list is not necc. sorted wrt. \< (any longer) */ #define SORT_FILTER_CHECKS() \ @@ -740,10 +743,11 @@ Obj HEAP_SORT_PLIST ( Obj self, Obj list ) #define SORT_FUNC_NAME SortDensePlistComp #define SORT_FUNC_ARGS Obj list, Obj func -#define SORT_CREATE_TEMP(name) Obj name ; +#define SORT_ARGS list, func +#define SORT_CREATE_LOCAL(name) Obj name ; #define SORT_LEN_LIST() LEN_PLIST(list) -#define SORT_ASS_LIST_TO_TEMP(t, i) t = ELM_PLIST(list, i) -#define SORT_ASS_TEMP_TO_LIST(i, j) SET_ELM_PLIST(list, i, j) +#define SORT_ASS_LIST_TO_LOCAL(t, i) t = ELM_PLIST(list, i) +#define SORT_ASS_LOCAL_TO_LIST(i, j) SET_ELM_PLIST(list, i, j) #define SORT_COMP(v, w) CALL_2ARGS(func, v, w) == True /* list is not necc. sorted wrt. \< (any longer) */ #define SORT_FILTER_CHECKS() \ @@ -770,12 +774,13 @@ Obj HEAP_SORT_PLIST ( Obj self, Obj list ) #define SORT_FUNC_NAME SORT_PARA_LIST #define SORT_FUNC_ARGS Obj list, Obj shadow -#define SORT_CREATE_TEMP(name) Obj name ; Obj name##s ; +#define SORT_ARGS list, shadow +#define SORT_CREATE_LOCAL(name) Obj name ; Obj name##s ; #define SORT_LEN_LIST() LEN_LIST(list) -#define SORT_ASS_LIST_TO_TEMP(t, i) \ +#define SORT_ASS_LIST_TO_LOCAL(t, i) \ t = ELMV_LIST(list, i); \ t##s = ELMV_LIST(shadow, i); -#define SORT_ASS_TEMP_TO_LIST(i, t) \ +#define SORT_ASS_LOCAL_TO_LIST(i, t) \ ASS_LIST(list, i, t); \ ASS_LIST(shadow, i, t##s); #define SORT_COMP(v, w) LT( v, w ) @@ -790,12 +795,13 @@ Obj HEAP_SORT_PLIST ( Obj self, Obj list ) #define SORT_FUNC_NAME SortParaDensePlist #define SORT_FUNC_ARGS Obj list, Obj shadow -#define SORT_CREATE_TEMP(name) Obj name ; Obj name##s ; +#define SORT_ARGS list, shadow +#define SORT_CREATE_LOCAL(name) Obj name ; Obj name##s ; #define SORT_LEN_LIST() LEN_PLIST(list) -#define SORT_ASS_LIST_TO_TEMP(t, i) \ +#define SORT_ASS_LIST_TO_LOCAL(t, i) \ t = ELM_PLIST(list, i); \ t##s = ELM_PLIST(shadow, i); -#define SORT_ASS_TEMP_TO_LIST(i, t) \ +#define SORT_ASS_LOCAL_TO_LIST(i, t) \ SET_ELM_PLIST(list, i, t); \ SET_ELM_PLIST(shadow, i, t##s); #define SORT_COMP(v, w) LT( v, w ) @@ -810,12 +816,13 @@ Obj HEAP_SORT_PLIST ( Obj self, Obj list ) #define SORT_FUNC_NAME SORT_PARA_LISTComp #define SORT_FUNC_ARGS Obj list, Obj shadow, Obj func -#define SORT_CREATE_TEMP(name) Obj name ; Obj name##s ; +#define SORT_ARGS list, shadow, func +#define SORT_CREATE_LOCAL(name) Obj name ; Obj name##s ; #define SORT_LEN_LIST() LEN_LIST(list) -#define SORT_ASS_LIST_TO_TEMP(t, i) \ +#define SORT_ASS_LIST_TO_LOCAL(t, i) \ t = ELMV_LIST(list, i); \ t##s = ELMV_LIST(shadow, i); -#define SORT_ASS_TEMP_TO_LIST(i, t) \ +#define SORT_ASS_LOCAL_TO_LIST(i, t) \ ASS_LIST(list, i, t); \ ASS_LIST(shadow, i, t##s); #define SORT_COMP(v, w) CALL_2ARGS( func, v, w ) == True @@ -830,12 +837,13 @@ Obj HEAP_SORT_PLIST ( Obj self, Obj list ) #define SORT_FUNC_NAME SortParaDensePlistComp #define SORT_FUNC_ARGS Obj list, Obj shadow, Obj func -#define SORT_CREATE_TEMP(name) Obj name ; Obj name##s ; +#define SORT_ARGS list, shadow, func +#define SORT_CREATE_LOCAL(name) Obj name ; Obj name##s ; #define SORT_LEN_LIST() LEN_PLIST(list) -#define SORT_ASS_LIST_TO_TEMP(t, i) \ +#define SORT_ASS_LIST_TO_LOCAL(t, i) \ t = ELM_PLIST(list, i); \ t##s = ELM_PLIST(shadow, i); -#define SORT_ASS_TEMP_TO_LIST(i, t) \ +#define SORT_ASS_LOCAL_TO_LIST(i, t) \ SET_ELM_PLIST(list, i, t); \ SET_ELM_PLIST(shadow, i, t##s); #define SORT_COMP(v, w) CALL_2ARGS( func, v, w ) == True diff --git a/src/sortbase.h b/src/sortbase.h index b23aea8181..0bcdbf618d 100644 --- a/src/sortbase.h +++ b/src/sortbase.h @@ -14,53 +14,292 @@ ** ** The following macros are used: ** SORT_FUNC_NAME : Name of function -** SORT_FUNC_ARGS : Arguments of function -** SORT_CREATE_TEMP(t) : Create a temp variable named t that can store +** SORT_FUNC_ARGS : Arguments of function for use in prototypes +** SORT_ARGS : Arguments of function for passing +** SORT_CREATE_LOCAL(t) : Create a temp variable named t that can store ** an element of the list ** SORT_LEN_LIST : Get the length of the list to be sorted -** SORT_ASS_LIST_TO_TEMP(t,i) : Copy list element 'i' to temporary 't' -** SORT_ASS_TEMP_TO_LIST(i,t) : Copy temporary 't' to list element 'i' +** SORT_ASS_LIST_TO_LOCAL(t,i) : Copy list element 'i' to temporary 't' +** SORT_ASS_LOCAL_TO_LIST(i,t) : Copy temporary 't' to list element 'i' ** SORT_COMP(v,w) : Compare temporaries v and w ** SORT_FILTER_CHECKS : Arbitary code to be called at end of function, ** to fix filters effected by the sorting. +** +** +** Design choices: +** Only temporaries can be compared, not list elements directly. This just +** reduces the number of functions we must define, and we trust the compiler +** to optimise away pointer copies. +** +** This implements a slightly simplified version of pattern defeating quicksort +** ( https://github.com/orlp/pdqsort ), which is an extension of introsort. +** +** A broad overview of the algorithm is: +** * Start with a quicksort which chooses pivot using median of 3 +** * Sort any range of size < 24 with insertion sort. +** * If the depth of the quicksort is > log2(len), then we seem to be +** hitting a bad O(n^2) case. In that case, switch to shellsort (but only +** for the bad cell). +** +** * The 'cleverness' of pdqsort, which if the partitioning phase doesn't +** move anything, then try insertion sorting, with a limit to the number +** of swaps we will perform. This quickly detects and sub-ranges in +** increasing order, and sub-ranges in decreasing order will get reversed +** by pivoting, and then detected next pass +** */ -void SORT_FUNC_NAME(SORT_FUNC_ARGS) +/* This lets us join together two macro names to make + * one identifier. The two levels (JOIN,JOIN2) is to force + * the compiler to evaluate macros, so: + * PREFIXNAME(Insert), where SORT_FUNC_NAME is + * Sort, comes out as SortInsert, rather than + * SORT_FUNC_NAMEInsert + */ + +#define PREFIXNAME(x) JOIN(SORT_FUNC_NAME,x) +#define JOIN(x,y) JOIN2(x,y) +#define JOIN2(x,y) x##y + +void PREFIXNAME(Shell)(SORT_FUNC_ARGS, Int start, Int end) { UInt len; /* length of the list */ UInt h; /* gap width in the shellsort */ - SORT_CREATE_TEMP(v); - SORT_CREATE_TEMP(w); + SORT_CREATE_LOCAL(v); + SORT_CREATE_LOCAL(w); UInt i, k; /* loop variables */ /* sort the list with a shellsort */ - len = SORT_LEN_LIST(); + len = end - start + 1; h = 1; while ( 9*h + 4 < len ) { h = 3*h + 1; } while ( 0 < h ) { - for ( i = h+1; i <= len; i++ ) { - SORT_ASS_LIST_TO_TEMP( v, i ); + for ( i = h+start; i <= end; i++ ) { + SORT_ASS_LIST_TO_LOCAL( v, i ); k = i; - SORT_ASS_LIST_TO_TEMP( w, k-h ); - while ( h < k && SORT_COMP( v, w ) ) { - SORT_ASS_TEMP_TO_LIST( k, w ); + SORT_ASS_LIST_TO_LOCAL( w, k-h ); + while ( h+(start-1) < k && SORT_COMP( v, w ) ) { + SORT_ASS_LOCAL_TO_LIST( k, w ); k -= h; - if ( h < k ) { - SORT_ASS_LIST_TO_TEMP( w, k-h ); + if ( h+(start-1) < k ) { + SORT_ASS_LIST_TO_LOCAL( w, k-h ); } } - SORT_ASS_TEMP_TO_LIST( k, v ); + SORT_ASS_LOCAL_TO_LIST( k, v ); } h = h / 3; } SORT_FILTER_CHECKS(); } + +/* Swap values at indices a and b */ +#define SWAP_INDICES PREFIXNAME(Swap) +static inline void PREFIXNAME(Swap) (SORT_FUNC_ARGS, Int a, Int b) +{ + SORT_CREATE_LOCAL(t); + SORT_CREATE_LOCAL(u); + SORT_ASS_LIST_TO_LOCAL(t, a); + SORT_ASS_LIST_TO_LOCAL(u, b); + SORT_ASS_LOCAL_TO_LIST(b, t); + SORT_ASS_LOCAL_TO_LIST(a, u); +} + +/* Compare values at indices a and b */ +#define COMP_INDICES PREFIXNAME(CompIndices) +static inline int COMP_INDICES(SORT_FUNC_ARGS, Int a, Int b) +{ + SORT_CREATE_LOCAL(t); + SORT_CREATE_LOCAL(u); + SORT_ASS_LIST_TO_LOCAL(t, a); + SORT_ASS_LIST_TO_LOCAL(u, b); + return SORT_COMP(t,u); +} + +/* Sort 3 indices */ +static inline void PREFIXNAME(Sort3) (SORT_FUNC_ARGS, Int a, Int b, Int c) +{ + if(!(COMP_INDICES(SORT_ARGS, b, a))) { + if(!(COMP_INDICES(SORT_ARGS, c, b))) return; + + SWAP_INDICES(SORT_ARGS, b, c); + if(COMP_INDICES(SORT_ARGS, b, a)) { + SWAP_INDICES(SORT_ARGS, a, b); + } + return; + } + + if(COMP_INDICES(SORT_ARGS, c, b)) { + SWAP_INDICES(SORT_ARGS, a, c); + return; + } + + SWAP_INDICES(SORT_ARGS, a, b); + if(COMP_INDICES(SORT_ARGS, c, b)) { + SWAP_INDICES(SORT_ARGS, b, c); + } +} + +/* Partition a list, from indices start to end. Return if any values had + * to be moved, and store the partition_point in the argument + * partition_point + */ +static inline Int PREFIXNAME(Partition) (SORT_FUNC_ARGS, + Int start, Int end, Int* partition_point) +{ + Int left = start; + Int right = end; + Int first_pass = 1; + SORT_CREATE_LOCAL(pivot); + + PREFIXNAME(Sort3) (SORT_ARGS, start, start/2+end/2, end); + SORT_ASS_LIST_TO_LOCAL(pivot, start/2+end/2); + + SORT_CREATE_LOCAL(a); + SORT_CREATE_LOCAL(b); + SORT_ASS_LIST_TO_LOCAL(a, start); + SORT_ASS_LIST_TO_LOCAL(b, end); + + left++; + + while(1) { + while(left < right) { + SORT_CREATE_LOCAL(listcpy); + SORT_ASS_LIST_TO_LOCAL(listcpy, left); + if(SORT_COMP(pivot, listcpy)) + break; + left++; + } + + right--; + while(left < right) { + SORT_CREATE_LOCAL(listcpy); + SORT_ASS_LIST_TO_LOCAL(listcpy, right); + if(!(SORT_COMP(pivot, listcpy))) + break; + right--; + } + + if(left >= right) + { + *partition_point = left; + return first_pass; + } + first_pass = 0; + + SWAP_INDICES(SORT_ARGS, left, right); + left++; + } +} + +void PREFIXNAME(Insertion) (SORT_FUNC_ARGS, Int start, Int end) +{ + SORT_CREATE_LOCAL(v); + SORT_CREATE_LOCAL(w); + UInt i, k; /* loop variables */ + + /* sort the list with insertion sort */ + for ( i = start + 1; i <= end; i++ ) { + SORT_ASS_LIST_TO_LOCAL( v, i ); + k = i; + SORT_ASS_LIST_TO_LOCAL( w, k-1 ); + while ( start < k && SORT_COMP( v, w ) ) { + SORT_ASS_LOCAL_TO_LIST( k, w ); + k -= 1; + if ( start < k ) { + SORT_ASS_LIST_TO_LOCAL( w, k-1 ); + } + } + SORT_ASS_LOCAL_TO_LIST( k, v ); + } +} + +/* This function performs an insertion sort with a limit to the number + * of swaps performed -- if we pass that limit we abandon the sort */ +Obj PREFIXNAME(LimitedInsertion) (SORT_FUNC_ARGS, Int start, Int end) +{ + SORT_CREATE_LOCAL(v); + SORT_CREATE_LOCAL(w); + UInt i, k; /* loop variables */ + Int limit = 8; /* how long do we try to insertion sort? */ + /* sort the list with insertion sort */ + for ( i = start + 1; i <= end; i++ ) { + SORT_ASS_LIST_TO_LOCAL( v, i ); + k = i; + SORT_ASS_LIST_TO_LOCAL( w, k-1 ); + while ( start < k && SORT_COMP( v, w ) ) { + limit--; + if(limit == 0) { + SORT_ASS_LOCAL_TO_LIST( k, v ); + return False; + } + + SORT_ASS_LOCAL_TO_LIST( k, w ); + k -= 1; + if ( start < k ) { + SORT_ASS_LIST_TO_LOCAL( w, k-1 ); + } + } + SORT_ASS_LOCAL_TO_LIST( k, v ); + } + return True; +} + +/* This function assumes it doesn't get called for ranges which are very small */ +void PREFIXNAME(CheckBadPivot) (SORT_FUNC_ARGS, Int start, Int end, Int pivot) +{ + Int length = end - start; + if(pivot - start < length / 8) { + SWAP_INDICES(SORT_ARGS, pivot, pivot+length/4); + SWAP_INDICES(SORT_ARGS, end, end - length/4); + } + if(pivot - start > 7*(length/8) ) { + SWAP_INDICES(SORT_ARGS, start, start + length/4); + SWAP_INDICES(SORT_ARGS, pivot-1, pivot-1 - length/4); + } +} +void PREFIXNAME(QuickSort) (SORT_FUNC_ARGS, Int start, Int end, Int depth) +{ + Int pivot, first_pass; + + if(end - start < 24) { + PREFIXNAME(Insertion)(SORT_ARGS, start, end); + return; + } + + /* If quicksort seems to be degrading into O(n^2), escape to shellsort */ + if(depth <= 0) { + PREFIXNAME(Shell)(SORT_ARGS, start, end); + return; + } + + first_pass = PREFIXNAME(Partition)(SORT_ARGS, start, end, &pivot); + PREFIXNAME(CheckBadPivot)(SORT_ARGS, start, end, pivot); + if(!first_pass || ! (PREFIXNAME(LimitedInsertion)(SORT_ARGS, start, pivot-1) == True)) { + PREFIXNAME(QuickSort)(SORT_ARGS, start, pivot-1, depth-1); + } + + if(!first_pass || ! (PREFIXNAME(LimitedInsertion)(SORT_ARGS, pivot, end) == True)) { + PREFIXNAME(QuickSort)(SORT_ARGS, pivot, end, depth-1); + } +} + + +void SORT_FUNC_NAME(SORT_FUNC_ARGS) +{ + Int len = SORT_LEN_LIST(); + PREFIXNAME(QuickSort)(SORT_ARGS, 1, len, CLog2Int(len)*2 + 2); +} + + +#undef PREFIXNAME +#undef COMP_INDICES #undef SORT_FUNC_NAME #undef SORT_FUNC_ARGS -#undef SORT_CREATE_TEMP +#undef SORT_ARGS +#undef SORT_CREATE_LOCAL #undef SORT_LEN_LIST -#undef SORT_ASS_LIST_TO_TEMP -#undef SORT_ASS_TEMP_TO_LIST +#undef SORT_ASS_LIST_TO_LOCAL +#undef SORT_ASS_LOCAL_TO_LIST #undef SORT_COMP #undef SORT_FILTER_CHECKS diff --git a/tst/teststandard/sort.tst b/tst/teststandard/sort.tst new file mode 100644 index 0000000000..1276d1a191 --- /dev/null +++ b/tst/teststandard/sort.tst @@ -0,0 +1,100 @@ +# This aims to test the various implementations of Sort. There are a few cases +# we must cover: +# +# * We can choose if we pass a comparator +# * We can do Sort or SortParallel +# * We specialise for plain lists +# Most of these checks are generate a whole bunch of random tests + +gap> START_TEST("sort.tst"); +gap> CheckSort := function(list, sorted) +> local listcpy, perm; +> listcpy := DEEP_COPY_OBJ(list); Sort(listcpy); +> if listcpy <> sorted then Print("Fail 1 : ", listcpy, list, sorted); fi; +> listcpy := DEEP_COPY_OBJ(list); Sort(listcpy, function (a,b) return a < b; end); +> if listcpy <> sorted then Print("Fail 2 : ", listcpy, list, sorted); fi; +> listcpy := DEEP_COPY_OBJ(list); Sort(listcpy, function (a,b) return a <= b; end); +> if listcpy <> sorted then Print("Fail 3 : ", listcpy, list, sorted); fi; +> listcpy := DEEP_COPY_OBJ(list); Sort(listcpy, function (a,b) return a > b; end); +> if listcpy <> Reversed(sorted) then Print("Fail 4 : ", listcpy, list, sorted); fi; +> end;; +gap> for i in [0..500] do CheckSort([1..i],[1..i]); od; +gap> for i in [0..500] do CheckSort([-i..i],[-i..i]); od; +gap> for i in [0..500] do CheckSort([i,i-1..-i],[-i..i]); od; +gap> for i in [0..500] do +> for j in [0..10] do +> CheckSort(Shuffle([1..i]), [1..i]); +> od; +> od; +gap> for i in [0..100] do +> for j in [0..10] do +> l := Concatenation(List([0..j], x -> List([0..i], y -> x))); +> l2 := Shuffle(List(l)); +> CheckSort(l2, l); +> od; +> od; + +# Need to test something which are not plists. Strings are a good choice. +gap> for i in [0..100] do +> for j in [0..10] do +> l := ""; +> for a in [1..j] do for b in [1..i] do +> Add(l, CHARS_LALPHA[a]); +> od; od; +> l2 := Shuffle(List(l)); +> if not(IsStringRep(l)) or not(IsStringRep(l2)) then +> Print("StringFail"); +> fi; +> CheckSort(l2, l); +> od; +> od; + +# Let test bool lists too! +gap> for i in [0..100] do +> for j in [0..10] do +> l := BlistList([1..i+j],[1..i]); +> l2 := Shuffle(List(l)); +> if not(IsBlistRep(l)) or not(IsBlistRep(l2)) then +> Print("BlistFail"); +> fi; +> CheckSort(l2, l); +> od; +> od; + +# Test SortParallel +gap> CheckSortParallel := function(sortedlist, perm, maxval) +> local list1, list2, listcpy, list1orig; +> # This slightly weird code is because I want to preserve +> # The type of the input list +> list1orig := DEEP_COPY_OBJ(sortedlist); +> for i in [1..maxval] do +> list1orig[i] := sortedlist[i^perm]; +> od; +> list1 := DEEP_COPY_OBJ(list1orig); +> list2 := List([1..maxval], x -> Random([1..100])); +> listcpy := List(list2); +> SortParallel(list1, list2); +> if ForAny([1..maxval], x -> list2[x^perm] <> listcpy[x]) then +> Print("failed SortParallel 1", perm, maxval, list2); +> fi; +> list1 := DEEP_COPY_OBJ(list1orig); +> listcpy := List(list2); +> SortParallel(list1, list2, function (a,b) return a <= b; end); +> if ForAny([1..maxval], x -> list2[x^perm] <> listcpy[x]) then +> Print("failed SortParallel 2", perm, maxval, list2); +> fi; +> end;; +gap> for i in [0..100] do +> for j in [0..10] do +> CheckSortParallel([1..i],Random(SymmetricGroup([1..i])), i); +> od; +> od; + +# Just sanity check I really am making string reps +gap> IsStringRep(CHARS_LALPHA{[1..0]}) and IsStringRep(CHARS_LALPHA{[1..10]}); +true +gap> for i in [0..26] do +> for j in [0..10] do +> CheckSortParallel(CHARS_LALPHA{[1..i]},Random(SymmetricGroup([1..i])), i); +> od; +> od; From 1d345639e557138a5a17bcc883292f847bd20227 Mon Sep 17 00:00:00 2001 From: Chris Jefferson Date: Thu, 11 Feb 2016 08:55:19 +0000 Subject: [PATCH 3/3] Code reformat --- lib/list.gd | 10 +- lib/list.gi | 85 +++++- src/listfunc.c | 257 +++++++++++++----- src/sortbase.h | 464 ++++++++++++++++++-------------- tst/teststandard/sort.tst | 3 + tst/teststandard/stablesort.tst | 158 +++++++++++ 6 files changed, 709 insertions(+), 268 deletions(-) create mode 100644 tst/teststandard/stablesort.tst diff --git a/lib/list.gd b/lib/list.gd index 1249bac1ec..367f69399a 100644 --- a/lib/list.gd +++ b/lib/list.gd @@ -1531,6 +1531,10 @@ DeclareOperation( "Sort", [ IsList and IsMutable ] ); DeclareOperation( "Sort", [ IsList and IsMutable, IsFunction ] ); DeclareOperation( "SortBy", [IsList and IsMutable, IsFunction ] ); +DeclareOperation( "StableSort", [ IsList and IsMutable ] ); +DeclareOperation( "StableSort", [ IsList and IsMutable, IsFunction ] ); +DeclareOperation( "StableSortBy", [IsList and IsMutable, IsFunction ] ); + ############################################################################# ## @@ -1663,6 +1667,11 @@ DeclareOperation( "SortParallel", DeclareOperation( "SortParallel", [ IsDenseList and IsMutable, IsDenseList and IsMutable, IsFunction ] ); +DeclareOperation( "StableSortParallel", + [ IsDenseList and IsMutable, IsDenseList and IsMutable ] ); +DeclareOperation( "StableSortParallel", + [ IsDenseList and IsMutable, IsDenseList and IsMutable, IsFunction ] ); + ############################################################################# ## @@ -2310,4 +2319,3 @@ DeclareGlobalFunction("Median"); ############################################################################# ## #E - diff --git a/lib/list.gi b/lib/list.gi index 52e2c87112..39e3fc167a 100644 --- a/lib/list.gi +++ b/lib/list.gi @@ -2101,6 +2101,11 @@ InstallMethod( Sort, [ IsList and IsMutable and IsSmallList ], SORT_LIST ); +InstallMethod( StableSort, + "for a mutable small list", + [ IsList and IsMutable and IsSmallList ], + STABLE_SORT_LIST ); + InstallMethod( Sort, "for a mutable list", [ IsList and IsMutable ], @@ -2112,16 +2117,37 @@ InstallMethod( Sort, fi; end ); +InstallMethod( StableSort, + "for a mutable list", + [ IsList and IsMutable ], + function( list ) + if IsSmallList( list ) then + STABLE_SORT_LIST( list ); + else + TryNextMethod(); + fi; + end ); + InstallMethod( Sort, "for a mutable set", [ IsList and IsMutable and IsSortedList ], SUM_FLAGS, Ignore ); +InstallMethod( StableSort, + "for a mutable set", + [ IsList and IsMutable and IsSortedList ], SUM_FLAGS, + Ignore ); + InstallMethod( Sort, "for a mutable small list and a function", [ IsList and IsMutable and IsSmallList, IsFunction ], SORT_LIST_COMP ); +InstallMethod( StableSort, + "for a mutable small list and a function", + [ IsList and IsMutable and IsSmallList, IsFunction ], + STABLE_SORT_LIST_COMP ); + InstallMethod( Sort, "for a mutable list and a function", [ IsList and IsMutable, IsFunction ], @@ -2133,6 +2159,17 @@ InstallMethod( Sort, fi; end ); +InstallMethod( StableSort, + "for a mutable list and a function", + [ IsList and IsMutable, IsFunction ], + function( list, func ) + if IsSmallList( list ) then + STABLE_SORT_LIST_COMP( list, func ); + else + TryNextMethod(); + fi; +end ); + ############################################################################# ## #M SortBy( , ) @@ -2147,7 +2184,14 @@ InstallMethod( SortBy, "for a mutable list and a function", return; end); - +InstallMethod( StableSortBy, "for a mutable list and a function", + [IsList and IsMutable, IsFunction ], + function(list, func) + local images; + images := List(list, func); + StableSortParallel(images, list); + return; +end); ############################################################################# ## @@ -2175,11 +2219,20 @@ InstallOtherMethod( Sort, [ IsList ], SORT_MUTABILITY_ERROR_HANDLER ); +InstallOtherMethod( StableSort, + "for an immutable list", + [ IsList ], + SORT_MUTABILITY_ERROR_HANDLER ); + InstallOtherMethod( Sort, "for an immutable list and a function", [ IsList, IsFunction ], SORT_MUTABILITY_ERROR_HANDLER ); +InstallOtherMethod( StableSort, + "for an immutable list and a function", + [ IsList, IsFunction ], + SORT_MUTABILITY_ERROR_HANDLER ); ############################################################################# ## @@ -2320,7 +2373,12 @@ InstallMethod( SortParallel, IsDenseList and IsMutable ], SORT_PARA_LIST ); - +InstallMethod( StableSortParallel, + "for two dense and mutable lists", + [ IsDenseList and IsMutable, + IsDenseList and IsMutable ], + STABLE_SORT_PARA_LIST ); + ############################################################################# ## #M SortParallel( , ) @@ -2332,6 +2390,12 @@ InstallMethod( SortParallel, SUM_FLAGS, Ignore ); +InstallMethod( StableSortParallel, + "for a mutable set and a dense mutable list", + [ IsDenseList and IsSortedList and IsMutable, + IsDenseList and IsMutable ], + SUM_FLAGS, + Ignore ); ############################################################################# ## @@ -2344,17 +2408,32 @@ InstallMethod( SortParallel, IsFunction ], SORT_PARA_LIST_COMP ); +InstallMethod( StableSortParallel, + "for two dense and mutable lists, and function", + [ IsDenseList and IsMutable, + IsDenseList and IsMutable, + IsFunction ], + STABLE_SORT_PARA_LIST_COMP ); InstallOtherMethod( SortParallel, "for two immutable lists", [IsList,IsList], SORT_MUTABILITY_ERROR_HANDLER); +InstallOtherMethod( StableSortParallel, + "for two immutable lists", + [IsList,IsList], + SORT_MUTABILITY_ERROR_HANDLER); + InstallOtherMethod( SortParallel, "for two immutable lists and function", [IsList,IsList,IsFunction], SORT_MUTABILITY_ERROR_HANDLER); +InstallOtherMethod( StableSortParallel, + "for two immutable lists and function", + [IsList,IsList,IsFunction], + SORT_MUTABILITY_ERROR_HANDLER); ############################################################################# ## @@ -3904,5 +3983,3 @@ end); ############################################################################# ## #E - - diff --git a/src/listfunc.c b/src/listfunc.c index 7e54df7dee..f2f38271fb 100644 --- a/src/listfunc.c +++ b/src/listfunc.c @@ -691,6 +691,15 @@ Obj HEAP_SORT_PLIST ( Obj self, Obj list ) // See sortbase.h for a description of these macros. +// We put these first, as they are the same for the next 4 functions so +// we do not have to repeat them +#define SORT_CREATE_TEMP_BUFFER(len) NEW_PLIST( T_PLIST, len + 1000); +#define SORT_ASS_BUF_TO_LOCAL(buffer, t, i) t = ELM_PLIST(buffer, i); +#define SORT_ASS_LOCAL_TO_BUF(buffer, i, j) \ + SET_ELM_PLIST(buffer, i, j); \ + CHANGED_BAG(buffer); + + #define SORT_FUNC_NAME SORT_LIST #define SORT_FUNC_ARGS Obj list #define SORT_ARGS list @@ -711,7 +720,9 @@ Obj HEAP_SORT_PLIST ( Obj self, Obj list ) #define SORT_CREATE_LOCAL(name) Obj name ; #define SORT_LEN_LIST() LEN_PLIST(list) #define SORT_ASS_LIST_TO_LOCAL(t, i) t = ELM_PLIST(list, i) -#define SORT_ASS_LOCAL_TO_LIST(i, j) SET_ELM_PLIST(list, i, j) +#define SORT_ASS_LOCAL_TO_LIST(i, j) \ + SET_ELM_PLIST(list, i, j); \ + CHANGED_BAG(list); #define SORT_COMP(v, w) LT(v, w) #define SORT_FILTER_CHECKS() \ RESET_FILT_LIST(list, FN_IS_NSORT); @@ -747,7 +758,9 @@ Obj HEAP_SORT_PLIST ( Obj self, Obj list ) #define SORT_CREATE_LOCAL(name) Obj name ; #define SORT_LEN_LIST() LEN_PLIST(list) #define SORT_ASS_LIST_TO_LOCAL(t, i) t = ELM_PLIST(list, i) -#define SORT_ASS_LOCAL_TO_LIST(i, j) SET_ELM_PLIST(list, i, j) +#define SORT_ASS_LOCAL_TO_LIST(i, j) \ + SET_ELM_PLIST(list, i, j); \ + CHANGED_BAG(list); #define SORT_COMP(v, w) CALL_2ARGS(func, v, w) == True /* list is not necc. sorted wrt. \< (any longer) */ #define SORT_FILTER_CHECKS() \ @@ -772,6 +785,22 @@ Obj HEAP_SORT_PLIST ( Obj self, Obj list ) ** the second list added in. */ +// These 3 macros are the same for all 4 of the following functions. +#undef SORT_CREATE_TEMP_BUFFER +#undef SORT_ASS_BUF_TO_LOCAL +#undef SORT_ASS_LOCAL_TO_BUF + +#define SORT_CREATE_TEMP_BUFFER(len) NEW_PLIST( T_PLIST, len * 2 + 1000); +#define SORT_ASS_BUF_TO_LOCAL(buffer, t, i) \ + t = ELM_PLIST(buffer, 2*(i)); \ + t##s = ELM_PLIST(buffer, 2*(i)-1); +#define SORT_ASS_LOCAL_TO_BUF(buffer, i, j) \ + SET_ELM_PLIST(buffer, 2*(i), j); \ + SET_ELM_PLIST(buffer, 2*(i)-1, j##s); \ + CHANGED_BAG(buffer); + + + #define SORT_FUNC_NAME SORT_PARA_LIST #define SORT_FUNC_ARGS Obj list, Obj shadow #define SORT_ARGS list, shadow @@ -803,7 +832,9 @@ Obj HEAP_SORT_PLIST ( Obj self, Obj list ) t##s = ELM_PLIST(shadow, i); #define SORT_ASS_LOCAL_TO_LIST(i, t) \ SET_ELM_PLIST(list, i, t); \ - SET_ELM_PLIST(shadow, i, t##s); + SET_ELM_PLIST(shadow, i, t##s); \ + CHANGED_BAG(list); \ + CHANGED_BAG(shadow); #define SORT_COMP(v, w) LT( v, w ) /* if list was ssorted, then it still will be, but, we don't know anything else any more */ @@ -845,7 +876,9 @@ Obj HEAP_SORT_PLIST ( Obj self, Obj list ) t##s = ELM_PLIST(shadow, i); #define SORT_ASS_LOCAL_TO_LIST(i, t) \ SET_ELM_PLIST(list, i, t); \ - SET_ELM_PLIST(shadow, i, t##s); + SET_ELM_PLIST(shadow, i, t##s); \ + CHANGED_BAG(list); \ + CHANGED_BAG(shadow); #define SORT_COMP(v, w) CALL_2ARGS( func, v, w ) == True /* list is not necc. sorted wrt. \< (any longer) */ #define SORT_FILTER_CHECKS() \ @@ -932,6 +965,31 @@ UInt RemoveDupsDensePlist ( *F * * * * * * * * * * * * * * GAP level functions * * * * * * * * * * * * * */ +/**************************************************************************** +** +** Some common checks. +*/ + +void CheckIsSmallList( + Obj list, + Char* caller) +{ + if ( ! IS_SMALL_LIST(list) ) { + ErrorMayQuit("%s: must be a small list (not a %s)", + (Int)caller, (Int)TNAM_OBJ(list)); + } +} + +void CheckIsFunction( + Obj func, + Char* caller) +{ + if ( TNUM_OBJ( func ) != T_FUNCTION ) { + ErrorMayQuit("%s: must be a function (not a %s)", + (Int)caller, (Int)TNAM_OBJ(func)); + } +} + /**************************************************************************** ** *F FuncSORT_LIST( , ) . . . . . . . . . . . . . . . sort a list @@ -941,12 +999,7 @@ Obj FuncSORT_LIST ( Obj list ) { /* check the first argument */ - while ( ! IS_SMALL_LIST(list) ) { - list = ErrorReturnObj( - "SORT_LIST: must be a small list (not a %s)", - (Int)TNAM_OBJ(list), 0L, - "you can replace via 'return ;'" ); - } + CheckIsSmallList(list, "SORT_LIST"); /* dispatch */ if ( IS_DENSE_PLIST(list) ) { @@ -961,11 +1014,30 @@ Obj FuncSORT_LIST ( return (Obj)0; } +Obj FuncSTABLE_SORT_LIST ( + Obj self, + Obj list ) +{ + /* check the first argument */ + CheckIsSmallList(list, "STABLE_SORT_LIST"); -/**************************************************************************** -** + /* dispatch */ + if ( IS_DENSE_PLIST(list) ) { + SortDensePlistMerge( list ); + } + else { + SORT_LISTMerge( list ); + } + IS_SSORT_LIST(list); + + /* return nothing */ + return (Obj)0; +} + +/**************************************************************************** +** *F FuncSORT_LIST_COMP( , , ) . . . . . . . . sort a list */ Obj FuncSORT_LIST_COMP ( @@ -974,20 +1046,10 @@ Obj FuncSORT_LIST_COMP ( Obj func ) { /* check the first argument */ - while ( ! IS_SMALL_LIST(list) ) { - list = ErrorReturnObj( - "SORT_LISTComp: must be a small list (not a %s)", - (Int)TNAM_OBJ(list), 0L, - "you can replace via 'return ;'" ); - } + CheckIsSmallList(list, "SORT_LIST_COMP"); /* check the third argument */ - while ( TNUM_OBJ( func ) != T_FUNCTION ) { - func = ErrorReturnObj( - "SORT_LISTComp: must be a function (not a %s)", - (Int)TNAM_OBJ(func), 0L, - "you can replace via 'return ;'" ); - } + CheckIsFunction(func, "SORT_LIST_COMP"); /* dispatch */ if ( IS_DENSE_PLIST(list) ) { @@ -1001,6 +1063,29 @@ Obj FuncSORT_LIST_COMP ( return (Obj)0; } +Obj FuncSTABLE_SORT_LIST_COMP ( + Obj self, + Obj list, + Obj func ) +{ + /* check the first argument */ + CheckIsSmallList(list, "STABLE_SORT_LIST_COMP"); + + /* check the third argument */ + CheckIsFunction(func, "STABLE_SORT_LIST_COMP"); + + /* dispatch */ + if ( IS_DENSE_PLIST(list) ) { + SortDensePlistCompMerge( list, func ); + } + else { + SORT_LISTCompMerge( list, func ); + } + + /* return nothing */ + return (Obj)0; +} + /**************************************************************************** ** @@ -1012,24 +1097,14 @@ Obj FuncSORT_PARA_LIST ( Obj shadow ) { /* check the first two arguments */ - while ( ! IS_SMALL_LIST(list) ) { - list = ErrorReturnObj( - "SORT_PARA_LIST: first must be a small list (not a %s)", - (Int)TNAM_OBJ(list), 0L, - "you can replace via 'return ;'" ); - } - while ( ! IS_SMALL_LIST(shadow) ) { - shadow = ErrorReturnObj( - "SORT_PARA_LIST: second must be a small list (not a %s)", - (Int)TNAM_OBJ(shadow), 0L, - "you can replace via 'return ;'" ); - } + CheckIsSmallList(list, "SORT_PARA_LIST"); + CheckIsSmallList(shadow, "SORT_PARA_LIST"); + if( LEN_LIST( list ) != LEN_LIST( shadow ) ) { - ErrorReturnVoid( + ErrorMayQuit( "SORT_PARA_LIST: lists must have the same length (not %d and %d)", (Int)LEN_LIST( list ), - (Int)LEN_LIST( shadow ), - "you can 'return;'" ); + (Int)LEN_LIST( shadow )); } /* dispatch */ @@ -1045,6 +1120,35 @@ Obj FuncSORT_PARA_LIST ( return (Obj)0; } +Obj FuncSTABLE_SORT_PARA_LIST ( + Obj self, + Obj list, + Obj shadow ) +{ + /* check the first two arguments */ + CheckIsSmallList(list, "STABLE_SORT_PARA_LIST"); + CheckIsSmallList(shadow, "STABLE_SORT_PARA_LIST"); + + if( LEN_LIST( list ) != LEN_LIST( shadow ) ) { + ErrorMayQuit( + "STABLE_SORT_PARA_LIST: lists must have the same length (not %d and %d)", + (Int)LEN_LIST( list ), + (Int)LEN_LIST( shadow )); + } + + /* dispatch */ + if ( IS_DENSE_PLIST(list) && IS_DENSE_PLIST(shadow) ) { + SortParaDensePlistMerge( list, shadow ); + } + else { + SORT_PARA_LISTMerge( list, shadow ); + } + IS_SSORT_LIST(list); + + /* return nothing */ + return (Obj)0; +} + /**************************************************************************** ** @@ -1057,34 +1161,19 @@ Obj FuncSORT_PARA_LIST_COMP ( Obj func ) { /* check the first two arguments */ - while ( ! IS_SMALL_LIST(list) ) { - list = ErrorReturnObj( - "SORT_LISTComp: must be a small list (not a %s)", - (Int)TNAM_OBJ(list), 0L, - "you can replace via 'return ;'" ); - } - while ( ! IS_SMALL_LIST(shadow) ) { - shadow = ErrorReturnObj( - "SORT_PARA_LIST: second must be a small list (not a %s)", - (Int)TNAM_OBJ(shadow), 0L, - "you can replace via 'return ;'" ); - } + CheckIsSmallList(list, "SORT_PARA_LIST_COMP"); + CheckIsSmallList(shadow, "SORT_PARA_LIST_COMP"); + if( LEN_LIST( list ) != LEN_LIST( shadow ) ) { - ErrorReturnVoid( - "SORT_PARA_LIST: lists must have the same length (not %d and %d)", + ErrorMayQuit( + "SORT_PARA_LIST_COMP: lists must have the same length (not %d and %d)", (Int)LEN_LIST( list ), - (Int)LEN_LIST( shadow ), - "you can 'return;'" ); + (Int)LEN_LIST( shadow )); } /* check the third argument */ - while ( TNUM_OBJ( func ) != T_FUNCTION ) { - func = ErrorReturnObj( - "SORT_LISTComp: must be a function (not a %s)", - (Int)TNAM_OBJ(func), 0L, - "you can replace via 'return ;'" ); - } - + CheckIsFunction(func, "SORT_PARA_LIST_COMP"); + /* dispatch */ if ( IS_DENSE_PLIST(list) && IS_DENSE_PLIST(shadow) ) { SortParaDensePlistComp( list, shadow, func ); @@ -1097,6 +1186,38 @@ Obj FuncSORT_PARA_LIST_COMP ( return (Obj)0; } +Obj FuncSTABLE_SORT_PARA_LIST_COMP ( + Obj self, + Obj list, + Obj shadow, + Obj func ) +{ + /* check the first two arguments */ + CheckIsSmallList(list, "SORT_PARA_LIST_COMP"); + CheckIsSmallList(shadow, "SORT_PARA_LIST_COMP"); + + if( LEN_LIST( list ) != LEN_LIST( shadow ) ) { + ErrorMayQuit( + "SORT_PARA_LIST_COMP: lists must have the same length (not %d and %d)", + (Int)LEN_LIST( list ), + (Int)LEN_LIST( shadow )); + } + + /* check the third argument */ + CheckIsFunction(func, "SORT_PARA_LIST_COMP"); + + /* dispatch */ + if ( IS_DENSE_PLIST(list) && IS_DENSE_PLIST(shadow) ) { + SortParaDensePlistCompMerge( list, shadow, func ); + } + else { + SORT_PARA_LISTCompMerge( list, shadow, func ); + } + + /* return nothing */ + return (Obj)0; +} + /**************************************************************************** ** @@ -1862,15 +1983,27 @@ static StructGVarFunc GVarFuncs [] = { { "SORT_LIST", 1, "list", FuncSORT_LIST, "src/listfunc.c:SORT_LIST" }, + { "STABLE_SORT_LIST", 1, "list", + FuncSTABLE_SORT_LIST, "src/listfunc.c:STABLE_SORT_LIST" }, + { "SORT_LIST_COMP", 2, "list, func", FuncSORT_LIST_COMP, "src/listfunc.c:SORT_LIST_COMP" }, + { "STABLE_SORT_LIST_COMP", 2, "list, func", + FuncSTABLE_SORT_LIST_COMP, "src/listfunc.c:STABLE_SORT_LIST_COMP" }, + { "SORT_PARA_LIST", 2, "list, list", FuncSORT_PARA_LIST, "src/listfunc.c:SORT_PARA_LIST" }, + { "STABLE_SORT_PARA_LIST", 2, "list, list", + FuncSTABLE_SORT_PARA_LIST, "src/listfunc.c:STABLE_SORT_PARA_LIST" }, + { "SORT_PARA_LIST_COMP", 3, "list, list, func", FuncSORT_PARA_LIST_COMP, "src/listfunc.c:SORT_PARA_LIST_COMP" }, + { "STABLE_SORT_PARA_LIST_COMP", 3, "list, list, func", + FuncSTABLE_SORT_PARA_LIST_COMP, "src/listfunc.c:STABLE_SORT_PARA_LIST_COMP" }, + { "OnPoints", 2, "pnt, elm", FuncOnPoints, "src/listfunc.c:OnPoints" }, diff --git a/src/sortbase.h b/src/sortbase.h index 0bcdbf618d..66aac47f1a 100644 --- a/src/sortbase.h +++ b/src/sortbase.h @@ -49,6 +49,8 @@ ** */ +#include + /* This lets us join together two macro names to make * one identifier. The two levels (JOIN,JOIN2) is to force * the compiler to evaluate macros, so: @@ -56,241 +58,301 @@ * Sort, comes out as SortInsert, rather than * SORT_FUNC_NAMEInsert */ - -#define PREFIXNAME(x) JOIN(SORT_FUNC_NAME,x) -#define JOIN(x,y) JOIN2(x,y) -#define JOIN2(x,y) x##y - -void PREFIXNAME(Shell)(SORT_FUNC_ARGS, Int start, Int end) -{ - UInt len; /* length of the list */ - UInt h; /* gap width in the shellsort */ - SORT_CREATE_LOCAL(v); - SORT_CREATE_LOCAL(w); - UInt i, k; /* loop variables */ - - /* sort the list with a shellsort */ - len = end - start + 1; - h = 1; - while ( 9*h + 4 < len ) { h = 3*h + 1; } - while ( 0 < h ) { - for ( i = h+start; i <= end; i++ ) { - SORT_ASS_LIST_TO_LOCAL( v, i ); - k = i; - SORT_ASS_LIST_TO_LOCAL( w, k-h ); - while ( h+(start-1) < k && SORT_COMP( v, w ) ) { - SORT_ASS_LOCAL_TO_LIST( k, w ); - k -= h; - if ( h+(start-1) < k ) { - SORT_ASS_LIST_TO_LOCAL( w, k-h ); - } - } - SORT_ASS_LOCAL_TO_LIST( k, v ); + +#define PREFIXNAME(x) JOIN(SORT_FUNC_NAME, x) +#define JOIN(x, y) JOIN2(x, y) +#define JOIN2(x, y) x##y + +void PREFIXNAME(Shell)(SORT_FUNC_ARGS, Int start, Int end) { + UInt len; /* length of the list */ + UInt h; /* gap width in the shellsort */ + SORT_CREATE_LOCAL(v); + SORT_CREATE_LOCAL(w); + UInt i, k; /* loop variables */ + + /* sort the list with a shellsort */ + len = end - start + 1; + h = 1; + while (9 * h + 4 < len) { + h = 3 * h + 1; + } + while (0 < h) { + for (i = h + start; i <= end; i++) { + SORT_ASS_LIST_TO_LOCAL(v, i); + k = i; + SORT_ASS_LIST_TO_LOCAL(w, k - h); + while (h + (start - 1) < k && SORT_COMP(v, w)) { + SORT_ASS_LOCAL_TO_LIST(k, w); + k -= h; + if (h + (start - 1) < k) { + SORT_ASS_LIST_TO_LOCAL(w, k - h); } - h = h / 3; + } + SORT_ASS_LOCAL_TO_LIST(k, v); } - SORT_FILTER_CHECKS(); + h = h / 3; + } + SORT_FILTER_CHECKS(); } - /* Swap values at indices a and b */ -#define SWAP_INDICES PREFIXNAME(Swap) -static inline void PREFIXNAME(Swap) (SORT_FUNC_ARGS, Int a, Int b) -{ - SORT_CREATE_LOCAL(t); - SORT_CREATE_LOCAL(u); - SORT_ASS_LIST_TO_LOCAL(t, a); - SORT_ASS_LIST_TO_LOCAL(u, b); - SORT_ASS_LOCAL_TO_LIST(b, t); - SORT_ASS_LOCAL_TO_LIST(a, u); +#define SWAP_INDICES PREFIXNAME(Swap) +static inline void PREFIXNAME(Swap)(SORT_FUNC_ARGS, Int a, Int b) { + SORT_CREATE_LOCAL(t); + SORT_CREATE_LOCAL(u); + SORT_ASS_LIST_TO_LOCAL(t, a); + SORT_ASS_LIST_TO_LOCAL(u, b); + SORT_ASS_LOCAL_TO_LIST(b, t); + SORT_ASS_LOCAL_TO_LIST(a, u); } /* Compare values at indices a and b */ #define COMP_INDICES PREFIXNAME(CompIndices) -static inline int COMP_INDICES(SORT_FUNC_ARGS, Int a, Int b) -{ - SORT_CREATE_LOCAL(t); - SORT_CREATE_LOCAL(u); - SORT_ASS_LIST_TO_LOCAL(t, a); - SORT_ASS_LIST_TO_LOCAL(u, b); - return SORT_COMP(t,u); +static inline int COMP_INDICES(SORT_FUNC_ARGS, Int a, Int b) { + SORT_CREATE_LOCAL(t); + SORT_CREATE_LOCAL(u); + SORT_ASS_LIST_TO_LOCAL(t, a); + SORT_ASS_LIST_TO_LOCAL(u, b); + return SORT_COMP(t, u); } /* Sort 3 indices */ -static inline void PREFIXNAME(Sort3) (SORT_FUNC_ARGS, Int a, Int b, Int c) -{ - if(!(COMP_INDICES(SORT_ARGS, b, a))) { - if(!(COMP_INDICES(SORT_ARGS, c, b))) return; - - SWAP_INDICES(SORT_ARGS, b, c); - if(COMP_INDICES(SORT_ARGS, b, a)) { - SWAP_INDICES(SORT_ARGS, a, b); - } - return; - } - - if(COMP_INDICES(SORT_ARGS, c, b)) { - SWAP_INDICES(SORT_ARGS, a, c); - return; - } - - SWAP_INDICES(SORT_ARGS, a, b); - if(COMP_INDICES(SORT_ARGS, c, b)) { - SWAP_INDICES(SORT_ARGS, b, c); - } +static inline void PREFIXNAME(Sort3)(SORT_FUNC_ARGS, Int a, Int b, Int c) { + if (!(COMP_INDICES(SORT_ARGS, b, a))) { + if (!(COMP_INDICES(SORT_ARGS, c, b))) + return; + + SWAP_INDICES(SORT_ARGS, b, c); + if (COMP_INDICES(SORT_ARGS, b, a)) { + SWAP_INDICES(SORT_ARGS, a, b); + } + return; + } + + if (COMP_INDICES(SORT_ARGS, c, b)) { + SWAP_INDICES(SORT_ARGS, a, c); + return; + } + + SWAP_INDICES(SORT_ARGS, a, b); + if (COMP_INDICES(SORT_ARGS, c, b)) { + SWAP_INDICES(SORT_ARGS, b, c); + } } /* Partition a list, from indices start to end. Return if any values had * to be moved, and store the partition_point in the argument * partition_point */ -static inline Int PREFIXNAME(Partition) (SORT_FUNC_ARGS, - Int start, Int end, Int* partition_point) -{ - Int left = start; - Int right = end; - Int first_pass = 1; - SORT_CREATE_LOCAL(pivot); - - PREFIXNAME(Sort3) (SORT_ARGS, start, start/2+end/2, end); - SORT_ASS_LIST_TO_LOCAL(pivot, start/2+end/2); - - SORT_CREATE_LOCAL(a); - SORT_CREATE_LOCAL(b); - SORT_ASS_LIST_TO_LOCAL(a, start); - SORT_ASS_LIST_TO_LOCAL(b, end); - - left++; - - while(1) { - while(left < right) { - SORT_CREATE_LOCAL(listcpy); - SORT_ASS_LIST_TO_LOCAL(listcpy, left); - if(SORT_COMP(pivot, listcpy)) - break; - left++; - } - - right--; - while(left < right) { - SORT_CREATE_LOCAL(listcpy); - SORT_ASS_LIST_TO_LOCAL(listcpy, right); - if(!(SORT_COMP(pivot, listcpy))) - break; - right--; - } - - if(left >= right) - { - *partition_point = left; - return first_pass; - } - first_pass = 0; - - SWAP_INDICES(SORT_ARGS, left, right); - left++; - } +static inline Int PREFIXNAME(Partition)(SORT_FUNC_ARGS, Int start, Int end, + Int *partition_point) { + Int left = start; + Int right = end; + Int first_pass = 1; + SORT_CREATE_LOCAL(pivot); + + PREFIXNAME(Sort3)(SORT_ARGS, start, start / 2 + end / 2, end); + SORT_ASS_LIST_TO_LOCAL(pivot, start / 2 + end / 2); + + left++; + + while (1) { + while (left < right) { + SORT_CREATE_LOCAL(listcpy); + SORT_ASS_LIST_TO_LOCAL(listcpy, left); + if (SORT_COMP(pivot, listcpy)) + break; + left++; + } + + right--; + while (left < right) { + SORT_CREATE_LOCAL(listcpy); + SORT_ASS_LIST_TO_LOCAL(listcpy, right); + if (!(SORT_COMP(pivot, listcpy))) + break; + right--; + } + + if (left >= right) { + *partition_point = left; + return first_pass; + } + first_pass = 0; + + SWAP_INDICES(SORT_ARGS, left, right); + left++; + } } -void PREFIXNAME(Insertion) (SORT_FUNC_ARGS, Int start, Int end) -{ - SORT_CREATE_LOCAL(v); - SORT_CREATE_LOCAL(w); - UInt i, k; /* loop variables */ - - /* sort the list with insertion sort */ - for ( i = start + 1; i <= end; i++ ) { - SORT_ASS_LIST_TO_LOCAL( v, i ); - k = i; - SORT_ASS_LIST_TO_LOCAL( w, k-1 ); - while ( start < k && SORT_COMP( v, w ) ) { - SORT_ASS_LOCAL_TO_LIST( k, w ); - k -= 1; - if ( start < k ) { - SORT_ASS_LIST_TO_LOCAL( w, k-1 ); - } - } - SORT_ASS_LOCAL_TO_LIST( k, v ); +void PREFIXNAME(Insertion)(SORT_FUNC_ARGS, Int start, Int end) { + SORT_CREATE_LOCAL(v); + SORT_CREATE_LOCAL(w); + UInt i, k; /* loop variables */ + + /* sort the list with insertion sort */ + for (i = start + 1; i <= end; i++) { + SORT_ASS_LIST_TO_LOCAL(v, i); + k = i; + SORT_ASS_LIST_TO_LOCAL(w, k - 1); + while (start < k && SORT_COMP(v, w)) { + SORT_ASS_LOCAL_TO_LIST(k, w); + k -= 1; + if (start < k) { + SORT_ASS_LIST_TO_LOCAL(w, k - 1); } + } + SORT_ASS_LOCAL_TO_LIST(k, v); + } } /* This function performs an insertion sort with a limit to the number * of swaps performed -- if we pass that limit we abandon the sort */ -Obj PREFIXNAME(LimitedInsertion) (SORT_FUNC_ARGS, Int start, Int end) -{ - SORT_CREATE_LOCAL(v); - SORT_CREATE_LOCAL(w); - UInt i, k; /* loop variables */ - Int limit = 8; /* how long do we try to insertion sort? */ - /* sort the list with insertion sort */ - for ( i = start + 1; i <= end; i++ ) { - SORT_ASS_LIST_TO_LOCAL( v, i ); - k = i; - SORT_ASS_LIST_TO_LOCAL( w, k-1 ); - while ( start < k && SORT_COMP( v, w ) ) { - limit--; - if(limit == 0) { - SORT_ASS_LOCAL_TO_LIST( k, v ); - return False; - } - - SORT_ASS_LOCAL_TO_LIST( k, w ); - k -= 1; - if ( start < k ) { - SORT_ASS_LIST_TO_LOCAL( w, k-1 ); - } - } - SORT_ASS_LOCAL_TO_LIST( k, v ); +Obj PREFIXNAME(LimitedInsertion)(SORT_FUNC_ARGS, Int start, Int end) { + SORT_CREATE_LOCAL(v); + SORT_CREATE_LOCAL(w); + UInt i, k; /* loop variables */ + Int limit = 8; /* how long do we try to insertion sort? */ + /* sort the list with insertion sort */ + for (i = start + 1; i <= end; i++) { + SORT_ASS_LIST_TO_LOCAL(v, i); + k = i; + SORT_ASS_LIST_TO_LOCAL(w, k - 1); + while (start < k && SORT_COMP(v, w)) { + limit--; + if (limit == 0) { + SORT_ASS_LOCAL_TO_LIST(k, v); + return False; + } + + SORT_ASS_LOCAL_TO_LIST(k, w); + k -= 1; + if (start < k) { + SORT_ASS_LIST_TO_LOCAL(w, k - 1); } - return True; + } + SORT_ASS_LOCAL_TO_LIST(k, v); + } + return True; +} + +/* This function assumes it doesn't get called for ranges which are very small + */ +void PREFIXNAME(CheckBadPivot)(SORT_FUNC_ARGS, Int start, Int end, Int pivot) { + Int length = end - start; + if (pivot - start < length / 8) { + SWAP_INDICES(SORT_ARGS, pivot, pivot + length / 4); + SWAP_INDICES(SORT_ARGS, end, end - length / 4); + } + if (pivot - start > 7 * (length / 8)) { + SWAP_INDICES(SORT_ARGS, start, start + length / 4); + SWAP_INDICES(SORT_ARGS, pivot - 1, pivot - 1 - length / 4); + } } +void PREFIXNAME(QuickSort)(SORT_FUNC_ARGS, Int start, Int end, Int depth) { + Int pivot, first_pass; + + if (end - start < 24) { + PREFIXNAME(Insertion)(SORT_ARGS, start, end); + return; + } + + /* If quicksort seems to be degrading into O(n^2), escape to shellsort */ + if (depth <= 0) { + PREFIXNAME(Shell)(SORT_ARGS, start, end); + return; + } + + first_pass = PREFIXNAME(Partition)(SORT_ARGS, start, end, &pivot); + PREFIXNAME(CheckBadPivot)(SORT_ARGS, start, end, pivot); + if (!first_pass || + !(PREFIXNAME(LimitedInsertion)(SORT_ARGS, start, pivot - 1) == True)) { + PREFIXNAME(QuickSort)(SORT_ARGS, start, pivot - 1, depth - 1); + } -/* This function assumes it doesn't get called for ranges which are very small */ -void PREFIXNAME(CheckBadPivot) (SORT_FUNC_ARGS, Int start, Int end, Int pivot) -{ - Int length = end - start; - if(pivot - start < length / 8) { - SWAP_INDICES(SORT_ARGS, pivot, pivot+length/4); - SWAP_INDICES(SORT_ARGS, end, end - length/4); - } - if(pivot - start > 7*(length/8) ) { - SWAP_INDICES(SORT_ARGS, start, start + length/4); - SWAP_INDICES(SORT_ARGS, pivot-1, pivot-1 - length/4); - } + if (!first_pass || + !(PREFIXNAME(LimitedInsertion)(SORT_ARGS, pivot, end) == True)) { + PREFIXNAME(QuickSort)(SORT_ARGS, pivot, end, depth - 1); + } } -void PREFIXNAME(QuickSort) (SORT_FUNC_ARGS, Int start, Int end, Int depth) -{ - Int pivot, first_pass; - - if(end - start < 24) { - PREFIXNAME(Insertion)(SORT_ARGS, start, end); - return; - } - - /* If quicksort seems to be degrading into O(n^2), escape to shellsort */ - if(depth <= 0) { - PREFIXNAME(Shell)(SORT_ARGS, start, end); - return; - } - - first_pass = PREFIXNAME(Partition)(SORT_ARGS, start, end, &pivot); - PREFIXNAME(CheckBadPivot)(SORT_ARGS, start, end, pivot); - if(!first_pass || ! (PREFIXNAME(LimitedInsertion)(SORT_ARGS, start, pivot-1) == True)) { - PREFIXNAME(QuickSort)(SORT_ARGS, start, pivot-1, depth-1); - } - - if(!first_pass || ! (PREFIXNAME(LimitedInsertion)(SORT_ARGS, pivot, end) == True)) { - PREFIXNAME(QuickSort)(SORT_ARGS, pivot, end, depth-1); - } + +void SORT_FUNC_NAME(SORT_FUNC_ARGS) { + Int len = SORT_LEN_LIST(); + PREFIXNAME(QuickSort)(SORT_ARGS, 1, len, CLog2Int(len) * 2 + 2); } +// Merge the consecutive ranges [b1..e1] and [e1+1..e2] in place, +// Using the temporary buffer 'tempbuf'. +void PREFIXNAME(MergeRanges)(SORT_FUNC_ARGS, Int b1, Int e1, Int e2, + Obj tempbuf) { + Int pos1 = b1; + Int pos2 = e1 + 1; + Int resultpos = 1; + Int i; + + while (pos1 <= e1 && pos2 <= e2) { + if (PREFIXNAME(CompIndices)(SORT_ARGS, pos2, pos1)) { + SORT_CREATE_LOCAL(t); + SORT_ASS_LIST_TO_LOCAL(t, pos2); + SORT_ASS_LOCAL_TO_BUF(tempbuf, resultpos, t); + pos2++; + resultpos++; + } else { + SORT_CREATE_LOCAL(t); + SORT_ASS_LIST_TO_LOCAL(t, pos1); + SORT_ASS_LOCAL_TO_BUF(tempbuf, resultpos, t); + pos1++; + resultpos++; + } + } + + while (pos1 <= e1) { + SORT_CREATE_LOCAL(t); + SORT_ASS_LIST_TO_LOCAL(t, pos1); + SORT_ASS_LOCAL_TO_BUF(tempbuf, resultpos, t); + pos1++; + resultpos++; + } + + while (pos2 <= e2) { + SORT_CREATE_LOCAL(t); + SORT_ASS_LIST_TO_LOCAL(t, pos2); + SORT_ASS_LOCAL_TO_BUF(tempbuf, resultpos, t); + pos2++; + resultpos++; + } -void SORT_FUNC_NAME(SORT_FUNC_ARGS) -{ - Int len = SORT_LEN_LIST(); - PREFIXNAME(QuickSort)(SORT_ARGS, 1, len, CLog2Int(len)*2 + 2); + for (i = 1; i < resultpos; ++i) { + SORT_CREATE_LOCAL(t); + SORT_ASS_BUF_TO_LOCAL(tempbuf, t, i); + SORT_ASS_LOCAL_TO_LIST(b1 + i - 1, t); + } } +void PREFIXNAME(Merge)(SORT_FUNC_ARGS) { + Int len = SORT_LEN_LIST(); + Obj buf = SORT_CREATE_TEMP_BUFFER(len); + + Int stepsize = 24; + Int i; + /* begin with splitting into small steps we insertion sort */ + for (i = 1; i + stepsize <= len; i += stepsize) { + PREFIXNAME(Insertion)(SORT_ARGS, i, i + stepsize - 1); + } + if (i < len) { + PREFIXNAME(Insertion)(SORT_ARGS, i, len); + } + + while (stepsize < len) { + for (i = 1; i + stepsize * 2 <= len; i += stepsize * 2) { + PREFIXNAME(MergeRanges)(SORT_ARGS, i, i+stepsize-1, i+stepsize*2-1, buf); + } + if (i + stepsize <= len) { + PREFIXNAME(MergeRanges)(SORT_ARGS, i, i + stepsize - 1, len, buf); + } + stepsize *= 2; + } +} #undef PREFIXNAME #undef COMP_INDICES diff --git a/tst/teststandard/sort.tst b/tst/teststandard/sort.tst index 1276d1a191..1a52a4b1a1 100644 --- a/tst/teststandard/sort.tst +++ b/tst/teststandard/sort.tst @@ -19,6 +19,9 @@ gap> CheckSort := function(list, sorted) > if listcpy <> Reversed(sorted) then Print("Fail 4 : ", listcpy, list, sorted); fi; > end;; gap> for i in [0..500] do CheckSort([1..i],[1..i]); od; + +# Want to make sure GAP doesn't know the list is sorted +gap> for i in [0..500] do CheckSort(List([1..i],x->x),[1..i]); od; gap> for i in [0..500] do CheckSort([-i..i],[-i..i]); od; gap> for i in [0..500] do CheckSort([i,i-1..-i],[-i..i]); od; gap> for i in [0..500] do diff --git a/tst/teststandard/stablesort.tst b/tst/teststandard/stablesort.tst new file mode 100644 index 0000000000..a4a46c7649 --- /dev/null +++ b/tst/teststandard/stablesort.tst @@ -0,0 +1,158 @@ +# This aims to test the various implementations of StableSort. +# There are a few cases we must cover: +# +# * We can choose if we pass a comparator +# * We can do Sort or SortParallel +# * We specialise for plain lists +# Most of these checks are generate a whole bunch of random tests +# +# We check StableSort implements Sort correctly +# and also have a special 'stability' check. + + +# We check stability in two ways, by building pairs, and also +# by using a comparator. We need this second case to ensure we +# check some non-plists. Also need to test with and without comparators. +gap> START_TEST("stablesort.tst"); +gap> CheckStabilityPair := function(inputlist) +> local pairlist, listcpy1, listcpy2, intlist; +> pairlist := List([1..Length(inputlist)], x -> [inputlist[x],x]); +> listcpy1 := DEEP_COPY_OBJ(pairlist); +> listcpy2 := DEEP_COPY_OBJ(pairlist); +> Sort(listcpy1); +> StableSort(listcpy2, function(x,y) return x[1] < y[1]; end); +> if listcpy1 <> listcpy2 then +> Print("failed Stability test 1:", inputlist, listcpy1, listcpy2); +> fi; +> listcpy2 := DEEP_COPY_OBJ(pairlist); +> intlist := [1..Length(inputlist)]; +> StableSortParallel(listcpy2, intlist, function(x,y) return x[1] < y[1]; end); +> if intlist <> List(listcpy2, x -> x[2]) then +> Print("failed Stability test 2:", listcpy2, intlist); +> fi; +> end;; + +# The function checks non-plists (in particular, strings) +gap> CheckStabilityStr := function(inputlist) +> local listcpy, listcpy1, listcpy2, intlist; +> listcpy := DEEP_COPY_OBJ(inputlist); +> Sort(listcpy, function(x,y) return IntChar(x) mod 4 < IntChar(y) mod 4; end); +> listcpy1 := DEEP_COPY_OBJ(listcpy); +> listcpy2 := DEEP_COPY_OBJ(listcpy); +> Sort(listcpy1); +> StableSort(listcpy2, function(x,y) return IntChar(x)/4 < IntChar(y)/4; end); +> if listcpy1 <> listcpy2 then +> Print("failed Stability test 1:", inputlist, listcpy1, listcpy2); +> fi; +> listcpy := DEEP_COPY_OBJ(inputlist); +> intlist := [1..Length(inputlist)]; +> StableSortParallel(listcpy, intlist); +> if not IsSortedList(List([1..Length(inputlist)], i -> [listcpy[i], intlist[i]])) then +> Print("failed Stability test 4:", listcpy, intlist); +> fi; +> listcpy := DEEP_COPY_OBJ(inputlist); +> intlist := [1..Length(inputlist)]; +> StableSortParallel(listcpy, intlist, function(x,y) return x < y; end); +> if not IsSortedList(List([1..Length(inputlist)], i -> [listcpy[i], intlist[i]])) then +> Print("failed Stability test 4:", listcpy, intlist); +> fi; +> end;; +gap> CheckSort := function(list, sorted) +> local listcpy, perm; +> listcpy := DEEP_COPY_OBJ(list); StableSort(listcpy); +> if listcpy <> sorted then Print("Fail 1 : ", listcpy, list, sorted); fi; +> listcpy := DEEP_COPY_OBJ(list); StableSort(listcpy, function (a,b) return a < b; end); +> if listcpy <> sorted then Print("Fail 2 : ", listcpy, list, sorted); fi; +> listcpy := DEEP_COPY_OBJ(list); StableSort(listcpy, function (a,b) return a <= b; end); +> if listcpy <> sorted then Print("Fail 3 : ", listcpy, list, sorted); fi; +> listcpy := DEEP_COPY_OBJ(list); Sort(listcpy, function (a,b) return a > b; end); +> if listcpy <> Reversed(sorted) then Print("Fail 4 : ", listcpy, list, sorted); fi; +> CheckStabilityPair(list); +> if IsStringRep(list) then +> CheckStabilityStr(list); +> fi; +> end;; +gap> for i in [0..500] do CheckSort([1..i],[1..i]); od; + +# Want to make sure GAP doesn't know the list is sorted +gap> for i in [0..500] do CheckSort(List([1..i],x->x),[1..i]); od; +gap> for i in [0..500] do CheckSort([-i..i],[-i..i]); od; +gap> for i in [0..500] do CheckSort([i,i-1..-i],[-i..i]); od; +gap> for i in [0..500] do +> for j in [0..10] do +> CheckSort(Shuffle([1..i]), [1..i]); +> od; +> od; +gap> for i in [0..100] do +> for j in [0..10] do +> l := Concatenation(List([0..j], x -> List([0..i], y -> x))); +> l2 := Shuffle(List(l)); +> CheckSort(l2, l); +> od; +> od; + +# Need to test something which are not plists. Strings are a good choice. +gap> for i in [0..100] do +> for j in [0..10] do +> l := ""; +> for a in [1..j] do for b in [1..i] do +> Add(l, CHARS_LALPHA[a]); +> od; od; +> l2 := Shuffle(List(l)); +> if not(IsStringRep(l)) or not(IsStringRep(l2)) then +> Print("StringFail"); +> fi; +> CheckSort(l2, l); +> od; +> od; + +# Let test bool lists too! +gap> for i in [0..100] do +> for j in [0..10] do +> l := BlistList([1..i+j],[1..i]); +> l2 := Shuffle(List(l)); +> if not(IsBlistRep(l)) or not(IsBlistRep(l2)) then +> Print("BlistFail"); +> fi; +> CheckSort(l2, l); +> od; +> od; + +# Test SortParallel +gap> CheckSortParallel := function(sortedlist, perm, maxval) +> local list1, list2, listcpy, list1orig; +> # This slightly weird code is because I want to preserve +> # The type of the input list +> list1orig := DEEP_COPY_OBJ(sortedlist); +> for i in [1..maxval] do +> list1orig[i] := sortedlist[i^perm]; +> od; +> list1 := DEEP_COPY_OBJ(list1orig); +> list2 := List([1..maxval], x -> Random([1..100])); +> listcpy := List(list2); +> SortParallel(list1, list2); +> if ForAny([1..maxval], x -> list2[x^perm] <> listcpy[x]) then +> Print("failed SortParallel 1", perm, maxval, list2); +> fi; +> list1 := DEEP_COPY_OBJ(list1orig); +> listcpy := List(list2); +> SortParallel(list1, list2, function (a,b) return a <= b; end); +> if ForAny([1..maxval], x -> list2[x^perm] <> listcpy[x]) then +> Print("failed SortParallel 2", perm, maxval, list2); +> fi; +> end;; +gap> for i in [0..100] do +> for j in [0..10] do +> CheckSortParallel([1..i],Random(SymmetricGroup([1..i])), i); +> od; +> od; + +# Just sanity check I really am making string reps +gap> IsStringRep(CHARS_LALPHA{[1..0]}) and IsStringRep(CHARS_LALPHA{[1..10]}); +true +gap> for i in [0..26] do +> for j in [0..10] do +> CheckSortParallel(CHARS_LALPHA{[1..i]},Random(SymmetricGroup([1..i])), i); +> od; +> od; +gap> STOP_TEST("stablesort.tst", 0);