From 1d345639e557138a5a17bcc883292f847bd20227 Mon Sep 17 00:00:00 2001 From: Chris Jefferson Date: Thu, 11 Feb 2016 08:55:19 +0000 Subject: [PATCH] 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);