diff --git a/Makefile.in b/Makefile.in index c3feba08c..37440b027 100644 --- a/Makefile.in +++ b/Makefile.in @@ -1,7 +1,7 @@ -# Makefile.in generated by automake 1.16.2 from Makefile.am. +# Makefile.in generated by automake 1.16.5 from Makefile.am. # @configure_input@ -# Copyright (C) 1994-2020 Free Software Foundation, Inc. +# Copyright (C) 1994-2021 Free Software Foundation, Inc. # This Makefile.in is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, @@ -154,16 +154,13 @@ am__define_uniq_tagged_files = \ unique=`for i in $$list; do \ if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ done | $(am__uniquify_input)` -ETAGS = etags -CTAGS = ctags -CSCOPE = cscope DIST_SUBDIRS = $(SUBDIRS) am__DIST_COMMON = $(srcdir)/Makefile.in \ $(top_srcdir)/acscripts/compile \ $(top_srcdir)/acscripts/config.guess \ $(top_srcdir)/acscripts/config.sub \ $(top_srcdir)/acscripts/install-sh \ - $(top_srcdir)/acscripts/missing AUTHORS COPYING \ + $(top_srcdir)/acscripts/missing AUTHORS COPYING README.md \ acscripts/compile acscripts/config.guess acscripts/config.sub \ acscripts/depcomp acscripts/install-sh acscripts/missing DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) @@ -204,6 +201,8 @@ am__relativize = \ DIST_ARCHIVES = $(distdir).tar.gz GZIP_ENV = --best DIST_TARGETS = dist-gzip +# Exists only to be overridden by the user if desired. +AM_DISTCHECK_DVI_TARGET = dvi distuninstallcheck_listfiles = find . -type f -print am__distuninstallcheck_listfiles = $(distuninstallcheck_listfiles) \ | sed 's|^\./|$(prefix)/|' | grep -v '$(infodir)/dir$$' @@ -219,6 +218,8 @@ CC = @CC@ CCDEPMODE = @CCDEPMODE@ CFLAGS = @CFLAGS@ CPPFLAGS = @CPPFLAGS@ +CSCOPE = @CSCOPE@ +CTAGS = @CTAGS@ CXX = @CXX@ CXXDEPMODE = @CXXDEPMODE@ CXXFLAGS = @CXXFLAGS@ @@ -228,6 +229,7 @@ DEPDIR = @DEPDIR@ ECHO_C = @ECHO_C@ ECHO_N = @ECHO_N@ ECHO_T = @ECHO_T@ +ETAGS = @ETAGS@ EXEEXT = @EXEEXT@ INSTALL = @INSTALL@ INSTALL_DATA = @INSTALL_DATA@ @@ -476,7 +478,6 @@ cscopelist-am: $(am__tagged_files) distclean-tags: -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags -rm -f cscope.out cscope.in.out cscope.po.out cscope.files - distdir: $(BUILT_SOURCES) $(MAKE) $(AM_MAKEFLAGS) distdir-am @@ -623,7 +624,7 @@ distcheck: dist $(DISTCHECK_CONFIGURE_FLAGS) \ --srcdir=../.. --prefix="$$dc_install_base" \ && $(MAKE) $(AM_MAKEFLAGS) \ - && $(MAKE) $(AM_MAKEFLAGS) dvi \ + && $(MAKE) $(AM_MAKEFLAGS) $(AM_DISTCHECK_DVI_TARGET) \ && $(MAKE) $(AM_MAKEFLAGS) check \ && $(MAKE) $(AM_MAKEFLAGS) install \ && $(MAKE) $(AM_MAKEFLAGS) installcheck \ diff --git a/aclocal.m4 b/aclocal.m4 index b385b22aa..0ec3a7819 100644 --- a/aclocal.m4 +++ b/aclocal.m4 @@ -1,6 +1,6 @@ -# generated automatically by aclocal 1.16.2 -*- Autoconf -*- +# generated automatically by aclocal 1.16.5 -*- Autoconf -*- -# Copyright (C) 1996-2020 Free Software Foundation, Inc. +# Copyright (C) 1996-2021 Free Software Foundation, Inc. # This file is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, @@ -20,8 +20,8 @@ You have another version of autoconf. It may work, but is not guaranteed to. If you have problems, you may need to regenerate the build system entirely. To do so, use the procedure documented by the package, typically 'autoreconf'.])]) -# pkg.m4 - Macros to locate and utilise pkg-config. -*- Autoconf -*- -# serial 11 (pkg-config-0.29.1) +# pkg.m4 - Macros to locate and use pkg-config. -*- Autoconf -*- +# serial 12 (pkg-config-0.29.2) dnl Copyright © 2004 Scott James Remnant . dnl Copyright © 2012-2015 Dan Nicholson @@ -63,13 +63,13 @@ dnl dnl See the "Since" comment for each macro you use to see what version dnl of the macros you require. m4_defun([PKG_PREREQ], -[m4_define([PKG_MACROS_VERSION], [0.29.1]) +[m4_define([PKG_MACROS_VERSION], [0.29.2]) m4_if(m4_version_compare(PKG_MACROS_VERSION, [$1]), -1, [m4_fatal([pkg.m4 version $1 or higher is required but ]PKG_MACROS_VERSION[ found])]) ])dnl PKG_PREREQ -dnl PKG_PROG_PKG_CONFIG([MIN-VERSION]) -dnl ---------------------------------- +dnl PKG_PROG_PKG_CONFIG([MIN-VERSION], [ACTION-IF-NOT-FOUND]) +dnl --------------------------------------------------------- dnl Since: 0.16 dnl dnl Search for the pkg-config tool and set the PKG_CONFIG variable to @@ -77,6 +77,12 @@ dnl first found in the path. Checks that the version of pkg-config found dnl is at least MIN-VERSION. If MIN-VERSION is not specified, 0.9.0 is dnl used since that's the first version where most current features of dnl pkg-config existed. +dnl +dnl If pkg-config is not found or older than specified, it will result +dnl in an empty PKG_CONFIG variable. To avoid widespread issues with +dnl scripts not checking it, ACTION-IF-NOT-FOUND defaults to aborting. +dnl You can specify [PKG_CONFIG=false] as an action instead, which would +dnl result in pkg-config tests failing, but no bogus error messages. AC_DEFUN([PKG_PROG_PKG_CONFIG], [m4_pattern_forbid([^_?PKG_[A-Z_]+$]) m4_pattern_allow([^PKG_CONFIG(_(PATH|LIBDIR|SYSROOT_DIR|ALLOW_SYSTEM_(CFLAGS|LIBS)))?$]) @@ -97,6 +103,9 @@ if test -n "$PKG_CONFIG"; then AC_MSG_RESULT([no]) PKG_CONFIG="" fi +fi +if test -z "$PKG_CONFIG"; then + m4_default([$2], [AC_MSG_ERROR([pkg-config not found])]) fi[]dnl ])dnl PKG_PROG_PKG_CONFIG @@ -108,7 +117,7 @@ dnl Check to see whether a particular set of modules exists. Similar to dnl PKG_CHECK_MODULES(), but does not set variables or print errors. dnl dnl Please remember that m4 expands AC_REQUIRE([PKG_PROG_PKG_CONFIG]) -dnl only at the first occurence in configure.ac, so if the first place +dnl only at the first occurrence in configure.ac, so if the first place dnl it's called might be skipped (such as if it is within an "if", you dnl have to call PKG_CHECK_EXISTS manually AC_DEFUN([PKG_CHECK_EXISTS], @@ -164,7 +173,7 @@ AC_ARG_VAR([$1][_CFLAGS], [C compiler flags for $1, overriding pkg-config])dnl AC_ARG_VAR([$1][_LIBS], [linker flags for $1, overriding pkg-config])dnl pkg_failed=no -AC_MSG_CHECKING([for $1]) +AC_MSG_CHECKING([for $2]) _PKG_CONFIG([$1][_CFLAGS], [cflags], [$2]) _PKG_CONFIG([$1][_LIBS], [libs], [$2]) @@ -174,17 +183,17 @@ and $1[]_LIBS to avoid the need to call pkg-config. See the pkg-config man page for more details.]) if test $pkg_failed = yes; then - AC_MSG_RESULT([no]) + AC_MSG_RESULT([no]) _PKG_SHORT_ERRORS_SUPPORTED if test $_pkg_short_errors_supported = yes; then - $1[]_PKG_ERRORS=`$PKG_CONFIG --short-errors --print-errors --cflags --libs "$2" 2>&1` - else - $1[]_PKG_ERRORS=`$PKG_CONFIG --print-errors --cflags --libs "$2" 2>&1` + $1[]_PKG_ERRORS=`$PKG_CONFIG --short-errors --print-errors --cflags --libs "$2" 2>&1` + else + $1[]_PKG_ERRORS=`$PKG_CONFIG --print-errors --cflags --libs "$2" 2>&1` fi - # Put the nasty error message in config.log where it belongs - echo "$$1[]_PKG_ERRORS" >&AS_MESSAGE_LOG_FD + # Put the nasty error message in config.log where it belongs + echo "$$1[]_PKG_ERRORS" >&AS_MESSAGE_LOG_FD - m4_default([$4], [AC_MSG_ERROR( + m4_default([$4], [AC_MSG_ERROR( [Package requirements ($2) were not met: $$1_PKG_ERRORS @@ -195,8 +204,8 @@ installed software in a non-standard prefix. _PKG_TEXT])[]dnl ]) elif test $pkg_failed = untried; then - AC_MSG_RESULT([no]) - m4_default([$4], [AC_MSG_FAILURE( + AC_MSG_RESULT([no]) + m4_default([$4], [AC_MSG_FAILURE( [The pkg-config script could not be found or is too old. Make sure it is in your PATH or set the PKG_CONFIG environment variable to the full path to pkg-config. @@ -206,10 +215,10 @@ _PKG_TEXT To get pkg-config, see .])[]dnl ]) else - $1[]_CFLAGS=$pkg_cv_[]$1[]_CFLAGS - $1[]_LIBS=$pkg_cv_[]$1[]_LIBS + $1[]_CFLAGS=$pkg_cv_[]$1[]_CFLAGS + $1[]_LIBS=$pkg_cv_[]$1[]_LIBS AC_MSG_RESULT([yes]) - $3 + $3 fi[]dnl ])dnl PKG_CHECK_MODULES @@ -373,14 +382,17 @@ AS_IF([test "$AS_TR_SH([with_]m4_tolower([$1]))" = "yes"], # # Changelog: # * also look for SDL2.framework under Mac OS X +# * removed HP/UX 9 support. +# * updated for newer autoconf. +# * (v3) use $PKG_CONFIG for pkg-config cross-compiling support -# serial 1 +# serial 3 dnl AM_PATH_SDL2([MINIMUM-VERSION, [ACTION-IF-FOUND [, ACTION-IF-NOT-FOUND]]]) dnl Test for SDL, and define SDL_CFLAGS and SDL_LIBS dnl AC_DEFUN([AM_PATH_SDL2], -[dnl +[dnl dnl Get the cflags and libraries from the sdl2-config script dnl AC_ARG_WITH(sdl-prefix,[ --with-sdl-prefix=PFX Prefix where SDL is installed (optional)], @@ -418,7 +430,7 @@ AC_ARG_VAR(SDL2_FRAMEWORK, [Path to SDL2.framework]) if test "x$sdl_pc" = xyes ; then no_sdl="" - SDL2_CONFIG="pkg-config sdl2" + SDL2_CONFIG="$PKG_CONFIG sdl2" else as_save_PATH="$PATH" if test "x$prefix" != xNONE && test "$cross_compiling" != yes; then @@ -434,8 +446,8 @@ AC_ARG_VAR(SDL2_FRAMEWORK, [Path to SDL2.framework]) sdl_framework=$SDL2_FRAMEWORK else for d in / ~/ /System/; do - if test -d "$dLibrary/Frameworks/SDL2.framework"; then - sdl_framework="$dLibrary/Frameworks/SDL2.framework" + if test -d "${d}Library/Frameworks/SDL2.framework"; then + sdl_framework="${d}Library/Frameworks/SDL2.framework" fi done fi @@ -475,41 +487,19 @@ dnl Now check if the installed SDL is sufficiently new. (Also sanity dnl checks the results of sdl2-config to some extent dnl rm -f conf.sdltest - AC_TRY_RUN([ + AC_RUN_IFELSE([AC_LANG_SOURCE([[ #include #include -#include #include "SDL.h" -char* -my_strdup (char *str) -{ - char *new_str; - - if (str) - { - new_str = (char *)malloc ((strlen (str) + 1) * sizeof(char)); - strcpy (new_str, str); - } - else - new_str = NULL; - - return new_str; -} - int main (int argc, char *argv[]) { int major, minor, micro; - char *tmp_version; + FILE *fp = fopen("conf.sdltest", "w"); - /* This hangs on some systems (?) - system ("touch conf.sdltest"); - */ - { FILE *fp = fopen("conf.sdltest", "a"); if ( fp ) fclose(fp); } + if (fp) fclose(fp); - /* HP/UX 9 (%@#!) writes to sscanf strings */ - tmp_version = my_strdup("$min_sdl_version"); - if (sscanf(tmp_version, "%d.%d.%d", &major, &minor, µ) != 3) { + if (sscanf("$min_sdl_version", "%d.%d.%d", &major, &minor, µ) != 3) { printf("%s, bad version string\n", "$min_sdl_version"); exit(1); } @@ -532,7 +522,7 @@ int main (int argc, char *argv[]) } } -],, no_sdl=yes,[echo $ac_n "cross compiling; assumed OK... $ac_c"]) +]])], [], [no_sdl=yes], [echo $ac_n "cross compiling; assumed OK... $ac_c"]) CFLAGS="$ac_save_CFLAGS" CXXFLAGS="$ac_save_CXXFLAGS" LIBS="$ac_save_LIBS" @@ -563,7 +553,7 @@ int main (int argc, char *argv[]) CFLAGS="$CFLAGS $SDL_CFLAGS" CXXFLAGS="$CXXFLAGS $SDL_CFLAGS" LIBS="$LIBS $SDL_LIBS" - AC_TRY_LINK([ + AC_LINK_IFELSE([AC_LANG_PROGRAM([[ #include #include "SDL.h" @@ -571,7 +561,7 @@ int main(int argc, char *argv[]) { return 0; } #undef main #define main K_and_R_C_main -], [ return 0; ], +]], [[ return 0; ]])], [ echo "*** The test program compiled, but did not run. This usually means" echo "*** that the run-time linker is not finding SDL or finding the wrong" echo "*** version of SDL. If it is not finding SDL, you'll need to set your" @@ -606,6 +596,9 @@ dnl Bob McCown (Mac-testing) dnl Creation date: 24/11/2001 dnl --------------------------------------------------------------------------- +dnl Increment this when changing this file. +# serial 42 + dnl =========================================================================== dnl Table of Contents of this macro file: dnl ------------------------------------- @@ -665,7 +658,7 @@ dnl If you want to support standard --enable-debug/unicode/shared options, you dnl may do the following: dnl dnl ... -dnl AC_CANONICAL_SYSTEM +dnl AC_CANONICAL_TARGET dnl dnl # define configure options dnl WX_CONFIG_OPTIONS @@ -743,7 +736,8 @@ AC_DEFUN([_WX_PRIVATE_CHECK_VERSION], dnl --------------------------------------------------------------------------- dnl WX_CONFIG_CHECK(VERSION, [ACTION-IF-FOUND [, ACTION-IF-NOT-FOUND -dnl [, WX-LIBS [, ADDITIONAL-WX-CONFIG-FLAGS]]]]) +dnl [, WX-LIBS [, ADDITIONAL-WX-CONFIG-FLAGS +dnl [, WX-OPTIONAL-LIBS]]]]]) dnl dnl Test for wxWidgets, and define WX_C*FLAGS, WX_LIBS and WX_LIBS_STATIC dnl (the latter is for static linking against wxWidgets). Set WX_CONFIG_NAME @@ -760,6 +754,10 @@ dnl Optional ADDITIONAL-WX-CONFIG-FLAGS argument is appended to wx-config dnl invocation command in present. It can be used to fine-tune lookup of dnl best wxWidgets build available. dnl +dnl Optional WX-OPTIONAL-LIBS argument contains comma- or space-separated list +dnl of wxWidgets libraries to link against if they are available. +dnl WX-OPTIONAL-LIBS is supported on version 2.9.0 and later. +dnl dnl Example use: dnl WX_CONFIG_CHECK([2.6.0], [wxWin=1], [wxWin=0], [html,core,net] dnl [--unicode --debug]) @@ -811,8 +809,8 @@ AC_DEFUN([WX_CONFIG_CHECK], AC_MSG_CHECKING([for wxWidgets version >= $min_wx_version ($5)]) fi - dnl don't add the libraries ($4) to this variable as this would result in - dnl an error when it's used with --version below + dnl don't add the libraries (4th argument) to this variable as this would + dnl result in an error when it's used with --version below WX_CONFIG_WITH_ARGS="$WX_CONFIG_PATH $wx_config_args $5" WX_VERSION=`$WX_CONFIG_WITH_ARGS --version 2>/dev/null` @@ -836,14 +834,20 @@ AC_DEFUN([WX_CONFIG_CHECK], if test -n "$wx_ver_ok"; then AC_MSG_RESULT(yes (version $WX_VERSION)) - WX_LIBS=`$WX_CONFIG_WITH_ARGS --libs $4` + + wx_optional_libs="" + _WX_PRIVATE_CHECK_VERSION(2,9,0) + if test -n "$wx_ver_ok" -a -n "$6"; then + wx_optional_libs="--optional-libs $6" + fi + WX_LIBS=`$WX_CONFIG_WITH_ARGS --libs $4 $wx_optional_libs` dnl is this even still appropriate? --static is a real option now dnl and WX_CONFIG_WITH_ARGS is likely to contain it if that is dnl what the user actually wants, making this redundant at best. dnl For now keep it in case anyone actually used it in the past. AC_MSG_CHECKING([for wxWidgets static library]) - WX_LIBS_STATIC=`$WX_CONFIG_WITH_ARGS --static --libs $4 2>/dev/null` + WX_LIBS_STATIC=`$WX_CONFIG_WITH_ARGS --static --libs $4 $wx_optional_libs 2>/dev/null` if test "x$WX_LIBS_STATIC" = "x"; then AC_MSG_RESULT(no) else @@ -1106,7 +1110,7 @@ dnl $5 = additional action to do in case option is given with "yes" value dnl --------------------------------------------------------------------------- AC_DEFUN([WX_ARG_ENABLE_YESNOAUTO], [AC_ARG_ENABLE($1, - AC_HELP_STRING([--enable-$1], [$3 (default is $4)]), + AS_HELP_STRING([--enable-$1],[$3 (default is $4)]), [], [enableval="$4"]) dnl Show a message to the user about this option @@ -1120,7 +1124,7 @@ AC_DEFUN([WX_ARG_ENABLE_YESNOAUTO], $2=0 elif test "$enableval" = "auto" ; then AC_MSG_RESULT([will be automatically detected]) - $2="auto" + $2="" else AC_MSG_ERROR([ Unrecognized option value (allowed values: yes, no, auto) @@ -1130,7 +1134,7 @@ AC_DEFUN([WX_ARG_ENABLE_YESNOAUTO], AC_DEFUN([WX_ARG_WITH_YESNOAUTO], [AC_ARG_WITH($1, - AC_HELP_STRING([--with-$1], [$3 (default is $4)]), + AS_HELP_STRING([--with-$1],[$3 (default is $4)]), [], [withval="$4"]) dnl Show a message to the user about this option @@ -1146,7 +1150,7 @@ AC_DEFUN([WX_ARG_WITH_YESNOAUTO], $2=0 elif test "$withval" = "auto" ; then AC_MSG_RESULT([will be automatically detected]) - $2="auto" + $2="" else AC_MSG_ERROR([ Unrecognized option value (allowed values: yes, auto) @@ -1190,25 +1194,24 @@ AC_DEFUN([WX_STANDARD_OPTIONS], ifelse(index([$1], [toolkit]), [-1],, [ AC_ARG_WITH([toolkit], - AC_HELP_STRING([--with-toolkit], - [Build against a specific wxWidgets toolkit (default is auto)]), + AS_HELP_STRING([--with-toolkit],[Build against a specific wxWidgets toolkit (default is auto)]), [], [withval="auto"]) dnl Show a message to the user about this option AC_MSG_CHECKING([for the --with-toolkit option]) if test "$withval" = "auto" ; then AC_MSG_RESULT([will be automatically detected]) - TOOLKIT="auto" + TOOLKIT="" else TOOLKIT="$withval" dnl PORT must be one of the allowed values - if test "$TOOLKIT" != "gtk1" -a "$TOOLKIT" != "gtk2" -a \ + if test "$TOOLKIT" != "gtk1" -a "$TOOLKIT" != "gtk2" -a "$TOOLKIT" != "gtk3" -a \ "$TOOLKIT" != "msw" -a "$TOOLKIT" != "motif" -a \ "$TOOLKIT" != "osx_carbon" -a "$TOOLKIT" != "osx_cocoa" -a \ - "$TOOLKIT" != "dfb" -a "$TOOLKIT" != "x11"; then + "$TOOLKIT" != "dfb" -a "$TOOLKIT" != "x11" -a "$TOOLKIT" != "base"; then AC_MSG_ERROR([ - Unrecognized option value (allowed values: auto, gtk1, gtk2, msw, motif, osx_carbon, osx_cocoa, dfb, x11) + Unrecognized option value (allowed values: auto, gtk1, gtk2, gtk3, msw, motif, osx_carbon, osx_cocoa, dfb, x11, base) ]) fi @@ -1264,15 +1267,14 @@ AC_DEFUN([WX_STANDARD_OPTIONS], ifelse(index([$1], [wxversion]), [-1],, [ AC_ARG_WITH([wxversion], - AC_HELP_STRING([--with-wxversion], - [Build against a specific version of wxWidgets (default is auto)]), + AS_HELP_STRING([--with-wxversion],[Build against a specific version of wxWidgets (default is auto)]), [], [withval="auto"]) dnl Show a message to the user about this option AC_MSG_CHECKING([for the --with-wxversion option]) if test "$withval" = "auto" ; then AC_MSG_RESULT([will be automatically detected]) - WX_RELEASE="auto" + WX_RELEASE="" else wx_requested_major_version=`echo $withval | \ @@ -1307,7 +1309,7 @@ dnl --------------------------------------------------------------------------- dnl WX_CONVERT_STANDARD_OPTIONS_TO_WXCONFIG_FLAGS dnl dnl Sets the WXCONFIG_FLAGS string using the SHARED,DEBUG,UNICODE variable values -dnl which are different from "auto". +dnl which were specified. dnl Thus this macro needs to be called only once all options have been set. dnl --------------------------------------------------------------------------- AC_DEFUN([WX_CONVERT_STANDARD_OPTIONS_TO_WXCONFIG_FLAGS], @@ -1331,11 +1333,11 @@ AC_DEFUN([WX_CONVERT_STANDARD_OPTIONS_TO_WXCONFIG_FLAGS], WXCONFIG_FLAGS="$WXCONFIG_FLAGS""--unicode=no " fi - if test "$TOOLKIT" != "auto" ; then + if test -n "$TOOLKIT" ; then WXCONFIG_FLAGS="$WXCONFIG_FLAGS""--toolkit=$TOOLKIT " fi - if test "$WX_RELEASE" != "auto" ; then + if test -n "$WX_RELEASE" ; then WXCONFIG_FLAGS="$WXCONFIG_FLAGS""--version=$WX_RELEASE " fi @@ -1349,16 +1351,16 @@ AC_DEFUN([WX_CONVERT_STANDARD_OPTIONS_TO_WXCONFIG_FLAGS], dnl --------------------------------------------------------------------------- -dnl _WX_SELECTEDCONFIG_CHECKFOR([RESULTVAR], [STRING], [MSG] -dnl [, ACTION-IF-FOUND [, ACTION-IF-NOT-FOUND]]) +dnl _WX_SELECTEDCONFIG_CHECKFOR([RESULTVAR], [STRING], [MSG]) dnl -dnl Outputs the given MSG. Then searches the given STRING in the wxWidgets -dnl additional CPP flags and put the result of the search in WX_$RESULTVAR -dnl also adding the "yes" or "no" message result to MSG. +dnl Sets WX_$RESULTVAR to the value of $RESULTVAR if it's defined. Otherwise, +dnl auto-detect the value by checking for the presence of STRING in +dnl $WX_SELECTEDCONFIG (which is supposed to be set by caller) and set +dnl WX_$RESULTVAR to either 0 or 1, also outputting "yes" or "no" after MSG. dnl --------------------------------------------------------------------------- AC_DEFUN([_WX_SELECTEDCONFIG_CHECKFOR], [ - if test "$$1" = "auto" ; then + if test -z "$$1" ; then dnl The user does not have particular preferences for this option; dnl so we will detect the wxWidgets relative build setting and use it @@ -1373,11 +1375,9 @@ AC_DEFUN([_WX_SELECTEDCONFIG_CHECKFOR], if test "$WX_$1" != "0"; then WX_$1=1 AC_MSG_RESULT([yes]) - ifelse([$4], , :, [$4]) else WX_$1=0 AC_MSG_RESULT([no]) - ifelse([$5], , :, [$5]) fi else @@ -1424,19 +1424,16 @@ AC_DEFUN([WX_DETECT_STANDARD_OPTION_VALUES], echo "[[dbg]] WX_SELECTEDCONFIG: $WX_SELECTEDCONFIG" fi - dnl we could test directly for WX_SHARED with a line like: dnl _WX_SELECTEDCONFIG_CHECKFOR([SHARED], [shared], dnl [if wxWidgets was built in SHARED mode]) dnl but wx-config --selected-config DOES NOT outputs the 'shared' dnl word when wx was built in shared mode; it rather outputs the dnl 'static' word when built in static mode. - if test $WX_SHARED = "1"; then + if test "$WX_SHARED" = "1"; then STATIC=0 - elif test $WX_SHARED = "0"; then + elif test "$WX_SHARED" = "0"; then STATIC=1 - elif test $WX_SHARED = "auto"; then - STATIC="auto" fi dnl Now set the WX_UNICODE, WX_DEBUG, WX_STATIC variables @@ -1459,7 +1456,7 @@ AC_DEFUN([WX_DETECT_STANDARD_OPTION_VALUES], AC_SUBST(WX_SHARED) dnl detect the WX_PORT to use - if test "$TOOLKIT" = "auto" ; then + if test -z "$TOOLKIT" ; then dnl The user does not have particular preferences for this option; dnl so we will detect the wxWidgets relative build setting and use it @@ -1467,22 +1464,26 @@ AC_DEFUN([WX_DETECT_STANDARD_OPTION_VALUES], WX_GTKPORT1=$(expr "$WX_SELECTEDCONFIG" : ".*gtk1.*") WX_GTKPORT2=$(expr "$WX_SELECTEDCONFIG" : ".*gtk2.*") + WX_GTKPORT3=$(expr "$WX_SELECTEDCONFIG" : ".*gtk3.*") WX_MSWPORT=$(expr "$WX_SELECTEDCONFIG" : ".*msw.*") WX_MOTIFPORT=$(expr "$WX_SELECTEDCONFIG" : ".*motif.*") WX_OSXCOCOAPORT=$(expr "$WX_SELECTEDCONFIG" : ".*osx_cocoa.*") WX_OSXCARBONPORT=$(expr "$WX_SELECTEDCONFIG" : ".*osx_carbon.*") WX_X11PORT=$(expr "$WX_SELECTEDCONFIG" : ".*x11.*") WX_DFBPORT=$(expr "$WX_SELECTEDCONFIG" : ".*dfb.*") + WX_BASEPORT=$(expr "$WX_SELECTEDCONFIG" : ".*base.*") WX_PORT="unknown" if test "$WX_GTKPORT1" != "0"; then WX_PORT="gtk1"; fi if test "$WX_GTKPORT2" != "0"; then WX_PORT="gtk2"; fi + if test "$WX_GTKPORT3" != "0"; then WX_PORT="gtk3"; fi if test "$WX_MSWPORT" != "0"; then WX_PORT="msw"; fi if test "$WX_MOTIFPORT" != "0"; then WX_PORT="motif"; fi if test "$WX_OSXCOCOAPORT" != "0"; then WX_PORT="osx_cocoa"; fi if test "$WX_OSXCARBONPORT" != "0"; then WX_PORT="osx_carbon"; fi if test "$WX_X11PORT" != "0"; then WX_PORT="x11"; fi if test "$WX_DFBPORT" != "0"; then WX_PORT="dfb"; fi + if test "$WX_BASEPORT" != "0"; then WX_PORT="base"; fi dnl NOTE: backward-compatible check for wx2.8; in wx2.9 the mac dnl ports are called 'osx_cocoa' and 'osx_carbon' (see above) @@ -1500,14 +1501,8 @@ AC_DEFUN([WX_DETECT_STANDARD_OPTION_VALUES], AC_MSG_RESULT([$WX_PORT]) else - dnl Use the setting given by the user - if test -z "$TOOLKIT" ; then - WX_PORT=$TOOLKIT - else - dnl try with PORT - WX_PORT=$PORT - fi + WX_PORT=$TOOLKIT fi AC_SUBST(WX_PORT) @@ -1538,35 +1533,29 @@ AC_DEFUN([WX_DETECT_STANDARD_OPTION_VALUES], ]) fi - dnl now we can finally update the DEBUG,UNICODE,SHARED options - dnl to their final values if they were set to 'auto' - if test "$DEBUG" = "auto"; then - DEBUG=$WX_DEBUG - fi - if test "$UNICODE" = "auto"; then + dnl now we can finally update the options to their final values if they + dnl were not already set + if test -z "$UNICODE" ; then UNICODE=$WX_UNICODE fi - if test "$SHARED" = "auto"; then + if test -z "$SHARED" ; then SHARED=$WX_SHARED fi - if test "$TOOLKIT" = "auto"; then + if test -z "$TOOLKIT" ; then TOOLKIT=$WX_PORT fi - dnl in case the user needs a BUILD=debug/release var... - if test "$DEBUG" = "1"; then - BUILD="debug" - elif test "$DEBUG" = "0" -o "$DEBUG" = ""; then - BUILD="release" - fi - - dnl respect the DEBUG variable adding the optimize/debug flags + dnl respect the DEBUG variable adding the optimize/debug flags and also + dnl define a BUILD variable in case the user wants to use it + dnl dnl NOTE: the CXXFLAGS are merged together with the CPPFLAGS so we dnl don't need to set them, too if test "$DEBUG" = "1"; then + BUILD="debug" CXXFLAGS="$CXXFLAGS -g -O0" CFLAGS="$CFLAGS -g -O0" - else + elif test "$DEBUG" = "0"; then + BUILD="release" CXXFLAGS="$CXXFLAGS -O2" CFLAGS="$CFLAGS -O2" fi @@ -1659,7 +1648,7 @@ AC_DEFUN([AM_PATH_WXCONFIG], [ ]) AC_DEFUN([AM_PATH_WXRC], [WXRC_CHECK([$1],[$2])]) -# Copyright (C) 2002-2020 Free Software Foundation, Inc. +# Copyright (C) 2002-2021 Free Software Foundation, Inc. # # This file is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, @@ -1674,7 +1663,7 @@ AC_DEFUN([AM_AUTOMAKE_VERSION], [am__api_version='1.16' dnl Some users find AM_AUTOMAKE_VERSION and mistake it for a way to dnl require some minimum version. Point them to the right macro. -m4_if([$1], [1.16.2], [], +m4_if([$1], [1.16.5], [], [AC_FATAL([Do not call $0, use AM_INIT_AUTOMAKE([$1]).])])dnl ]) @@ -1690,14 +1679,14 @@ m4_define([_AM_AUTOCONF_VERSION], []) # Call AM_AUTOMAKE_VERSION and AM_AUTOMAKE_VERSION so they can be traced. # This function is AC_REQUIREd by AM_INIT_AUTOMAKE. AC_DEFUN([AM_SET_CURRENT_AUTOMAKE_VERSION], -[AM_AUTOMAKE_VERSION([1.16.2])dnl +[AM_AUTOMAKE_VERSION([1.16.5])dnl m4_ifndef([AC_AUTOCONF_VERSION], [m4_copy([m4_PACKAGE_VERSION], [AC_AUTOCONF_VERSION])])dnl _AM_AUTOCONF_VERSION(m4_defn([AC_AUTOCONF_VERSION]))]) # AM_AUX_DIR_EXPAND -*- Autoconf -*- -# Copyright (C) 2001-2020 Free Software Foundation, Inc. +# Copyright (C) 2001-2021 Free Software Foundation, Inc. # # This file is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, @@ -1749,7 +1738,7 @@ am_aux_dir=`cd "$ac_aux_dir" && pwd` # AM_CONDITIONAL -*- Autoconf -*- -# Copyright (C) 1997-2020 Free Software Foundation, Inc. +# Copyright (C) 1997-2021 Free Software Foundation, Inc. # # This file is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, @@ -1780,7 +1769,7 @@ AC_CONFIG_COMMANDS_PRE( Usually this means the macro was only invoked conditionally.]]) fi])]) -# Copyright (C) 1999-2020 Free Software Foundation, Inc. +# Copyright (C) 1999-2021 Free Software Foundation, Inc. # # This file is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, @@ -1971,7 +1960,7 @@ _AM_SUBST_NOTMAKE([am__nodep])dnl # Generate code to set up dependency tracking. -*- Autoconf -*- -# Copyright (C) 1999-2020 Free Software Foundation, Inc. +# Copyright (C) 1999-2021 Free Software Foundation, Inc. # # This file is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, @@ -2039,7 +2028,7 @@ AC_DEFUN([AM_OUTPUT_DEPENDENCY_COMMANDS], # Do all the work for Automake. -*- Autoconf -*- -# Copyright (C) 1996-2020 Free Software Foundation, Inc. +# Copyright (C) 1996-2021 Free Software Foundation, Inc. # # This file is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, @@ -2067,6 +2056,10 @@ m4_defn([AC_PROG_CC]) # release and drop the old call support. AC_DEFUN([AM_INIT_AUTOMAKE], [AC_PREREQ([2.65])dnl +m4_ifdef([_$0_ALREADY_INIT], + [m4_fatal([$0 expanded multiple times +]m4_defn([_$0_ALREADY_INIT]))], + [m4_define([_$0_ALREADY_INIT], m4_expansion_stack)])dnl dnl Autoconf wants to disallow AM_ names. We explicitly allow dnl the ones we care about. m4_pattern_allow([^AM_[A-Z]+FLAGS$])dnl @@ -2103,7 +2096,7 @@ m4_ifval([$3], [_AM_SET_OPTION([no-define])])dnl [_AM_SET_OPTIONS([$1])dnl dnl Diagnose old-style AC_INIT with new-style AM_AUTOMAKE_INIT. m4_if( - m4_ifdef([AC_PACKAGE_NAME], [ok]):m4_ifdef([AC_PACKAGE_VERSION], [ok]), + m4_ifset([AC_PACKAGE_NAME], [ok]):m4_ifset([AC_PACKAGE_VERSION], [ok]), [ok:ok],, [m4_fatal([AC_INIT should be called with package and version arguments])])dnl AC_SUBST([PACKAGE], ['AC_PACKAGE_TARNAME'])dnl @@ -2155,6 +2148,20 @@ AC_PROVIDE_IFELSE([AC_PROG_OBJCXX], [m4_define([AC_PROG_OBJCXX], m4_defn([AC_PROG_OBJCXX])[_AM_DEPENDENCIES([OBJCXX])])])dnl ]) +# Variables for tags utilities; see am/tags.am +if test -z "$CTAGS"; then + CTAGS=ctags +fi +AC_SUBST([CTAGS]) +if test -z "$ETAGS"; then + ETAGS=etags +fi +AC_SUBST([ETAGS]) +if test -z "$CSCOPE"; then + CSCOPE=cscope +fi +AC_SUBST([CSCOPE]) + AC_REQUIRE([AM_SILENT_RULES])dnl dnl The testsuite driver may need to know about EXEEXT, so add the dnl 'am__EXEEXT' conditional if _AM_COMPILER_EXEEXT was seen. This @@ -2236,7 +2243,7 @@ for _am_header in $config_headers :; do done echo "timestamp for $_am_arg" >`AS_DIRNAME(["$_am_arg"])`/stamp-h[]$_am_stamp_count]) -# Copyright (C) 2001-2020 Free Software Foundation, Inc. +# Copyright (C) 2001-2021 Free Software Foundation, Inc. # # This file is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, @@ -2257,7 +2264,7 @@ if test x"${install_sh+set}" != xset; then fi AC_SUBST([install_sh])]) -# Copyright (C) 2003-2020 Free Software Foundation, Inc. +# Copyright (C) 2003-2021 Free Software Foundation, Inc. # # This file is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, @@ -2279,7 +2286,7 @@ AC_SUBST([am__leading_dot])]) # Add --enable-maintainer-mode option to configure. -*- Autoconf -*- # From Jim Meyering -# Copyright (C) 1996-2020 Free Software Foundation, Inc. +# Copyright (C) 1996-2021 Free Software Foundation, Inc. # # This file is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, @@ -2314,7 +2321,7 @@ AC_MSG_CHECKING([whether to enable maintainer-specific portions of Makefiles]) # Check to see how 'make' treats includes. -*- Autoconf -*- -# Copyright (C) 2001-2020 Free Software Foundation, Inc. +# Copyright (C) 2001-2021 Free Software Foundation, Inc. # # This file is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, @@ -2357,7 +2364,7 @@ AC_SUBST([am__quote])]) # Fake the existence of programs that GNU maintainers use. -*- Autoconf -*- -# Copyright (C) 1997-2020 Free Software Foundation, Inc. +# Copyright (C) 1997-2021 Free Software Foundation, Inc. # # This file is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, @@ -2378,12 +2385,7 @@ AC_DEFUN([AM_MISSING_HAS_RUN], [AC_REQUIRE([AM_AUX_DIR_EXPAND])dnl AC_REQUIRE_AUX_FILE([missing])dnl if test x"${MISSING+set}" != xset; then - case $am_aux_dir in - *\ * | *\ *) - MISSING="\${SHELL} \"$am_aux_dir/missing\"" ;; - *) - MISSING="\${SHELL} $am_aux_dir/missing" ;; - esac + MISSING="\${SHELL} '$am_aux_dir/missing'" fi # Use eval to expand $SHELL if eval "$MISSING --is-lightweight"; then @@ -2396,7 +2398,7 @@ fi # Helper functions for option handling. -*- Autoconf -*- -# Copyright (C) 2001-2020 Free Software Foundation, Inc. +# Copyright (C) 2001-2021 Free Software Foundation, Inc. # # This file is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, @@ -2425,7 +2427,7 @@ AC_DEFUN([_AM_SET_OPTIONS], AC_DEFUN([_AM_IF_OPTION], [m4_ifset(_AM_MANGLE_OPTION([$1]), [$2], [$3])]) -# Copyright (C) 1999-2020 Free Software Foundation, Inc. +# Copyright (C) 1999-2021 Free Software Foundation, Inc. # # This file is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, @@ -2472,7 +2474,7 @@ AC_LANG_POP([C])]) # For backward compatibility. AC_DEFUN_ONCE([AM_PROG_CC_C_O], [AC_REQUIRE([AC_PROG_CC])]) -# Copyright (C) 2001-2020 Free Software Foundation, Inc. +# Copyright (C) 2001-2021 Free Software Foundation, Inc. # # This file is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, @@ -2491,7 +2493,7 @@ AC_DEFUN([AM_RUN_LOG], # Check to make sure that the build environment is sane. -*- Autoconf -*- -# Copyright (C) 1996-2020 Free Software Foundation, Inc. +# Copyright (C) 1996-2021 Free Software Foundation, Inc. # # This file is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, @@ -2572,7 +2574,7 @@ AC_CONFIG_COMMANDS_PRE( rm -f conftest.file ]) -# Copyright (C) 2009-2020 Free Software Foundation, Inc. +# Copyright (C) 2009-2021 Free Software Foundation, Inc. # # This file is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, @@ -2632,7 +2634,7 @@ AC_SUBST([AM_BACKSLASH])dnl _AM_SUBST_NOTMAKE([AM_BACKSLASH])dnl ]) -# Copyright (C) 2001-2020 Free Software Foundation, Inc. +# Copyright (C) 2001-2021 Free Software Foundation, Inc. # # This file is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, @@ -2660,7 +2662,7 @@ fi INSTALL_STRIP_PROGRAM="\$(install_sh) -c -s" AC_SUBST([INSTALL_STRIP_PROGRAM])]) -# Copyright (C) 2006-2020 Free Software Foundation, Inc. +# Copyright (C) 2006-2021 Free Software Foundation, Inc. # # This file is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, @@ -2679,7 +2681,7 @@ AC_DEFUN([AM_SUBST_NOTMAKE], [_AM_SUBST_NOTMAKE($@)]) # Check how to create a tarball. -*- Autoconf -*- -# Copyright (C) 2004-2020 Free Software Foundation, Inc. +# Copyright (C) 2004-2021 Free Software Foundation, Inc. # # This file is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, diff --git a/configure b/configure index bfa8dbabf..e235dfb04 100755 --- a/configure +++ b/configure @@ -640,6 +640,9 @@ AM_BACKSLASH AM_DEFAULT_VERBOSITY AM_DEFAULT_V AM_V +CSCOPE +ETAGS +CTAGS am__untar am__tar AMTAR @@ -1673,7 +1676,7 @@ else /* end confdefs.h. */ $4 int -main () +main (void) { #ifndef $as_decl_name #ifdef __cplusplus @@ -2370,12 +2373,7 @@ program_transform_name=`$as_echo "$program_transform_name" | sed "$ac_script"` am_aux_dir=`cd "$ac_aux_dir" && pwd` if test x"${MISSING+set}" != xset; then - case $am_aux_dir in - *\ * | *\ *) - MISSING="\${SHELL} \"$am_aux_dir/missing\"" ;; - *) - MISSING="\${SHELL} $am_aux_dir/missing" ;; - esac + MISSING="\${SHELL} '$am_aux_dir/missing'" fi # Use eval to expand $SHELL if eval "$MISSING --is-lightweight"; then @@ -2730,6 +2728,20 @@ am__tar='$${TAR-tar} chof - "$$tardir"' am__untar='$${TAR-tar} xf -' +# Variables for tags utilities; see am/tags.am +if test -z "$CTAGS"; then + CTAGS=ctags +fi + +if test -z "$ETAGS"; then + ETAGS=etags +fi + +if test -z "$CSCOPE"; then + CSCOPE=cscope +fi + + # POSIX will say in a future version that running "rm -f" with no argument # is OK; and we want to be able to make that assumption in our Makefile @@ -2828,7 +2840,7 @@ if test "$CFLAGS" == ""; then CFLAGS="-g -Wno-unused-local-typedefs" fi if test "$CXXFLAGS" == ""; then - CXXFLAGS="-g -Wno-unused-local-typedefs" + CXXFLAGS="-O2 -Wno-unused-local-typedefs" fi # enable full optimization by configure switch @@ -3182,7 +3194,7 @@ cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ int -main () +main (void) { ; @@ -3322,7 +3334,7 @@ cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include int -main () +main (void) { FILE *f = fopen ("conftest.out", "w"); return ferror (f) || fclose (f) != 0; @@ -3386,7 +3398,7 @@ else /* end confdefs.h. */ int -main () +main (void) { ; @@ -3437,7 +3449,7 @@ else /* end confdefs.h. */ int -main () +main (void) { #ifndef __GNUC__ choke me @@ -3478,7 +3490,7 @@ else /* end confdefs.h. */ int -main () +main (void) { ; @@ -3493,7 +3505,7 @@ else /* end confdefs.h. */ int -main () +main (void) { ; @@ -3509,7 +3521,7 @@ else /* end confdefs.h. */ int -main () +main (void) { ; @@ -3558,9 +3570,7 @@ struct stat; /* Most of the following tests are stolen from RCS 5.7's src/conf.sh. */ struct buf { int x; }; FILE * (*rcsopen) (struct buf *, struct stat *, int); -static char *e (p, i) - char **p; - int i; +static char *e (char **p, int i) { return p[i]; } @@ -3595,7 +3605,7 @@ int pairnames (int, char **, FILE *(*)(struct buf *, struct stat *, int), int, i int argc; char **argv; int -main () +main (void) { return f (e, argv, 0) != argv[0] || f (e, argv, 1) != argv[1]; ; @@ -3653,7 +3663,7 @@ else /* end confdefs.h. */ int -main () +main (void) { ; @@ -4034,7 +4044,7 @@ else /* end confdefs.h. */ int -main () +main (void) { #ifndef __GNUC__ choke me @@ -4075,7 +4085,7 @@ else /* end confdefs.h. */ int -main () +main (void) { ; @@ -4090,7 +4100,7 @@ else /* end confdefs.h. */ int -main () +main (void) { ; @@ -4106,7 +4116,7 @@ else /* end confdefs.h. */ int -main () +main (void) { ; @@ -4497,6 +4507,9 @@ $as_echo "no" >&6; } PKG_CONFIG="" fi fi +if test -z "$PKG_CONFIG"; then + as_fn_error $? "pkg-config not found" "$LINENO" 5 +fi # Check whether --with-sdl-prefix was given. @@ -4536,8 +4549,8 @@ fi if test "x$sdl_prefix$sdl_exec_prefix" = x ; then pkg_failed=no -{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for SDL" >&5 -$as_echo_n "checking for SDL... " >&6; } +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for sdl2 >= $min_sdl_version" >&5 +$as_echo_n "checking for sdl2 >= $min_sdl_version... " >&6; } if test -n "$SDL_CFLAGS"; then pkg_cv_SDL_CFLAGS="$SDL_CFLAGS" @@ -4577,7 +4590,7 @@ fi if test $pkg_failed = yes; then - { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } if $PKG_CONFIG --atleast-pkgconfig-version 0.20; then @@ -4586,24 +4599,24 @@ else _pkg_short_errors_supported=no fi if test $_pkg_short_errors_supported = yes; then - SDL_PKG_ERRORS=`$PKG_CONFIG --short-errors --print-errors --cflags --libs "sdl2 >= $min_sdl_version" 2>&1` + SDL_PKG_ERRORS=`$PKG_CONFIG --short-errors --print-errors --cflags --libs "sdl2 >= $min_sdl_version" 2>&1` else - SDL_PKG_ERRORS=`$PKG_CONFIG --print-errors --cflags --libs "sdl2 >= $min_sdl_version" 2>&1` + SDL_PKG_ERRORS=`$PKG_CONFIG --print-errors --cflags --libs "sdl2 >= $min_sdl_version" 2>&1` fi - # Put the nasty error message in config.log where it belongs - echo "$SDL_PKG_ERRORS" >&5 + # Put the nasty error message in config.log where it belongs + echo "$SDL_PKG_ERRORS" >&5 - sdl_pc=no + sdl_pc=no elif test $pkg_failed = untried; then - { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } - sdl_pc=no + sdl_pc=no else - SDL_CFLAGS=$pkg_cv_SDL_CFLAGS - SDL_LIBS=$pkg_cv_SDL_LIBS + SDL_CFLAGS=$pkg_cv_SDL_CFLAGS + SDL_LIBS=$pkg_cv_SDL_LIBS { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 $as_echo "yes" >&6; } - sdl_pc=yes + sdl_pc=yes fi else sdl_pc=no @@ -4623,7 +4636,7 @@ fi if test "x$sdl_pc" = xyes ; then no_sdl="" - SDL2_CONFIG="pkg-config sdl2" + SDL2_CONFIG="$PKG_CONFIG sdl2" else as_save_PATH="$PATH" if test "x$prefix" != xNONE && test "$cross_compiling" != yes; then @@ -4680,8 +4693,8 @@ $as_echo_n "checking for SDL2.framework... " >&6; } sdl_framework=$SDL2_FRAMEWORK else for d in / ~/ /System/; do - if test -d "$dLibrary/Frameworks/SDL2.framework"; then - sdl_framework="$dLibrary/Frameworks/SDL2.framework" + if test -d "${d}Library/Frameworks/SDL2.framework"; then + sdl_framework="${d}Library/Frameworks/SDL2.framework" fi done fi @@ -4727,38 +4740,16 @@ else #include #include -#include #include "SDL.h" -char* -my_strdup (char *str) -{ - char *new_str; - - if (str) - { - new_str = (char *)malloc ((strlen (str) + 1) * sizeof(char)); - strcpy (new_str, str); - } - else - new_str = NULL; - - return new_str; -} - int main (int argc, char *argv[]) { int major, minor, micro; - char *tmp_version; + FILE *fp = fopen("conf.sdltest", "w"); - /* This hangs on some systems (?) - system ("touch conf.sdltest"); - */ - { FILE *fp = fopen("conf.sdltest", "a"); if ( fp ) fclose(fp); } + if (fp) fclose(fp); - /* HP/UX 9 (%@#!) writes to sscanf strings */ - tmp_version = my_strdup("$min_sdl_version"); - if (sscanf(tmp_version, "%d.%d.%d", &major, &minor, µ) != 3) { + if (sscanf("$min_sdl_version", "%d.%d.%d", &major, &minor, µ) != 3) { printf("%s, bad version string\n", "$min_sdl_version"); exit(1); } @@ -4836,7 +4827,7 @@ int main(int argc, char *argv[]) #define main K_and_R_C_main int -main () +main (void) { return 0; ; @@ -5036,11 +5027,36 @@ $as_echo_n "checking for wxWidgets version >= $min_wx_version ()... " >&6; } if test -n "$wx_ver_ok"; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes (version $WX_VERSION)" >&5 $as_echo "yes (version $WX_VERSION)" >&6; } - WX_LIBS=`$WX_CONFIG_WITH_ARGS --libs adv,core,base` + + wx_optional_libs="" + + wx_ver_ok="" + if test "x$WX_VERSION" != x ; then + if test $wx_config_major_version -gt 2; then + wx_ver_ok=yes + else + if test $wx_config_major_version -eq 2; then + if test $wx_config_minor_version -gt 9; then + wx_ver_ok=yes + else + if test $wx_config_minor_version -eq 9; then + if test $wx_config_micro_version -ge 0; then + wx_ver_ok=yes + fi + fi + fi + fi + fi + fi + + if test -n "$wx_ver_ok" -a -n ""; then + wx_optional_libs="--optional-libs " + fi + WX_LIBS=`$WX_CONFIG_WITH_ARGS --libs adv,core,base $wx_optional_libs` { $as_echo "$as_me:${as_lineno-$LINENO}: checking for wxWidgets static library" >&5 $as_echo_n "checking for wxWidgets static library... " >&6; } - WX_LIBS_STATIC=`$WX_CONFIG_WITH_ARGS --static --libs adv,core,base 2>/dev/null` + WX_LIBS_STATIC=`$WX_CONFIG_WITH_ARGS --static --libs adv,core,base $wx_optional_libs 2>/dev/null` if test "x$WX_LIBS_STATIC" = "x"; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } diff --git a/configure.ac b/configure.ac index 360d8f243..f31ca354d 100644 --- a/configure.ac +++ b/configure.ac @@ -32,7 +32,7 @@ if test "$CFLAGS" == ""; then CFLAGS="-g -Wno-unused-local-typedefs" fi if test "$CXXFLAGS" == ""; then - CXXFLAGS="-g -Wno-unused-local-typedefs" + CXXFLAGS="-O2 -Wno-unused-local-typedefs" fi # enable full optimization by configure switch diff --git a/src/Makefile.am b/src/Makefile.am index d29ee395e..bc65be036 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -5,8 +5,12 @@ bin_PROGRAMS = sound-of-sorting COMMON = SortArray.cpp SortArray.h \ SortSound.cpp \ SortAlgo.cpp SortAlgo.h \ + algorithms/flansort.cpp \ + algorithms/grailsort.cpp \ + algorithms/pdqsort.cpp \ algorithms/timsort.cpp \ algorithms/wikisort.cpp + sound_of_sorting_SOURCES = \ WMain.cpp WMain.h \ @@ -24,16 +28,17 @@ sorting_test_SOURCES = \ SortTest.cpp \ $(COMMON) -AM_CXXFLAGS = -W -Wall @WX_CXXFLAGS@ @SDL_CFLAGS@ -LDADD = @WX_LIBS@ @SDL_LIBS@ +AM_CXXFLAGS = -W -Wall @WX_CXXFLAGS@ $(filter-out -Dmain=SDL_main, @SDL_CFLAGS@) +LDADD = if GOT_RESCOMP resources.o: resources.rc $(WX_RESCOMP) $(srcdir)/resources.rc resources.o - LDADD += resources.o endif +LDADD += @WX_LIBS@ @SDL_LIBS@ + EXTRA_DIST = \ resources.rc sos.ico sos.xpm \ wxg/sos.wxg diff --git a/src/Makefile.in b/src/Makefile.in index 818a299ef..7d665f5e3 100644 --- a/src/Makefile.in +++ b/src/Makefile.in @@ -1,7 +1,7 @@ -# Makefile.in generated by automake 1.16.2 from Makefile.am. +# Makefile.in generated by automake 1.16.5 from Makefile.am. # @configure_input@ -# Copyright (C) 1994-2020 Free Software Foundation, Inc. +# Copyright (C) 1994-2021 Free Software Foundation, Inc. # This Makefile.in is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, @@ -108,8 +108,9 @@ am__installdirs = "$(DESTDIR)$(bindir)" PROGRAMS = $(bin_PROGRAMS) $(noinst_PROGRAMS) am__dirstamp = $(am__leading_dot)dirstamp am__objects_1 = SortArray.$(OBJEXT) SortSound.$(OBJEXT) \ - SortAlgo.$(OBJEXT) algorithms/timsort.$(OBJEXT) \ - algorithms/wikisort.$(OBJEXT) + SortAlgo.$(OBJEXT) algorithms/flansort.$(OBJEXT) \ + algorithms/grailsort.$(OBJEXT) algorithms/pdqsort.$(OBJEXT) \ + algorithms/timsort.$(OBJEXT) algorithms/wikisort.$(OBJEXT) am_sorting_test_OBJECTS = SortTest.$(OBJEXT) $(am__objects_1) sorting_test_OBJECTS = $(am_sorting_test_OBJECTS) sorting_test_LDADD = $(LDADD) @@ -138,7 +139,10 @@ am__maybe_remake_depfiles = depfiles am__depfiles_remade = ./$(DEPDIR)/SortAlgo.Po ./$(DEPDIR)/SortArray.Po \ ./$(DEPDIR)/SortSound.Po ./$(DEPDIR)/SortTest.Po \ ./$(DEPDIR)/WMain.Po ./$(DEPDIR)/WSortView.Po \ - ./$(DEPDIR)/wxClickText.Po algorithms/$(DEPDIR)/timsort.Po \ + ./$(DEPDIR)/wxClickText.Po algorithms/$(DEPDIR)/flansort.Po \ + algorithms/$(DEPDIR)/grailsort.Po \ + algorithms/$(DEPDIR)/pdqsort.Po \ + algorithms/$(DEPDIR)/timsort.Po \ algorithms/$(DEPDIR)/wikisort.Po wxg/$(DEPDIR)/WAbout_wxg.Po \ wxg/$(DEPDIR)/WMain_wxg.Po am__mv = mv -f @@ -191,8 +195,6 @@ am__define_uniq_tagged_files = \ unique=`for i in $$list; do \ if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ done | $(am__uniquify_input)` -ETAGS = etags -CTAGS = ctags am__tty_colors_dummy = \ mgn= red= grn= lgn= blu= brg= std=; \ am__color_tests=no @@ -375,6 +377,7 @@ am__set_TESTS_bases = \ bases='$(TEST_LOGS)'; \ bases=`for i in $$bases; do echo $$i; done | sed 's/\.log$$//'`; \ bases=`echo $$bases` +AM_TESTSUITE_SUMMARY_HEADER = ' for $(PACKAGE_STRING)' RECHECK_LOGS = $(TEST_LOGS) AM_RECURSIVE_TARGETS = check recheck TEST_SUITE_LOG = test-suite.log @@ -412,6 +415,8 @@ CC = @CC@ CCDEPMODE = @CCDEPMODE@ CFLAGS = @CFLAGS@ CPPFLAGS = @CPPFLAGS@ +CSCOPE = @CSCOPE@ +CTAGS = @CTAGS@ CXX = @CXX@ CXXDEPMODE = @CXXDEPMODE@ CXXFLAGS = @CXXFLAGS@ @@ -421,6 +426,7 @@ DEPDIR = @DEPDIR@ ECHO_C = @ECHO_C@ ECHO_N = @ECHO_N@ ECHO_T = @ECHO_T@ +ETAGS = @ETAGS@ EXEEXT = @EXEEXT@ INSTALL = @INSTALL@ INSTALL_DATA = @INSTALL_DATA@ @@ -526,6 +532,9 @@ top_srcdir = @top_srcdir@ COMMON = SortArray.cpp SortArray.h \ SortSound.cpp \ SortAlgo.cpp SortAlgo.h \ + algorithms/flansort.cpp \ + algorithms/grailsort.cpp \ + algorithms/pdqsort.cpp \ algorithms/timsort.cpp \ algorithms/wikisort.cpp @@ -541,8 +550,8 @@ sorting_test_SOURCES = \ SortTest.cpp \ $(COMMON) -AM_CXXFLAGS = -W -Wall @WX_CXXFLAGS@ @SDL_CFLAGS@ -LDADD = @WX_LIBS@ @SDL_LIBS@ $(am__append_1) +AM_CXXFLAGS = -W -Wall $(filter-out -g, @WX_CXXFLAGS@) $(filter-out -Dmain=SDL_main, @SDL_CFLAGS@) +LDADD = $(am__append_1) @WX_LIBS@ @SDL_LIBS@ EXTRA_DIST = \ resources.rc sos.ico sos.xpm \ wxg/sos.wxg @@ -631,6 +640,12 @@ algorithms/$(am__dirstamp): algorithms/$(DEPDIR)/$(am__dirstamp): @$(MKDIR_P) algorithms/$(DEPDIR) @: > algorithms/$(DEPDIR)/$(am__dirstamp) +algorithms/flansort.$(OBJEXT): algorithms/$(am__dirstamp) \ + algorithms/$(DEPDIR)/$(am__dirstamp) +algorithms/grailsort.$(OBJEXT): algorithms/$(am__dirstamp) \ + algorithms/$(DEPDIR)/$(am__dirstamp) +algorithms/pdqsort.$(OBJEXT): algorithms/$(am__dirstamp) \ + algorithms/$(DEPDIR)/$(am__dirstamp) algorithms/timsort.$(OBJEXT): algorithms/$(am__dirstamp) \ algorithms/$(DEPDIR)/$(am__dirstamp) algorithms/wikisort.$(OBJEXT): algorithms/$(am__dirstamp) \ @@ -669,6 +684,9 @@ distclean-compile: @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/WMain.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/WSortView.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/wxClickText.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@algorithms/$(DEPDIR)/flansort.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@algorithms/$(DEPDIR)/grailsort.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@algorithms/$(DEPDIR)/pdqsort.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@algorithms/$(DEPDIR)/timsort.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@algorithms/$(DEPDIR)/wikisort.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@wxg/$(DEPDIR)/WAbout_wxg.Po@am__quote@ # am--include-marker @@ -855,7 +873,7 @@ $(TEST_SUITE_LOG): $(TEST_LOGS) test x"$$VERBOSE" = x || cat $(TEST_SUITE_LOG); \ fi; \ echo "$${col}$$br$${std}"; \ - echo "$${col}Testsuite summary for $(PACKAGE_STRING)$${std}"; \ + echo "$${col}Testsuite summary"$(AM_TESTSUITE_SUMMARY_HEADER)"$${std}"; \ echo "$${col}$$br$${std}"; \ create_testsuite_report --maybe-color; \ echo "$$col$$br$$std"; \ @@ -910,7 +928,6 @@ sorting-test.log: sorting-test$(EXEEXT) @am__EXEEXT_TRUE@ --log-file $$b.log --trs-file $$b.trs \ @am__EXEEXT_TRUE@ $(am__common_driver_flags) $(AM_TEST_LOG_DRIVER_FLAGS) $(TEST_LOG_DRIVER_FLAGS) -- $(TEST_LOG_COMPILE) \ @am__EXEEXT_TRUE@ "$$tst" $(AM_TESTS_FD_REDIRECT) - distdir: $(BUILT_SOURCES) $(MAKE) $(AM_MAKEFLAGS) distdir-am @@ -1002,6 +1019,9 @@ distclean: distclean-am -rm -f ./$(DEPDIR)/WMain.Po -rm -f ./$(DEPDIR)/WSortView.Po -rm -f ./$(DEPDIR)/wxClickText.Po + -rm -f algorithms/$(DEPDIR)/flansort.Po + -rm -f algorithms/$(DEPDIR)/grailsort.Po + -rm -f algorithms/$(DEPDIR)/pdqsort.Po -rm -f algorithms/$(DEPDIR)/timsort.Po -rm -f algorithms/$(DEPDIR)/wikisort.Po -rm -f wxg/$(DEPDIR)/WAbout_wxg.Po @@ -1058,6 +1078,9 @@ maintainer-clean: maintainer-clean-am -rm -f ./$(DEPDIR)/WMain.Po -rm -f ./$(DEPDIR)/WSortView.Po -rm -f ./$(DEPDIR)/wxClickText.Po + -rm -f algorithms/$(DEPDIR)/flansort.Po + -rm -f algorithms/$(DEPDIR)/grailsort.Po + -rm -f algorithms/$(DEPDIR)/pdqsort.Po -rm -f algorithms/$(DEPDIR)/timsort.Po -rm -f algorithms/$(DEPDIR)/wikisort.Po -rm -f wxg/$(DEPDIR)/WAbout_wxg.Po diff --git a/src/SortAlgo.cpp b/src/SortAlgo.cpp index 866702acc..ed5f6588b 100644 --- a/src/SortAlgo.cpp +++ b/src/SortAlgo.cpp @@ -37,6 +37,10 @@ #include #include #include +#include +#include +#include +#include typedef ArrayItem value_type; @@ -47,16 +51,37 @@ const struct AlgoEntry g_algolist[] = { { _("Selection Sort"), &SelectionSort, UINT_MAX, UINT_MAX, wxEmptyString }, + { _("Double Selection Sort"), &DoubleSelectionSort, UINT_MAX, UINT_MAX, + wxEmptyString }, + { _("Sandpaper Sort"), &SandpaperSort, UINT_MAX, UINT_MAX, + _("Also known as Exchange Sort.") }, + { _("Double Sandpaper Sort"), &DoubleSandpaperSort, UINT_MAX, UINT_MAX, + _("A variant of Exchange Sort that sorts the array bidirectionally.") }, { _("Insertion Sort"), &InsertionSort, UINT_MAX, UINT_MAX, wxEmptyString }, { _("Binary Insertion Sort"), &BinaryInsertionSort, UINT_MAX, UINT_MAX, wxEmptyString }, { _("Merge Sort"), &MergeSort, UINT_MAX, 512, - _("Merge sort which merges two sorted sequences into a shadow array," - "and then copies it back to the shown array.") }, + _("Merge sort which merges two sorted sequences into a shadow array, and then copies it back to the shown array.") }, { _("Merge Sort (iterative)"), &MergeSortIterative, UINT_MAX, 512, _("Merge sort variant which iteratively merges " "subarrays of sizes of powers of two.") }, + { _("Pairwise Merge Sort (Recursive)"), &PairwiseSort, UINT_MAX, 512, + wxEmptyString }, + { _("Pairwise Merge Sort (Iterative)"), &PairwiseIterativeSort, UINT_MAX, 512, + wxEmptyString }, + { _("Weave Merge Sort"), &WeaveMergeSort, UINT_MAX, 512, + _("An in-place merge sort variant that interleaves 2 halves of the input array, and then uses Insertion Sort to sort the array.")}, + { _("New Shuffle Merge Sort"), &NewShuffleMergeSort, UINT_MAX, 512, + _("An improvement upon Weave Merge Sort, with faster weave time, and inserting now makes comparisons with a worst-case similar to Merge Sort.") }, + { _("Andrey's In-Place Merge Sort"), &AndreyMergeSort, UINT_MAX, 512, + wxEmptyString }, + { _("Proportion Extend Merge Sort"), &ProportionMergeSort, UINT_MAX, 512, + wxEmptyString }, + { _("Buffer Partition Merge Sort"), &BufferPartitionMergeSort, UINT_MAX, 512, + wxEmptyString }, + { _("Strand Sort"), &StrandSort, UINT_MAX, 512, + wxEmptyString }, { _("Quick Sort (LR ptrs)"), &QuickSortLR, UINT_MAX, UINT_MAX, _("Quick sort variant with left and right pointers.") }, { _("Quick Sort (LL ptrs)"), &QuickSortLL, UINT_MAX, UINT_MAX, @@ -71,17 +96,40 @@ const struct AlgoEntry g_algolist[] = { _("Quick Sort (dual pivot)"), &QuickSortDualPivot, UINT_MAX, UINT_MAX, _("Dual pivot quick sort variant: partitions \"<1<2?>\" using three pointers, " "two at left and one at right.") }, + { _("PDQ Sort"), &PDQSort, UINT_MAX, inversion_count_instrumented, + _("Pattern-defeating Quick Sort (pdqsort) is a novel sorting algorithm that combines the fast average case of randomized quicksort" + " with the fast worst case of heapsort, while achieving linear time on inputs with certain patterns.") }, + { _("Branchless PDQ Sort"), &PDQSortBranchless, UINT_MAX, inversion_count_instrumented, + _("Provides potential speedup over default Pattern-Defeating Quick Sort for arithmetic data.") }, + { _("Flan Sort (Quick Library Sort)"), &QuickLibrarySort, UINT_MAX, inversion_count_instrumented, + _("An in-place Library Sort variant that uses Quick Sort partitioning to make space.") }, { _("Bubble Sort"), &BubbleSort, UINT_MAX, UINT_MAX, wxEmptyString }, + { _("Optimized Bubble Sort"), &OptimizedBubbleSort, UINT_MAX, UINT_MAX, + _("This variant terminates early if the array is already sorted.") }, + { _("Targeted Bubble Sort"), &TargetedBubbleSort, UINT_MAX, 1024, + _("This variant of Bubble Sort is capable of adjusting the sorting boundaries based on the input array.") }, { _("Cocktail Shaker Sort"), &CocktailShakerSort, UINT_MAX, UINT_MAX, wxEmptyString }, + { _("Dual Cocktail Shaker Sort"), &DualCocktailShakerSort, UINT_MAX, UINT_MAX, + _("This variant sorts from both directions of the array simultaneously.") }, + { _("Circle Sort"), &CircleSort, UINT_MAX, UINT_MAX, + _("Circle Sort is a recursive sorting algorithm that works by comparing and swapping elements in a circular manner.") }, + { _("Iterative Circle Sort"), &CircleSort2, UINT_MAX, UINT_MAX, + _("A variant of Circle Sort that has less recursion overhead.") }, + { _("Introspective Circle Sort"), &IntroCircleSort, UINT_MAX, UINT_MAX, + _("A variant of Circle Sort that switches to Insertion Sort when a certain threshold has been reached.") }, + { _("Introspective Iterative Circle Sort"), &IntroIteCircleSort, UINT_MAX, UINT_MAX, + _("A variant of Iterative Circle Sort that switches to Insertion Sort when a certain threshold has been reached.") }, { _("Gnome Sort"), &GnomeSort, UINT_MAX, UINT_MAX, wxEmptyString }, + { _("Optimized Gnome Sort"), &OptimizedGnomeSort, UINT_MAX, UINT_MAX, + _("This variant avoids scanning through sorted portions of the array after a number has been placed in its correct spot") }, { _("Comb Sort"), &CombSort, UINT_MAX, UINT_MAX, wxEmptyString }, { _("Shell Sort"), &ShellSort, UINT_MAX, 1024, wxEmptyString }, - { _("Heap Sort"), &HeapSort, UINT_MAX, UINT_MAX, + { _("Heap Sort"), &HeapSort, UINT_MAX, inversion_count_instrumented, wxEmptyString }, { _("Smooth Sort"), &SmoothSort, UINT_MAX, 1024, wxEmptyString }, @@ -98,8 +146,16 @@ const struct AlgoEntry g_algolist[] = { _("Radix Sort (LSD)"), &RadixSortLSD, UINT_MAX, 512, _("Least significant digit radix sort, which copies item into a shadow " "array during counting.") }, - { _("Radix Sort (MSD)"), &RadixSortMSD, UINT_MAX, UINT_MAX, + { _("In-Place Radix Sort (LSD)"), &InPlaceRadixSortLSD, UINT_MAX, UINT_MAX, + _("Least significant digit radix sort, performed in O(1) space.") }, + { _("Radix Sort (MSD)"), &RadixSortMSD, UINT_MAX, 512, _("Most significant digit radix sort, which permutes items in-place by walking cycles.") }, + { _("Rotate Radix Sort (MSD)"), &RotateRadixSortMSD, UINT_MAX, 512, + wxEmptyString }, + { _("Rotate Radix Sort (LSD)"), &RotateRadixSortLSD, UINT_MAX, 512, + wxEmptyString }, + { _("American Flag Sort"), &AmericanFlagSort, UINT_MAX, inversion_count_instrumented, + _("American Flag Sort is an efficient, in-place variant of radix sort that distributes items into hundreds of buckets.") }, { _("std::sort (gcc)"), &StlSort, UINT_MAX, inversion_count_instrumented, wxEmptyString }, { _("std::stable_sort (gcc)"), &StlStableSort, UINT_MAX, inversion_count_instrumented, @@ -110,6 +166,20 @@ const struct AlgoEntry g_algolist[] = wxEmptyString }, { _("Block Merge Sort (WikiSort)"), &WikiSort, UINT_MAX, inversion_count_instrumented, _("An O(1) place O(n log n) time stable merge sort.") }, + { _("Grail Sort (O(1) buffer)"), &GrailSort, UINT_MAX, inversion_count_instrumented, + _("Grail Sort is a stable, in-place sorting algorithm that efficiently organizes an array by using a block-based merging technique.") }, + { _("Grail Sort (external buffer)"), &AuxGrailSort, UINT_MAX, inversion_count_instrumented, + _("A variant of Grail Sort that uses an external buffer for a potential speedup.") }, + { _("Bead Sort"), &BeadSort, UINT_MAX, UINT_MAX, + _("This is a non-comparison based sorting algorithm that uses the concept of stacked beads to sort the elements.") }, + { _("Gravity Sort"), &GravitySort, UINT_MAX, UINT_MAX, + _("A non-comparison based sorting algorithm that uses the concept of gravitational fall to sort the elements.") }, + { _("Pancake Sort"), &PancakeSort, UINT_MAX, UINT_MAX, + _("Sorts the array by performing a series of 'flips' to push the maximum element to the correct spot.") }, + { _("Optimized Pancake Sort"), &OptimizedPancakeSort, UINT_MAX, UINT_MAX, + _("An optimized variant of Pancake Sort that performs 1/2 as many flips.") }, + { _("Adjacency Pancake Sort"), &AdjacencyPancakeSort, UINT_MAX, UINT_MAX, + _("An improvement upon Pancake Sort, which performs only 5/3 N + O(1) flips.") }, { _("Bogo Sort"), &BogoSort, 10, UINT_MAX, wxEmptyString }, { _("Bozo Sort"), &BozoSort, 10, UINT_MAX, @@ -117,34 +187,76 @@ const struct AlgoEntry g_algolist[] = { _("Stooge Sort"), &StoogeSort, 256, inversion_count_instrumented, wxEmptyString }, { _("Slow Sort"), &SlowSort, 128, inversion_count_instrumented, - wxEmptyString } + wxEmptyString }, + { _("Bad Sort"), &BadSort, 128, inversion_count_instrumented, + _("A humorous sorting algorithm with a time complexity of O(n^3).") } }; const size_t g_algolist_size = sizeof(g_algolist) / sizeof(g_algolist[0]); const struct AlgoEntry* g_algolist_end = g_algolist + g_algolist_size; +std::random_device rd1; +std::mt19937 gen1(rd1()); + // **************************************************************************** // *** Selection Sort +void DoubleSelectionSort(SortArray& A) +{ + size_t left = 0; + size_t right = A.size() - 1, n = right; + std::atomic max_i{ 0 }, low_i{ 0 }; + A.watch(&max_i, 4); + A.watch(&low_i, 5); + while (left < right) + { + max_i.store(right); + low_i.store(left); + for (size_t i = left; i <= right; ++i) + { + if (A[i] < A[low_i.load()]) + { + A.mark_swap(i, low_i.load()); + low_i.store(i); + } + else if (A[i] > A[max_i.load()]) + { + A.mark_swap(i, max_i.load()); + max_i.store(i); + } + } + A.swap(left, low_i.load()); + ssize_t l = left; // This removes comparison warning + if (max_i.load() == l) { max_i.store(low_i.load()); } + A.swap(right, max_i.load()); + if (left > 0) { A.unmark(left - 1); } + if (right < n) { A.unmark(right + 1); } + A.mark(left); + A.mark(right); + ++left; --right; + } + A.unwatch_all(); +} + void SelectionSort(SortArray& A) { - volatile ssize_t jMin = 0; - A.watch(&jMin, 3); + std::atomic kMin{ 0 }; + A.watch(&kMin, 3); for (size_t i = 0; i < A.size()-1; ++i) { - jMin = i; + kMin.store(i); for (size_t j = i+1; j < A.size(); ++j) { - if (A[j] < A[jMin]) { - A.mark_swap(j, jMin); - jMin = j; + if (A[j] < A[kMin.load()]) { + A.mark_swap(j, kMin.load()); + kMin.store(j); } } - A.swap(i, jMin); + A.swap(i, kMin.load()); // mark the last good element if (i > 0) A.unmark(i-1); @@ -153,50 +265,97 @@ void SelectionSort(SortArray& A) A.unwatch_all(); } +// **************************************************************************** +// *** Sandpaper and Double Sandpaper Sort +// Double Sandpaper Sort by Taihennami + +void DoubleSandpaperSort(SortArray& A) +{ + for (size_t left = 0, right = A.size() - 1; left < right; ++left, --right) + { + if (A[left] > A[right]) + { + A.swap(left, right); + } + for (size_t i = left + 1; i < right; ++i) + { + if (A[left] > A[i]) + { + A.swap(i, left); + } + else if (A[i] > A[right]) + { + A.swap(i, right); + } + } + } +} + +void SandpaperSort(SortArray& A) +{ + size_t n = A.size(); + for (size_t i = 0; i < n; ++i) + { + for (size_t j = i + 1; j < n; ++j) + { + if (A[i] > A[j]) + { + A.swap(i, j); + } + } + } +} + // **************************************************************************** // *** Insertion Sort -// swaps every time (keeps all values visible) -void InsertionSort(SortArray& A) +void InsertSort(SortArray& A, size_t start, size_t end) { - for (size_t i = 1; i < A.size(); ++i) + int begin = static_cast(start); + for (size_t i = start; i < end; ++i) { - value_type key = A[i]; A.mark(i); - - ssize_t j = i - 1; - while (j >= 0 && A[j] > key) + int j = i - 1; + value_type key = A[i]; + while (j >= begin && A[j] > key) { - A.swap(j, j+1); - j--; + A.set(j + 1, A[j]); + --j; } + int index = j + 1; + A.set(index, key); A.unmark(i); } } // with extra item on stack +void InsertionSort(SortArray& A) +{ + InsertSort(A, 0, A.size()); +} + +// swaps every time (keeps all values visible) void InsertionSort2(SortArray& A) { for (size_t i = 1; i < A.size(); ++i) { - value_type tmp, key = A[i]; + value_type key = A[i]; A.mark(i); ssize_t j = i - 1; - while (j >= 0 && (tmp = A[j]) > key) + while (j >= 0 && A[j] > key) { - A.set(j + 1, tmp); + A.swap(j, j + 1); j--; } - A.set(j + 1, key); A.unmark(i); } } // swaps every time (keeps all values visible) -void BinaryInsertionSort(SortArray& A) +void BinaryInsertionSort2(SortArray& A) { for (size_t i = 1; i < A.size(); ++i) { @@ -206,7 +365,7 @@ void BinaryInsertionSort(SortArray& A) int lo = 0, hi = i; while (lo < hi) { int mid = (lo + hi) / 2; - if (key <= A[mid]) + if (key < A[mid]) hi = mid; else lo = mid + 1; @@ -217,7 +376,7 @@ void BinaryInsertionSort(SortArray& A) ssize_t j = i - 1; while (j >= lo) { - A.swap(j, j+1); + A.swap(j, j + 1); j--; } @@ -225,6 +384,38 @@ void BinaryInsertionSort(SortArray& A) } } +void BinaryInsertSort(SortArray& A, size_t start, size_t end) +{ + for (size_t i = start; i < end; ++i) + { + value_type key = A[i]; + A.mark(i); + + size_t lo = start, hi = i; + while (lo < hi) { + size_t mid = (lo + hi) / 2; + if (key < A[mid]) + hi = mid; + else + lo = mid + 1; + } + + size_t j = i; + while (j > lo) + { + A.set(j, A[j - 1]); + j--; + } + A.set(lo, key); + A.unmark(i); + } +} + +void BinaryInsertionSort(SortArray& A) +{ + BinaryInsertSort(A, 0, A.size()); +} + // **************************************************************************** // *** Merge Sort (out-of-place with sentinels) @@ -296,230 +487,692 @@ void MergeSortIterative(SortArray& A) } } -// **************************************************************************** -// *** Quick Sort Pivot Selection +/* + MIT License -QuickSortPivotType g_quicksort_pivot = PIVOT_FIRST; + Copyright (c) 2020 aphitorite/2021 EmeraldBlock -// some quicksort variants use hi inclusive and some exclusive, we require it -// to be _exclusive_. hi == array.end()! -ssize_t QuickSortSelectPivot(SortArray& A, ssize_t lo, ssize_t hi) -{ - if (g_quicksort_pivot == PIVOT_FIRST) - return lo; + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), to deal + in the Software without restriction, including without limitation the rights + to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in all + copies or substantial portions of the Software. - if (g_quicksort_pivot == PIVOT_LAST) - return hi-1; + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + SOFTWARE. +*/ - if (g_quicksort_pivot == PIVOT_MID) - return (lo + hi) / 2; +void weaveMerge2(SortArray& A, std::vector& tmp, size_t len, size_t residue, size_t modulus) +{ + if (residue + modulus >= len) { return; } - if (g_quicksort_pivot == PIVOT_RANDOM) - return lo + (rand() % (hi - lo)); + size_t low = residue, high = residue + modulus, dmodulus = modulus << 1; - if (g_quicksort_pivot == PIVOT_MEDIAN3) - { - ssize_t mid = (lo + hi) / 2; + weaveMerge2(A, tmp, len, low, dmodulus); + weaveMerge2(A, tmp, len, high, dmodulus); - // cases if two are equal - if (A[lo] == A[mid]) return lo; - if (A[lo] == A[hi-1] || A[mid] == A[hi-1]) return hi-1; + size_t next = residue; + for (; low < len && high < len; next += modulus) + { + int cmp = 0; + if (A[low] > A[high]) { cmp = 1; } + else if (A[low] < A[high]) { cmp = -1; } + + if (cmp == 1 || (cmp == 0 && low > high)) + { + tmp[next] = A[high]; + high += dmodulus; + } + else + { + tmp[next] = A[low]; + low += dmodulus; + } + } - // cases if three are different - return A[lo] < A[mid] - ? (A[mid] < A[hi-1] ? mid : (A[lo] < A[hi-1] ? hi-1 : lo)) - : (A[mid] > A[hi-1] ? mid : (A[lo] < A[hi-1] ? lo : hi-1)); + if (low >= len) + { + while (high < len) + { + tmp[next] = A[high]; + next += modulus; + high += dmodulus; + + } + } + else + { + while (low < len) + { + tmp[next] = A[low]; + next += modulus; + low += dmodulus; + } } - return lo; + for (size_t i = residue; i < len; i += modulus) + { + A.set(i, tmp[i]); + A[i].get(); + } } -wxArrayString QuickSortPivotText() +void WeaveMergeSort2(SortArray& A) { - wxArrayString sl; - - sl.Add( _("First Item") ); - sl.Add( _("Last Item") ); - sl.Add( _("Middle Item") ); - sl.Add( _("Random Item") ); - sl.Add( _("Median of Three") ); - - return sl; + size_t len = A.size(); + std::vector tmp(len); + weaveMerge2(A, tmp, len, 0, 1); } -// **************************************************************************** -// *** Quick Sort LR (in-place, pointers at left and right, pivot is middle element) - -// by myself (Timo Bingmann), based on Hoare's original code - -void QuickSortLR(SortArray& A, ssize_t lo, ssize_t hi) +void insertTo(SortArray& A, size_t a, size_t b) { - // pick pivot and watch - volatile ssize_t p = QuickSortSelectPivot(A, lo, hi+1); - - value_type pivot = A[p]; - A.watch(&p, 2); - - volatile ssize_t i = lo, j = hi; - A.watch(&i, 3); - A.watch(&j, 3); + value_type temp = A[a]; + while (a > b) { A.set(a, A[a - 1]); --a; } + A.set(b, temp); +} - while (i <= j) +void shiftValue(SortArray& A, size_t a, size_t b, size_t len) +{ + for (size_t i = 0; i < len; ++i) { - while (A[i] < pivot) - i++; - - while (A[j] > pivot) - j--; + A[a + i].get(); + A.swap(a + i, b + i); + } +} - if (i <= j) +void rotate(SortArray& A, int a, int m, int b) +{ + int l = m - a, r = b - m; + while (l > 0 && r > 0) + { + if (r < l) { - A.swap(i,j); - - // follow pivot if it is swapped - if (p == i) p = j; - else if (p == j) p = i; + shiftValue(A, static_cast(m - r), static_cast(m), static_cast(r)); + b -= r; + m -= r; + l -= r; + } - i++, j--; + else + { + shiftValue(A, static_cast(a), static_cast(m), static_cast(l)); + a += l; + m += l; + r -= l; } } - - A.unwatch_all(); - - if (lo < j) - QuickSortLR(A, lo, j); - - if (i < hi) - QuickSortLR(A, i, hi); } -void QuickSortLR(SortArray& A) +void bitReversal(SortArray& A, size_t a, size_t b) { - return QuickSortLR(A, 0, A.size()-1); + size_t len = b - a, m = 0; + size_t d1 = len >> 1, d2 = d1 + (d1 >> 1); + for (size_t i = 1; i < len - 1; ++i) + { + size_t j = d1; + for (size_t k = i, n = d2; (k & 1) == 0; j -= n, k >>= 1, n >>= 1) {} + m += j; + if (m > i) + { + A.swap(a + i, a + m); + } + } } -// **************************************************************************** -// *** Quick Sort LL (in-place, two pointers at left, pivot is first element and moved to right) - -// by myself (Timo Bingmann), based on CLRS' 3rd edition - -size_t PartitionLL(SortArray& A, size_t lo, size_t hi) +void weaveInsert(SortArray& A, size_t a, size_t b, bool right) { - // pick pivot and move to back - size_t p = QuickSortSelectPivot(A, lo, hi); - - value_type pivot = A[p]; - A.swap(p, hi-1); - A.mark(hi-1); - - volatile ssize_t i = lo; - A.watch(&i, 3); - - for (size_t j = lo; j < hi-1; ++j) + size_t i = a, j = i + 1; + while (j < b) { - if (A[j] <= pivot) { - A.swap(i, j); - ++i; + while (i < j && ((right == true && A[i] <= A[j]) || (right == false && A[i] < A[j]))) { ++i; } + if (i == j) + { + right = !right; + ++j; + } + else + { + insertTo(A, j, i++); + j += 2; } } - - A.swap(i, hi-1); - A.unmark(hi-1); - A.unwatch_all(); - - return i; } -void QuickSortLL(SortArray& A, size_t lo, size_t hi) +void weaveMerge(SortArray& A, size_t a, size_t m, size_t b) { - if (lo + 1 < hi) + if (b - a < 2) { return; } + size_t a1 = a, b1 = b; + bool right = true; + if ((b - a) % 2 == 1) { - size_t mid = PartitionLL(A, lo, hi); + if (m - a < b - m) + { + --a1; + right = false; + } + else { ++b1; } + } - QuickSortLL(A, lo, mid); - QuickSortLL(A, mid+1, hi); + for (size_t e = b1, f; e - a1 > 2; e = f) + { + m = (a1 + e) / 2; + size_t p = 1 << static_cast(log(m - a1) / log(2)); + rotate(A, static_cast(m - p), static_cast(m), static_cast(e - p)); + + m = e - p; + f = m - p; + + bitReversal(A, f, m); + bitReversal(A, m, e); + bitReversal(A, f, e); } + weaveInsert(A, a, b, right); } -void QuickSortLL(SortArray& A) +void WeaveMergeSort(SortArray& A) { - return QuickSortLL(A, 0, A.size()); + size_t n = A.size(), d = 1 << static_cast(log(n - 1) / log(2) + 1); + while (d > 1) + { + size_t i = 0, dec = 0; + while (i < n) + { + size_t j = i; + dec += n; + while (dec >= d) + { + dec -= d; + ++j; + } + size_t k = j; + dec += n; + while (dec >= d) + { + dec -= d; + ++k; + } + weaveMerge(A, i, j, k); + i = k; + } + d /= 2; + } } - // **************************************************************************** -// *** Quick Sort Ternary (in-place, two pointers at left, pivot is first element and moved to right) +// *** Quick Sort Pivot Selection -// by myself (Timo Bingmann), loosely based on multikey quicksort by B&S +QuickSortPivotType g_quicksort_pivot = PIVOT_FIRST; -void QuickSortTernaryLR(SortArray& A, ssize_t lo, ssize_t hi) +static ssize_t SingleMedianOfThree(SortArray& A, ssize_t lo, ssize_t mid, ssize_t hi) { - if (hi <= lo) return; - - int cmp; - - // pick pivot and swap to back - ssize_t piv = QuickSortSelectPivot(A, lo, hi+1); - A.swap(piv, hi); - A.mark(hi); - - const value_type& pivot = A[hi]; - - // schema: |p === |i <<< | ??? |j >>> |q === |piv - volatile ssize_t i = lo, j = hi-1; - volatile ssize_t p = lo, q = hi-1; - - A.watch(&i, 3); - A.watch(&j, 3); + // cases if two are equal + if (A[lo] == A[mid]) return lo; + if (A[lo] == A[hi - 1] || A[mid] == A[hi - 1]) return hi - 1; + + // cases if three are different + return A[lo] < A[mid] + ? (A[mid] < A[hi - 1] ? mid : (A[lo] < A[hi - 1] ? hi - 1 : lo)) + : (A[mid] > A[hi - 1] ? mid : (A[lo] < A[hi - 1] ? lo : hi - 1)); +} - for (;;) +template +static void PivotInsertionSort(SortArray& A, std::array& arr) +{ + for (size_t i = 1; i < N; ++i) { - // partition on left - while (i <= j && (cmp = A[i].cmp(pivot)) <= 0) + ssize_t key = arr[i]; + size_t j = i; + while (j > 0 && A[arr[j - 1]] > A[key]) { - if (cmp == 0) { - A.mark(p,4); - A.swap(i, p++); - } - ++i; + arr[j] = arr[j - 1]; + --j; } + arr[j] = key; + } +} - // partition on right - while (i <= j && (cmp = A[j].cmp(pivot)) >= 0) +static std::array gaps = { 8, 3, 1 }; +template +static void PivotShellSort(SortArray& A, std::array& arr) +{ + for (size_t k = 0; k < 3; ++k) + { + for (size_t h = gaps[k], i = h; i < N; ++i) { - if (cmp == 0) { - A.mark(q,4); - A.swap(j, q--); + ssize_t key = arr[i]; + size_t j = i; + while (j >= h && A[arr[j - h]] > A[key]) + { + arr[j] = arr[j - h]; + j -= h; } - --j; + arr[j] = key; } - - if (i > j) break; - - // swap item between < > regions - A.swap(i++, j--); } +} - // swap pivot to right place - A.swap(i,hi); - A.mark_swap(i,hi); - - ssize_t num_less = i - p; - ssize_t num_greater = q - j; +static ssize_t MedianOfFive(SortArray& A, ssize_t lo, ssize_t hi) +{ + if (hi - lo < 25) { return SingleMedianOfThree(A, lo, (lo + hi) / 2, hi); } + ssize_t segment = (hi - lo) / 5; + std::array nums = { lo, lo + segment, lo + 2 * segment, lo + 3 * segment, lo + 4 * segment }; + PivotInsertionSort(A, nums); + return nums[2]; +} - // swap equal ranges into center, but avoid swapping equal elements - j = i-1; i = i+1; +static ssize_t MedianOfSeven(SortArray& A, ssize_t lo, ssize_t hi) +{ + if (hi - lo < 49) { return SingleMedianOfThree(A, lo, (lo + hi) / 2, hi); } + ssize_t segment = (hi - lo) / 7; + std::array samples = { lo, lo + segment, lo + segment * 2, lo + segment * 3, lo + segment * 4, lo + segment * 5, lo + segment * 6 }; + PivotInsertionSort(A, samples); + return samples[3]; +} - ssize_t pe = lo + std::min(p-lo, num_less); - for (ssize_t k = lo; k < pe; k++, j--) { - A.swap(k,j); - A.mark_swap(k,j); - } +static ssize_t NintherPivot(SortArray& A, ssize_t lo, ssize_t hi) +{ + if (hi - lo < 81) { return SingleMedianOfThree(A, lo, (lo + hi) / 2, hi); } + ssize_t segment_size = (hi - lo) / 9; + ssize_t g1 = SingleMedianOfThree(A, lo, lo + segment_size, (lo + segment_size * 2) + 1); + ssize_t g2 = SingleMedianOfThree(A, lo + 3 * segment_size, lo + 4 * segment_size, (lo + 5 * segment_size) + 1); + ssize_t g3 = SingleMedianOfThree(A, lo + 6 * segment_size, lo + 7 * segment_size, (lo + 8 * segment_size) + 1); + return SingleMedianOfThree(A, g1, g2, g3 + 1); +} - ssize_t qe = hi-1 - std::min(hi-1-q, num_greater-1); // one already greater at end - for (ssize_t k = hi-1; k > qe; k--, i++) { - A.swap(i,k); - A.mark_swap(i,k); - } +// some quicksort variants use hi inclusive and some exclusive, we require it +// to be _exclusive_. hi == array.end()! +ssize_t QuickSortSelectPivot(SortArray& A, ssize_t lo, ssize_t hi) +{ + switch (g_quicksort_pivot) + { + case PIVOT_FIRST: + { + return lo; + } + case PIVOT_LAST: + { + return hi - 1; + } + case PIVOT_MID: + { + return (lo + hi) / 2; + } + case PIVOT_RANDOM: + { + return lo + (rand() % (hi - lo)); + } + case PIVOT_MEDIAN3: + { + ssize_t mid = (lo + hi) / 2; + return SingleMedianOfThree(A, lo, mid, hi); + } + case PIVOT_MEDIAN3RANDOM: + { + if (lo > hi) + { + ssize_t temp = lo; + lo = hi; + hi = temp; + } + std::uniform_int_distribution dist(lo, hi - 1); + return SingleMedianOfThree(A, dist(gen1), dist(gen1), dist(gen1) + 1); + } + case PIVOT_MEDIAN5: + { + return MedianOfFive(A, lo, hi); + } + case PIVOT_MEDIAN5RANDOM: + { + if (lo > hi) + { + ssize_t temp = lo; + lo = hi; + hi = temp; + } + std::uniform_int_distribution dist(lo, hi - 1); + if (hi - lo < 25) + { + return SingleMedianOfThree(A, dist(gen1), dist(gen1), dist(gen1) + 1); + } + std::array samples = { dist(gen1), dist(gen1), dist(gen1), dist(gen1), dist(gen1) }; + PivotInsertionSort(A, samples); + return samples[2]; + } + case PIVOT_MEDIAN7: + { + return MedianOfSeven(A, lo, hi); + } + case PIVOT_MEDIAN7RANDOM: + { + if (lo > hi) + { + ssize_t temp = lo; + lo = hi; + hi = temp; + } + std::uniform_int_distribution dist(lo, hi - 1); + if (hi - lo < 49) + { + return SingleMedianOfThree(A, dist(gen1), dist(gen1), dist(gen1) + 1); + } + std::array samples = { dist(gen1), dist(gen1), dist(gen1), dist(gen1), dist(gen1), dist(gen1), dist(gen1) }; + PivotInsertionSort(A, samples); + return samples[3]; + } + case PIVOT_NINTHER: + { + return NintherPivot(A, lo, hi); + } + case PIVOT_RANDOMNINTHER: + { + if (lo > hi) + { + ssize_t temp = lo; + lo = hi; + hi = temp; + } + std::uniform_int_distribution dist(lo, hi - 1); + if (hi - lo < 81) + { + return SingleMedianOfThree(A, dist(gen1), dist(gen1), dist(gen1) + 1); + } + ssize_t lo1 = dist(gen1), lo2 = dist(gen1), lo3 = dist(gen1), lo4 = dist(gen1), lo5 = dist(gen1); + ssize_t lo6 = dist(gen1), lo7 = dist(gen1), lo8 = dist(gen1), lo9 = dist(gen1); + ssize_t g1 = SingleMedianOfThree(A, lo1, lo2, lo3 + 1); + ssize_t g2 = SingleMedianOfThree(A, lo4, lo5, lo6 + 1); + ssize_t g3 = SingleMedianOfThree(A, lo7, lo8, lo9 + 1); + return SingleMedianOfThree(A, g1, g2, g3 + 1); + } + case PIVOT_MEDIAN9: + { + if (hi - lo < 81) { return SingleMedianOfThree(A, lo, (lo + hi) / 2, hi); } + ssize_t segment = (hi - lo) / 9; + std::array samples = { lo, lo + segment, lo + segment * 2, lo + segment * 3, lo + segment * 4, lo + segment * 5, lo + segment * 6, lo + segment * 7, lo + segment * 8 }; + PivotInsertionSort(A, samples); + return samples[4]; + } + case PIVOT_MEDIAN9RANDOM: + { + if (lo > hi) + { + ssize_t temp = lo; + lo = hi; + hi = temp; + } + std::uniform_int_distribution dist(lo, hi - 1); + if (hi - lo < 81) + { + return SingleMedianOfThree(A, dist(gen1), dist(gen1), dist(gen1) + 1); + } + std::array samples = { dist(gen1), dist(gen1), dist(gen1), dist(gen1), dist(gen1), dist(gen1), dist(gen1), dist(gen1), dist(gen1) }; + PivotInsertionSort(A, samples); + return samples[4]; + } + case PIVOT_MEDIAN11: + { + if (hi - lo < 121) { return SingleMedianOfThree(A, lo, (lo + hi) / 2, hi); } + ssize_t segment = (hi - lo) / 11; + std::array samples = {lo, lo + segment, lo + segment * 2, lo + segment * 3, lo + segment * 4, lo + segment * 5, lo + segment * 6, lo + segment * 7, lo + segment * 8, lo + segment * 9, lo + segment * 10 }; + PivotShellSort(A, samples); + return samples[5]; + break; + } + case PIVOT_MEDIAN11RANDOM: + { + if (lo > hi) + { + ssize_t temp = lo; + lo = hi; + hi = temp; + } + std::uniform_int_distribution dist(lo, hi - 1); + if (hi - lo < 121) + { + return SingleMedianOfThree(A, dist(gen1), dist(gen1), dist(gen1) + 1); + } + std::array samples = { dist(gen1), dist(gen1), dist(gen1), dist(gen1), dist(gen1), dist(gen1), dist(gen1), dist(gen1), dist(gen1), dist(gen1), dist(gen1) }; + PivotShellSort(A, samples); + return samples[5]; + break; + } + case PIVOT_MEDIAN15: + { + if (hi - lo < 75) { return MedianOfFive(A, lo, hi); } + ssize_t segment = (hi - lo) / 3; + ssize_t g1 = MedianOfFive(A, lo, lo + segment); + ssize_t g2 = MedianOfFive(A, lo + segment, lo + segment * 2); + ssize_t g3 = MedianOfFive(A, lo + segment * 2, hi); + return SingleMedianOfThree(A, g1, g2, g3 + 1); + } + case PIVOT_MEDIAN21: + { + if (hi - lo < 143) { return MedianOfSeven(A, lo, hi); } + ssize_t segment = (hi - lo) / 3; + ssize_t g1 = MedianOfSeven(A, lo, lo + segment); + ssize_t g2 = MedianOfSeven(A, lo + segment, lo + segment * 2); + ssize_t g3 = MedianOfSeven(A, lo + segment * 2, hi); + return SingleMedianOfThree(A, g1, g2, g3 + 1); + } + case PIVOT_THREENINTHER: + { + if (hi - lo < 243) { return NintherPivot(A, lo, hi); } + ssize_t segment = (hi - lo) / 3; + ssize_t g1 = NintherPivot(A, lo, lo + segment); + ssize_t g2 = NintherPivot(A, lo + segment, lo + segment * 2); + ssize_t g3 = NintherPivot(A, lo + segment * 2, hi); + return SingleMedianOfThree(A, g1, g2, g3 + 1); + } + default: + { + return lo; + } + } +} + +wxArrayString QuickSortPivotText() +{ + wxArrayString sl; + + sl.Add(_("First Item")); + sl.Add(_("Last Item")); + sl.Add(_("Middle Item")); + sl.Add(_("Random Item")); + sl.Add(_("Median of Three")); + sl.Add(_("Random Median of Three")); + sl.Add(_("Median of Five")); + sl.Add(_("Random Median of Five")); + sl.Add(_("Median of Seven")); + sl.Add(_("Random Median of Seven")); + sl.Add(_("Ninther")); + sl.Add(_("Random Ninther")); + sl.Add(_("Median of Nine")); + sl.Add(_("Random Median of Nine")); + sl.Add(_("Median of Eleven")); + sl.Add(_("Random Median of Eleven")); + sl.Add(_("Median of Fifteen")); + sl.Add(_("Median of Twenty-One")); + sl.Add(_("Median of Three Ninther")); + return sl; +} + +// **************************************************************************** +// *** Quick Sort LR (in-place, pointers at left and right, pivot is middle element) + +// by myself (Timo Bingmann), based on Hoare's original code + +void QuickSortLR(SortArray& A, ssize_t lo, ssize_t hi) +{ + // pick pivot and watch + std::atomic p{ QuickSortSelectPivot(A, lo, hi + 1) }; + value_type pivot = A[p.load()]; + A.watch(&p, 2); + + std::atomic i{ lo }, j{ hi }; + A.watch(&i, 3); + A.watch(&j, 3); + + while (i.load() <= j.load()) + { + while (A[i.load()] < pivot) { i++; } + + while (A[j.load()] > pivot) { j--; } + + if (i.load() <= j.load()) + { + A.swap(i.load(), j.load()); + + // follow pivot if it is swapped + if (p.load() == i.load()) p.store(j.load()); + else if (p.load() == j.load()) p.store(i.load()); + + i++, j--; + } + } + + A.unwatch_all(); + + if (lo < j) + QuickSortLR(A, lo, j.load()); + + if (i < hi) + QuickSortLR(A, i.load(), hi); +} + +void QuickSortLR(SortArray& A) +{ + return QuickSortLR(A, 0, A.size()-1); +} + +// **************************************************************************** +// *** Quick Sort LL (in-place, two pointers at left, pivot is first element and moved to right) + +// by myself (Timo Bingmann), based on CLRS' 3rd edition + +size_t PartitionLL(SortArray& A, size_t lo, size_t hi) +{ + // pick pivot and move to back + size_t p = QuickSortSelectPivot(A, lo, hi); + + value_type pivot = A[p]; + A.swap(p, hi - 1); + A.mark(hi - 1); + + std::atomic i{static_cast(lo)}; + A.watch(&i, 3); + + for (size_t j = lo; j < hi - 1; ++j) + { + if (A[j] <= pivot) { + A.swap(i.load(), j); + ++i; + } + } + + A.swap(i.load(), hi - 1); + A.unmark(hi - 1); + A.unwatch_all(); + + return i.load(); +} + +void QuickSortLL(SortArray& A, size_t lo, size_t hi) +{ + if (lo + 1 < hi) + { + size_t mid = PartitionLL(A, lo, hi); + + QuickSortLL(A, lo, mid); + QuickSortLL(A, mid+1, hi); + } +} + +void QuickSortLL(SortArray& A) +{ + return QuickSortLL(A, 0, A.size()); +} + +// **************************************************************************** +// *** Quick Sort Ternary (in-place, two pointers at left, pivot is first element and moved to right) + +// by myself (Timo Bingmann), loosely based on multikey quicksort by B&S + +void QuickSortTernaryLR(SortArray& A, ssize_t lo, ssize_t hi) +{ + if (hi <= lo) return; + + int cmp; + + // pick pivot and swap to back + ssize_t piv = QuickSortSelectPivot(A, lo, hi + 1); + A.swap(piv, hi); + A.mark(hi); + + const value_type& pivot = A[hi]; + + // schema: |p === |i <<< | ??? |j >>> |q === |piv + std::atomic i{ lo }, j{hi - 1}; + ssize_t p = lo, q = hi - 1; + + A.watch(&i, 3); + A.watch(&j, 3); + + for (;;) + { + // partition on left + while (i.load() <= j.load() && (cmp = A[i.load()].cmp(pivot)) <= 0) + { + if (cmp == 0) { + A.mark(p, 4); + A.swap(i.load(), p++); + } + ++i; + } + + // partition on right + while (i.load() <= j.load() && (cmp = A[j.load()].cmp(pivot)) >= 0) + { + if (cmp == 0) { + A.mark(q, 4); + A.swap(j.load(), q--); + } + --j; + } + + if (i.load() > j.load()) break; + + // swap item between < > regions + A.swap(i++, j--); + } + + // swap pivot to right place + A.swap(i.load(), hi); + A.mark_swap(i.load(), hi); + + ssize_t num_less = i.load() - p; + ssize_t num_greater = q - j.load(); + + // swap equal ranges into center, but avoid swapping equal elements + j.store(i.load() - 1); i.store(i.load() + 1); + + ssize_t pe = lo + std::min(p - lo, num_less); + for (ssize_t k = lo; k < pe; k++, j--) { + A.swap(k, j.load()); + A.mark_swap(k, j.load()); + } + + ssize_t qe = hi - 1 - std::min(hi - 1 - q, num_greater - 1); // one already greater at end + for (ssize_t k = hi - 1; k > qe; k--, i++) { + A.swap(i.load(), k); + A.mark_swap(i.load(), k); + } A.unwatch_all(); A.unmark_all(); @@ -538,16 +1191,18 @@ void QuickSortTernaryLR(SortArray& A) // by myself (Timo Bingmann) -std::pair PartitionTernaryLL(SortArray& A, ssize_t lo, ssize_t hi) +std::pair PartitionTernaryLL(SortArray& A, ssize_t lo, ssize_t hi) { // pick pivot and swap to back ssize_t p = QuickSortSelectPivot(A, lo, hi); value_type pivot = A[p]; - A.swap(p, hi-1); - A.mark(hi-1); + A.swap(p, hi - 1); + A.mark(hi - 1); - volatile ssize_t i = lo, k = hi-1; + std::atomic i{lo}; + ssize_t k = hi - 1; + A.watch(&i, 3); for (ssize_t j = lo; j < k; ++j) @@ -556,7 +1211,7 @@ std::pair PartitionTernaryLL(SortArray& A, ssize_t lo, ssize_t if (cmp == 0) { A.swap(--k, j); --j; // reclassify A[j] - A.mark(k,4); + A.mark(k, 4); } else if (cmp < 0) { A.swap(i++, j); @@ -567,15 +1222,15 @@ std::pair PartitionTernaryLL(SortArray& A, ssize_t lo, ssize_t // in the first step of the following swap loop. A.unwatch_all(); - ssize_t j = i + (hi-k); + ssize_t j = i.load() + (hi - k); - for (ssize_t s = 0; s < hi-k; ++s) { - A.swap(i+s, hi-1-s); - A.mark_swap(i+s, hi-1-s); + for (ssize_t s = 0; s < hi - k; ++s) { + A.swap(i.load() + s, hi - 1 - s); + A.mark_swap(i.load() + s, hi - 1 - s); } A.unmark_all(); - return std::make_pair(i,j); + return std::make_pair(i.load(), j); } void QuickSortTernaryLL(SortArray& A, size_t lo, size_t hi) @@ -613,27 +1268,24 @@ void dualPivotYaroslavskiy(class SortArray& a, int left, int right) a.mark(left); a.mark(right); - volatile ssize_t l = left + 1; - volatile ssize_t g = right - 1; - volatile ssize_t k = l; - + std::atomic l{ left + 1 }, g{ right - 1 }, k{ l.load() }; + a.watch(&l, 3); a.watch(&g, 3); a.watch(&k, 3); - while (k <= g) + while (k.load() <= g) { - if (a[k] < p) { - a.swap(k, l); + if (a[k.load()] < p) { + a.swap(k.load(), l.load()); ++l; } - else if (a[k] >= q) { - while (a[g] > q && k < g) --g; - a.swap(k, g); - --g; - - if (a[k] < p) { - a.swap(k, l); + else if (a[k.load()] >= q) { + while (a[g.load()] > q && k.load() < g) { --g; } + a.swap(k.load(), g.load()); + --g; + if (a[k.load()] < p) { + a.swap(k.load(), l.load()); ++l; } } @@ -641,15 +1293,16 @@ void dualPivotYaroslavskiy(class SortArray& a, int left, int right) } --l; ++g; - a.swap(left, l); - a.swap(right, g); + + a.swap(left, l.load()); + a.swap(right, g.load()); a.unmark_all(); a.unwatch_all(); - dualPivotYaroslavskiy(a, left, l - 1); - dualPivotYaroslavskiy(a, l + 1, g - 1); - dualPivotYaroslavskiy(a, g + 1, right); + dualPivotYaroslavskiy(a, left, l.load() - 1); + dualPivotYaroslavskiy(a, l.load() + 1, g.load() - 1); + dualPivotYaroslavskiy(a, g.load() + 1, right); } } @@ -675,6 +1328,43 @@ void BubbleSort(SortArray& A) } } +void OptimizedBubbleSort(SortArray& A) +{ + for (size_t i = 0; i < A.size() - 1; ++i) + { + bool sorted = true; + for (size_t j = 0; j < A.size() - 1 - i; ++j) + { + if (A[j] > A[j + 1]) + { + A.swap(j, j + 1); + sorted = false; + } + } + if (sorted == true) { break; } + } +} + +void TargetedBubbleSort(SortArray& A) +{ + bool sorted = false; + size_t target = A.size() - 1, lastSwapped = 0; + while (!sorted) + { + sorted = true; + for (size_t i = 0; i < target; ++i) + { + if (A[i] > A[i + 1]) + { + A.swap(i, i + 1); + lastSwapped = i; + sorted = false; + } + } + target = lastSwapped; + } +} + // **************************************************************************** // *** Cocktail Shaker Sort @@ -710,36 +1400,174 @@ void CocktailShakerSort(SortArray& A) } } -// **************************************************************************** -// *** Gnome Sort - -// from http://en.wikipediA.org/wiki/Gnome_sort - -void GnomeSort(SortArray& A) +void DualCocktailShakerSort(SortArray& A) { - for (size_t i = 1; i < A.size(); ) + size_t lo = 0, hi = A.size() - 1; + while (lo < hi) { - if (A[i] >= A[i-1]) + size_t lo_mov = 0, hi_mov = 0; + for (size_t i = lo + 1, j = hi - 1; i <= hi; ++i, --j) { - ++i; - } - else - { - A.swap(i, i-1); - if (i > 1) --i; + if (A[i - 1] > A[i]) + { + A.swap(i - 1, i); + lo_mov = i; + } + if (A[j + 1] < A[j]) + { + A.swap(j + 1, j); + hi_mov = j; + } } + lo = hi_mov; + hi = lo_mov; } } // **************************************************************************** -// *** Comb Sort - -// from http://en.wikipediA.org/wiki/Comb_sort +// *** Circle Sort -void CombSort(SortArray& A) +bool CircleSortRec(SortArray& A, size_t low, size_t high, size_t len) { - const double shrink = 1.3; - + bool swapped = false; + if (low == high) { return false; } + size_t lo = low, hi = high; + while (lo < hi) + { + if (hi < len && A[lo] > A[hi]) + { + A.swap(lo, hi); + swapped = true; + } + ++lo; --hi; + } + size_t mid = (high - low) / 2; + bool firstHalf = CircleSortRec(A, low, low + mid, len); + bool secondHalf = false; + if (low + mid + 1 < len) + { + secondHalf = CircleSortRec(A, low + mid + 1, high, len); + } + return swapped || firstHalf || secondHalf; +} + +void CircleSort(SortArray& A) +{ + size_t len = A.size(), n = 1; + for (; n < len; n *= 2) {} + while (CircleSortRec(A, 0, n - 1, len)) {} +} + +bool CircleSortIte(SortArray& A, size_t length, size_t arr_len) +{ + bool swapped = false; + for (size_t gap = length / 2; gap > 0; gap /= 2) + { + for (size_t start = 0; start + gap < arr_len; start += 2 * gap) + { + size_t high = start + 2 * gap - 1, low = start; + while (low < high) + { + if (high < arr_len && A[low] > A[high]) + { + A.swap(low, high); + swapped = true; + } + ++low; --high; + } + } + } + return swapped; +} + +void CircleSort2(SortArray& A) +{ + size_t len = A.size(), n = 1; + for (; n < len; n *= 2) {} + while (CircleSortIte(A, n, len)) {} +} + +void IntroCircleSort(SortArray& A) +{ + size_t len = A.size(), threshold = 0, n = 1, iterations = 0; + for (; n < len; n *= 2, ++threshold) {} + threshold /= 2; + do + { + ++iterations; + if (iterations >= threshold) + { + InsertSort(A, 0, len); + break; + } + } + while (CircleSortRec(A, 0, n - 1, len)); +} + +void IntroIteCircleSort(SortArray& A) +{ + size_t len = A.size(), threshold = 0, n = 1, iterations = 0; + for (; n < len; n *= 2, ++threshold) {} + threshold /= 2; + do + { + ++iterations; + if (iterations >= threshold) + { + InsertSort(A, 0, len); + break; + } + } while (CircleSortIte(A, n, len)); +} + +// **************************************************************************** +// *** Gnome Sort + +// from http://en.wikipediA.org/wiki/Gnome_sort + +void GnomeSort(SortArray& A) +{ + for (size_t i = 1; i < A.size(); ) + { + if (A[i] >= A[i-1]) + { + ++i; + } + else + { + A.swap(i, i-1); + if (i > 1) --i; + } + } +} + +void OptimizedGnomeSort(SortArray& A) +{ + size_t prev = 0; + for (size_t i = 1; i < A.size(); ) + { + if (i == 0 || A[i] >= A[i - 1]) + { + if (prev != 0) { i += prev; prev = 0; } + ++i; + } + else + { + A.swap(i, i - 1); + --i; ++prev; + } + } +} + +// **************************************************************************** +// *** Comb Sort + +// from http://en.wikipediA.org/wiki/Comb_sort + +void CombSort(SortArray& A) +{ + const double shrink = 1.3; + bool swapped = false; size_t gap = A.size(); @@ -903,7 +1731,7 @@ void HeapSort(SortArray& A) // by myself (Timo Bingmann) -void RadixSortMSD(SortArray& A, size_t lo, size_t hi, size_t depth) +void RadixSortMSD2(SortArray& A, size_t lo, size_t hi, size_t depth) { A.mark(lo); A.mark(hi-1); @@ -955,14 +1783,109 @@ void RadixSortMSD(SortArray& A, size_t lo, size_t hi, size_t depth) for (size_t i = 0; i < RADIX; ++i) { if (count[i] > 1) - RadixSortMSD(A, sum, sum+count[i], depth+1); + RadixSortMSD2(A, sum, sum+count[i], depth+1); sum += count[i]; } } +void RadixSortMSD2(SortArray& A) +{ + return RadixSortMSD2(A, 0, A.size(), 0); +} + +/* + MIT License + + Copyright (c) 2019 w0rthy + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), to deal + in the Software without restriction, including without limitation the rights + to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in all + copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + SOFTWARE. +*/ + +size_t maxLog(SortArray& A, size_t n, size_t base) +{ + int max = A[0]; + for (size_t i = 1, j = n - 1; i <= j; ++i, --j) + { + int ele1 = A[i].get(), ele2 = A[j]; + if (ele1 > max) { max = ele1; } + if (ele2 > max) { max = ele2; } + } + size_t digit = static_cast(log(max) / log(base)); + return digit; +} + +int getDigit2(int a, double power, int radix) +{ + double digit = (a / static_cast(pow(radix, power)) % radix); + return static_cast(digit); +} + +void transcribeMSD(SortArray& A, std::vector>& registers, size_t start, size_t min) +{ + size_t total = start, temp = 0; + for (const std::vector& arr : registers) + { + total += arr.size(); + } + + for (int i = registers.size() - 1; i >= 0; --i) + { + for (int j = registers[i].size() - 1; j >= 0; --j) + { + size_t loc = total + min - temp - 1; + A.set(loc, registers[i].at(j)); + A[loc].get(); + ++temp; + } + } +} + +void radixMSD(SortArray& A, size_t len, size_t min, size_t max, size_t radix, double pow) +{ + if (min >= max || pow < 0) { return; } + A.mark(min); A.mark(max - 1); + + std::vector> registers(radix, std::vector()); + + for (size_t i = min; i < max; ++i) + { + int ele = A[i].get(); + int digit = getDigit2(ele, pow, radix); + registers[digit].push_back(A[i]); + } + + transcribeMSD(A, registers, 0, min); + A.unmark_all(); + + size_t sum = 0; + for (size_t i = 0; i < registers.size(); ++i) + { + radixMSD(A, len, sum + min, sum + min + registers[i].size(), radix, pow - 1); + sum += registers[i].size(); + registers[i].clear(); + } +} + void RadixSortMSD(SortArray& A) { - return RadixSortMSD(A, 0, A.size(), 0); + size_t n = A.size(), maxPow = maxLog(A, n, 4); + radixMSD(A, n, 0, n, 4, (double)maxPow); } // **************************************************************************** @@ -1014,672 +1937,2537 @@ void RadixSortLSD(SortArray& A) } // **************************************************************************** -// *** Use STL Sorts via Iterator Adapters - -void StlSort(SortArray& A) -{ - std::sort(MyIterator(&A,0), MyIterator(&A,A.size())); -} - -void StlStableSort(SortArray& A) +// *** In-Place Radix Sort LSD +/* + Copyright (c) 2019 w0rthy + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), to deal + in the Software without restriction, including without limitation the rights + to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + The above copyright notice and this permission notice shall be included in all + copies or substantial portions of the Software. + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + SOFTWARE. +*/ + +void shiftElement(SortArray& A, size_t start, size_t end) { - std::stable_sort(MyIterator(&A,0), MyIterator(&A,A.size())); + if (start < end) + { + while (start < end) + { + A[start].get(); + A.swap(start, start + 1); + ++start; + } + } + else + { + while (start > end) + { + A[start].get(); + A.swap(start, start - 1); + --start; + } + } } -void StlHeapSort(SortArray& A) +void InPlaceRadixSortLSD(SortArray& A) { - std::make_heap(MyIterator(&A,0), MyIterator(&A,A.size())); - std::sort_heap(MyIterator(&A,0), MyIterator(&A,A.size())); + size_t pos = 0, n = A.size(); + int bucket = 20; + std::vector buckets(bucket - 1, 0); + size_t maxPow = maxLog(A, n, bucket); + for (size_t p = 0; p <= maxPow; ++p) + { + for (size_t i = 0; i < buckets.size(); ++i) { buckets[i] = n - 1; } + pos = 0; + for (size_t i = 0; i < n; ++i) + { + int ele = A[pos].get(); + int digit = getDigit2(ele, (double)p, bucket); + if (digit == 0) { ++pos; } + else + { + shiftElement(A, pos, buckets[digit - 1]); + for (size_t j = digit - 1; j > 0; --j) + { + buckets[j - 1] = buckets[j - 1] - 1; + } + } + } + } } // **************************************************************************** -// *** BogoSort and more slow sorts +// *** Rotate Radix MSD/LSD Sort -// by myself (Timo Bingmann) +/* + Copyright (c) 2020-2021 aphitorite -bool BogoCheckSorted(SortArray& A) + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), to deal + in the Software without restriction, including without limitation the rights + to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in all + copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + SOFTWARE. +*/ + +static const int base = 4; + +int shift(int n, int q) { - size_t i; - value_type prev = A[0]; - A.mark(0); - for (i = 1; i < A.size(); ++i) + while (q > 0) { - value_type val = A[i]; - if (prev > val) break; - prev = val; - A.mark(i); + n /= base; + --q; } + return n; +} - if (i == A.size()) { - // this is amazing. - return true; +int binSearch(SortArray& A, int a, int b, int d, int p) +{ + while (a < b) + { + int m = (a + b) / 2; + int ele = A[m].get(); + int result = static_cast(getDigit2(static_cast(ele), static_cast(p), static_cast(base))); + if (result >= d) { b = m; } + else { a = m + 1; } } + return a; +} - // unmark - while (i > 0) A.unmark(i--); - A.unmark(0); - - return false; +void rotateMerge(SortArray& A, int a, int m, int b, int da, int db, int p) +{ + if (b - a < 2 || db - da < 2) { return; } + int dm = (da + db) / 2; + int m1 = binSearch(A, a, m, dm, p); + int m2 = binSearch(A, m, b, dm, p); + rotate(A, m1, m, m2); + m = m1 + (m2 - m); + rotateMerge(A, m, m2, b, dm, db, p); + rotateMerge(A, a, m1, m, da, dm, p); } -void BogoSort(SortArray& A) +void rotateMergeSort(SortArray& A, int a, int b, int p) { - // keep a permutation of [0,size) - std::vector perm(A.size()); + if (b - a < 2) { return; } + int m = (a + b) / 2; + rotateMergeSort(A, a, m, p); + rotateMergeSort(A, m, b, p); + rotateMerge(A, a, m, b, 0, base, p); +} - for (size_t i = 0; i < A.size(); ++i) - perm[i] = i; +int dist(SortArray& A, int a, int b, int p) +{ + rotateMergeSort(A, a, b, p); + return binSearch(A, a, b, 1, p); +} - while (1) +void RotateRadixSortMSD(SortArray& A) +{ + int len = static_cast(A.size()); + int q = static_cast(maxLog(A, static_cast(len), static_cast(base))); + int m = 0, i = 0, b = len; + while (i < len) { - // check if array is sorted - if (BogoCheckSorted(A)) break; - - // pick a random permutation of indexes - std::random_shuffle(perm.begin(), perm.end()); - - // permute array in-place - std::vector pmark(A.size(), 0); - - for (size_t i = 0; i < A.size(); ++i) + int p = 0; + if (b - i < 1) { p = i; } + else { p = dist(A, i, b, q); } + if (q == 0) { - if (pmark[i]) continue; - - // walk a cycle - size_t j = i; - - //std::cout << "cycle start " << j << " -> " << perm[j] << "\n"; - - while ( perm[j] != i ) + m += base; + int t = m / base; + while (t % base == 0) { - ASSERT(!pmark[j]); - A.swap(j, perm[j]); - pmark[j] = 1; - - j = perm[j]; - //std::cout << "cycle step " << j << " -> " << perm[j] << "\n"; + t /= base; + ++q; + } + i = b; + while (b < len) + { + int ele = A[b].get(); + if (shift(ele, q + 1) == shift(m, q + 1)) { ++b; } + else { break; } } - //std::cout << "cycle end\n"; - - ASSERT(!pmark[j]); - pmark[j] = 1; } - - //std::cout << "permute end\n"; - - for (size_t i = 0; i < A.size(); ++i) - ASSERT(pmark[i]); + else + { + b = p; + --q; + } } } -void BozoSort(SortArray& A) +void RotateRadixSortLSD(SortArray& A) { - srand(time(NULL)); - - while (1) + int len = static_cast(A.size()); + int max = static_cast(maxLog(A, static_cast(len), static_cast(base))); + for (int i = 0; i <= max; ++i) { - // check if array is sorted - if (BogoCheckSorted(A)) break; - - // swap two random items - A.swap(rand() % A.size(), rand() % A.size()); + rotateMergeSort(A, 0, len, i); } } // **************************************************************************** -// *** Bitonic Sort - -// from http://www.iti.fh-flensburg.de/lang/algorithmen/sortieren/bitonic/oddn.htm +// *** Use STL Sorts via Iterator Adapters -namespace BitonicSortNS { +void siftDown(SortArray& A, size_t root, size_t dist, size_t start, bool isMax) +{ + int compareVal = 0; + if (isMax) { compareVal = -1; } + else { compareVal = 1; } + while (root <= dist / 2) + { + size_t leaf = 2 * root; + int compVal = 0; + + if (leaf < dist) + { + if (A[start + leaf - 1] < A[start + leaf]) { compVal = -1; } + else if (A[start + leaf - 1] > A[start + leaf]) { compVal = 1; } + if (compVal == compareVal) { ++leaf; } + } + + if (A[start + root - 1] < A[start + leaf - 1]) { compVal = -1; } + else if (A[start + root - 1] > A[start + leaf - 1]) { compVal = 1; } + else { compVal = 0; } -static const bool ASCENDING = true; // sorting direction + if (compVal == compareVal) + { + A.swap(start + root - 1, start + leaf - 1); + root = leaf; + } + else { break; } + } +} -static void compare(SortArray& A, int i, int j, bool dir) +void heapifyArr(SortArray& A, size_t low, size_t high, bool isMax) { - if (dir == (A[i] > A[j])) - A.swap(i, j); + size_t len = high - low; + for (size_t i = len / 2; i >= 1; --i) + { + siftDown(A, i, len, low, isMax); + } } -static void bitonicMerge(SortArray& A, int lo, int n, bool dir) +void reverseArr(SortArray& A, size_t start, size_t len) { - if (n > 1) + for (size_t i = start; i < start + ((len - start + 1) / 2); ++i) { - int m = largestPowerOfTwoLessThan(n); - - for (int i = lo; i < lo + n - m; i++) - compare(A, i, i+m, dir); - - bitonicMerge(A, lo, m, dir); - bitonicMerge(A, lo + m, n - m, dir); + A.swap(i, start + len - 1); } } -static void bitonicSort(SortArray& A, int lo, int n, bool dir) +void HeapSort2(SortArray& A, size_t start, size_t len, bool isMax) { - if (n > 1) + heapifyArr(A, start, len, isMax); + for (size_t i = len - start; i > 1; --i) { - int m = n / 2; - bitonicSort(A, lo, m, !dir); - bitonicSort(A, lo + m, n - m, dir); - bitonicMerge(A, lo, n, dir); + A.swap(start, start + i - 1); + siftDown(A, 1, i - 1, start, isMax); } -} -} // namespace BitonicSortNS + if (!isMax) + { + reverseArr(A, start, start + len - 1); + } +} -void BitonicSort(SortArray& A) +size_t floorLogBaseTwo(size_t a) { - BitonicSortNS::bitonicSort(A, 0, A.size(), BitonicSortNS::ASCENDING); + return static_cast(floor(log(a) / log(2))); } -// **************************************************************************** -// *** Bitonic Sort as "Parallel" Sorting Network - -// from http://www.iti.fh-flensburg.de/lang/algorithmen/sortieren/bitonic/oddn.htm - -// modified to first record the recursively generated swap sequence, and then -// sort it back into the order a parallel sorting network would perform the -// swaps in - -namespace BitonicSortNetworkNS { - -struct swappair_type +value_type gccmedianof3(SortArray& A, size_t left, size_t mid, size_t right) { - // swapped positions - unsigned int i,j; - - // depth of recursions: sort / merge - unsigned int sort_depth, merge_depth; - - swappair_type(unsigned int _i, unsigned int _j, - unsigned int _sort_depth, unsigned int _merge_depth) - : i(_i), j(_j), - sort_depth(_sort_depth), merge_depth(_merge_depth) - { } - - // order relation for sorting swaps - bool operator < (const swappair_type& b) const + if (A[left] < A[mid]) { - if (sort_depth != b.sort_depth) - return sort_depth > b.sort_depth; - - if (merge_depth != b.merge_depth) - return merge_depth < b.merge_depth; - - return i < b.i; + if (A[mid] < A[right]) + { + A.swap(left, mid); + } + else if (A[left] < A[right]) + { + A.swap(left, right); + } } -}; - -typedef std::vector sequence_type; -std::vector sequence; - -void replay(SortArray& A) -{ - for (sequence_type::const_iterator si = sequence.begin(); - si != sequence.end(); ++si) + else if (A[left] < A[right]) { return A[left]; } + else if (A[mid] < A[right]) { - if (A[si->i] > A[si->j]) - A.swap(si->i, si->j); + A.swap(left, right); + } + else + { + A.swap(mid, right); } + return A[left]; } -static const bool ASCENDING = true; // sorting direction - -static void compare(SortArray& /* A */, unsigned int i, unsigned int j, bool dir, - unsigned int sort_depth, unsigned int merge_depth) +value_type medianof3(SortArray& A, size_t left, size_t mid, size_t right) { - // if (dir == (A[i] > A[j])) A.swap(i, j); - - if (dir) - sequence.push_back( swappair_type(i,j, sort_depth, merge_depth) ); - else - sequence.push_back( swappair_type(j,i, sort_depth, merge_depth) ); + if (A[right] < A[left]) { A.swap(left, right); } + if (A[mid] < A[left]) { A.swap(mid, left); } + if (A[right] < A[mid]) { A.swap(right, mid); } + return A[mid]; } -static void bitonicMerge(SortArray& A, unsigned int lo, unsigned int n, bool dir, - unsigned int sort_depth, unsigned int merge_depth) +size_t partitionArr(SortArray& A, size_t lo, size_t hi, value_type x) { - if (n > 1) + size_t i = lo, j = hi; + while (true) { - unsigned int m = largestPowerOfTwoLessThan(n); - - for (unsigned int i = lo; i < lo + n - m; i++) - compare(A, i, i + m, dir, sort_depth, merge_depth); + while (A[i] < x) { ++i; } + --j; + while (x < A[j]) { --j; } - bitonicMerge(A, lo, m, dir, sort_depth, merge_depth+1); - bitonicMerge(A, lo + m, n - m, dir, sort_depth, merge_depth+1); + if (!(i < j)) { return i; } + A.swap(i, j); + ++i; } } -static void bitonicSort(SortArray& A, unsigned int lo, unsigned int n, bool dir, - unsigned int sort_depth) +void introsortLoop(SortArray& A, size_t lo, size_t hi, size_t depth) { - if (n > 1) + size_t threshold = 16; + while (hi - lo > threshold) { - unsigned int m = n / 2; - bitonicSort(A, lo, m, !dir, sort_depth+1); - bitonicSort(A, lo + m, n - m, dir, sort_depth+1); - bitonicMerge(A, lo, n, dir, sort_depth, 0); + if (depth == 0) + { + HeapSort2(A, lo, hi, true); + return; + } + --depth; + size_t p = partitionArr(A, lo, hi, medianof3(A, lo, lo + ((hi - lo) / 2), hi - 1)); + introsortLoop(A, p, hi, depth); + hi = p; } + return; } -void sort(SortArray& A) +void StlSort(SortArray& A) { - sequence.clear(); - bitonicSort(A, 0, A.size(), BitonicSortNS::ASCENDING, 0); - std::sort(sequence.begin(), sequence.end()); - replay(A); - sequence.clear(); + size_t n = A.size(); + introsortLoop(A, 0, n, 2 * floorLogBaseTwo(n)); + InsertionSort(A); } -} // namespace BitonicSortNS +void StlSort2(SortArray& A) +{ + std::sort(MyIterator(&A, 0), MyIterator(&A, A.size())); +} -void BitonicSortNetwork(SortArray& A) +void StlStableSort(SortArray& A) { - BitonicSortNetworkNS::sort(A); + std::stable_sort(MyIterator(&A, 0), MyIterator(&A, A.size())); } -// **************************************************************************** -// *** Batcher's Odd-Even Merge Sort as "Parallel" Sorting Network +void StlHeapSort2(SortArray& A) +{ + std::make_heap(MyIterator(&A, 0), MyIterator(&A, A.size())); + std::sort_heap(MyIterator(&A, 0), MyIterator(&A, A.size())); +} -// from http://www.iti.fh-flensburg.de/lang/algorithmen/sortieren/networks/oemen.htm +void StlHeapSort(SortArray& A) +{ + HeapSort2(A, 0, A.size(), true); +} -// modified to first record the recursively generated swap sequence, and then -// sort it back into the order a parallel sorting network would perform the -// swaps in +// **************************************************************************** +// *** BogoSort and more slow sorts -namespace BatcherSortNetworkNS { +// by myself (Timo Bingmann) -struct swappair_type +bool BogoCheckSorted(SortArray& A) { - // swapped positions - unsigned int i,j; + size_t i; + A.mark(0); + for (i = 1; i < A.size(); ++i) + { + if (A[i - 1] > A[i]) break; + A.mark(i); + } - // depth of recursions: sort / merge - unsigned int sort_depth, merge_depth; + if (i == A.size()) { + // this is amazing. + return true; + } - swappair_type(unsigned int _i, unsigned int _j, - unsigned int _sort_depth, unsigned int _merge_depth) - : i(_i), j(_j), - sort_depth(_sort_depth), merge_depth(_merge_depth) - { } + // unmark + while (i > 0) A.unmark(i--); + A.unmark(0); - // order relation for sorting swaps - bool operator < (const swappair_type& b) const + return false; +} + +void BogoSort(SortArray& A) +{ + // keep a permutation of [0,size) + std::vector perm(A.size()); + + for (size_t i = 0; i < A.size(); ++i) + perm[i] = i; + + while (1) { - if (sort_depth != b.sort_depth) - return sort_depth > b.sort_depth; + // check if array is sorted + if (BogoCheckSorted(A)) break; - if (merge_depth != b.merge_depth) - return merge_depth > b.merge_depth; + // pick a random permutation of indexes + std::shuffle(perm.begin(), perm.end(), gen1); - return i < b.i; - } -}; + // permute array in-place + std::vector pmark(A.size(), 0); -typedef std::vector sequence_type; -std::vector sequence; + for (size_t i = 0; i < A.size(); ++i) + { + if (pmark[i]) continue; -void replay(SortArray& A) -{ - for (sequence_type::const_iterator si = sequence.begin(); - si != sequence.end(); ++si) - { - if (A[si->i] > A[si->j]) - A.swap(si->i, si->j); + // walk a cycle + size_t j = i; + + //std::cout << "cycle start " << j << " -> " << perm[j] << "\n"; + + while ( perm[j] != i ) + { + ASSERT(!pmark[j]); + A.swap(j, perm[j]); + pmark[j] = 1; + + j = perm[j]; + //std::cout << "cycle step " << j << " -> " << perm[j] << "\n"; + } + //std::cout << "cycle end\n"; + + ASSERT(!pmark[j]); + pmark[j] = 1; + } + + //std::cout << "permute end\n"; + + for (size_t i = 0; i < A.size(); ++i) + ASSERT(pmark[i]); } } -static void compare(SortArray& A, unsigned int i, unsigned int j, - unsigned int sort_depth, unsigned int merge_depth) +void BozoSort(SortArray& A) { - // skip all swaps beyond end of array - ASSERT(i < j); - if (j >= A.size()) return; + srand(time(nullptr)); - sequence.push_back( swappair_type(i,j, sort_depth, merge_depth) ); + while (1) + { + // check if array is sorted + if (BogoCheckSorted(A)) break; - //if (A[i] > A[j]) A.swap(i, j); + // swap two random items + A.swap(rand() % A.size(), rand() % A.size()); + } } -// lo is the starting position and n is the length of the piece to be merged, r -// is the distance of the elements to be compared -static void oddEvenMerge(SortArray& A, unsigned int lo, unsigned int n, unsigned int r, - unsigned int sort_depth, unsigned int merge_depth) +void flip(SortArray& A, size_t high) { - unsigned int m = r * 2; - if (m < n) + size_t low = 0; + while (low < high) { - // even subsequence - oddEvenMerge(A, lo, n, m, sort_depth, merge_depth+1); - // odd subsequence - oddEvenMerge(A, lo + r, n, m, sort_depth, merge_depth+1); - - for (unsigned int i = lo + r; i + r < lo + n; i += m) - compare(A, i, i + r, sort_depth, merge_depth); - } - else { - compare(A, lo, lo + r, sort_depth, merge_depth); + A[low].get(); + A.swap(low, high); + ++low; --high; } } -// sorts a piece of length n of the array starting at position lo -static void oddEvenMergeSort(SortArray& A, unsigned int lo, unsigned int n, - unsigned int sort_depth) +size_t find_max(SortArray& A, size_t n) // Optimized find_max method, searching the max element in n/2 time { - if (n > 1) + size_t max = 0; + for (size_t low = 1, hi = n; low <= hi; ++low, --hi) { - unsigned int m = n / 2; - oddEvenMergeSort(A, lo, m, sort_depth+1); - oddEvenMergeSort(A, lo + m, m, sort_depth+1); - oddEvenMerge(A, lo, n, 1, sort_depth, 0); + if (A[low] > A[max]) + { + max = low; + } + if (A[hi] > A[max]) + { + max = hi; + } } + return max; } -void sort(SortArray& A) +void PancakeSort(SortArray& A) { - sequence.clear(); + size_t n = A.size() - 1; + for (size_t cur_size = n; cur_size >= 1; --cur_size) + { + size_t max_idx = find_max(A, cur_size); + if (max_idx != cur_size) + { + if (max_idx > 0) { flip(A, max_idx); } + flip(A, cur_size); + } + } +} - unsigned int n = largestPowerOfTwoLessThan(A.size()); - if (n != A.size()) n *= 2; - oddEvenMergeSort(A, 0, n, 0); - std::sort(sequence.begin(), sequence.end()); +// **************************************************************************** +// *** Optimized Pancake Sort +/* + The MIT License (MIT) + + Copyright (c) 2021-2023 aphitorite + + Permission is hereby granted, free of charge, to any person obtaining a copy of + this software and associated documentation files (the "Software"), to deal in + the Software without restriction, including without limitation the rights to + use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of + the Software, and to permit persons to whom the Software is furnished to do so, + subject to the following conditions: + + The above copyright notice and this permission notice shall be included in all + copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +*/ + +void flip2(SortArray& A, size_t high) +{ + if (high > 0) { --high; } + size_t low = 0; + while (low < high) + { + A[low].get(); + A.swap(low, high); + ++low; --high; + } +} + + +bool mergeFlip(SortArray& A, size_t h1, size_t h2) +{ + if (h1 == 1 && h2 == 1) + { + if (A[0] > A[1]) { flip2(A, 2); } + return true; + } + size_t n = h1 + h2, m = n / 2; + if (h2 < h1) + { + if (h2 < 1) { return true; } + size_t i = 0, j = h2; + while (i < j) + { + size_t k = (i + j) / 2, loc = n - 1 - k; + A[loc].get(); + if (A[n - 1 - k - m] > A[loc]) { i = k + 1; } + else { j = k; } + } + flip2(A, n - m - i); + flip2(A, n - i); + if (mergeFlip(A, h2 - i, i + m - h2)) { flip2(A, m); } + flip2(A, n); + if (!mergeFlip(A, i, n - m - i)) { flip2(A, n - m); } + } + else + { + if (h1 < 1) { return false; } + size_t i = 0, j = h1; + while (i < j) + { + size_t k = (i + j) / 2; + A[k].get(); + if (A[k] < A[k + m]) { i = k + 1; } + else { j = k; } + } + flip2(A, i); + flip2(A, i + m); + if (mergeFlip(A, i + m - h1, h1 - i)) { flip2(A, m); } + flip2(A, n); + if (!mergeFlip(A, n - m - i, i)) { flip2(A, n - m); } + } + return true; +} + +void sortFlip(SortArray& A, size_t n) +{ + if (n < 2) { return; } + size_t h = n / 2; + sortFlip(A, h); + flip2(A, n); + sortFlip(A, n - h); + mergeFlip(A, n - h, h); +} + +void OptimizedPancakeSort(SortArray& A) +{ + sortFlip(A, A.size()); +} + +void BeadSort(SortArray& A) +{ + int max = A[0]; + int len = A.size(); + for (int n = 1; n < len; ++n) + { + int m = A[n].get(); + if (m > max) + { max = m; } + } + + std::vector> beads(len, std::vector(max, 0)); + + for (int i = 0; i < len; ++i) + { + int n = A[i].get(); + for (int j = 0; j < n; ++j) + { beads[i][j] = 1; } + } + + for (int j = 0; j < max; ++j) + { + int sum = 0; + for (int i = 0; i < len; ++i) + { + sum += beads[i][j]; + beads[i][j] = 0; + } + for (int i = len - 1; i >= len - sum; --i) + { + size_t k = static_cast(i); + A.set(k, ArrayItem(j + 1)); + A[k].get(); + } + } +} + +/* + MIT License + + Copyright (c) 2020 aphitorite + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), to deal + in the Software without restriction, including without limitation the rights + to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in all + copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + SOFTWARE. +*/ + +void GravitySort(SortArray& A) +{ + int n = A.size(); + int min = A[0], max = A[0]; + for (int i = 1; i < n; ++i) + { + int ele = A[i].get(); + if (ele < min) { min = ele; } + if (ele > max) { max = ele; } + } + std::vector x(n, 0); + std::vector y(max - min + 1, 0); + for (int i = 0; i < n; ++i) + { + int ele = A[i].get(); + x[i] = ele - min; + y[ele - min] = y[ele - min] + 1; + } + + int y_size = static_cast(y.size() - 1); + for (int i = y_size; i > 0; --i) + { + y[i - 1] = y[i - 1] += y[i]; + } + + for (int j = y_size; j >= 0; --j) + { + for (int i = 0; i < n; ++i) + { + int val = 0, val2 = 0; + if (i >= n - y[j]) { val = 1; } + if (x[i] >= j) { val2 = 1; } + int inc = val - val2, ele = A[i].get(); + A.set(i, ArrayItem(ele + inc)); + } + } +} + +/* + MIT License + + Copyright (c) 2024 aphitorite + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), to deal + in the Software without restriction, including without limitation the rights + to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in all + copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + SOFTWARE. + +*/ + +void dualSwap(SortArray& A, std::vector& keys, int a, int b) +{ + size_t z = static_cast(a); + A[z].get(); + A.swap(z, static_cast(b)); + value_type temp = keys[a]; + keys[a] = keys[b]; + keys[b] = temp; +} + +void reversal(SortArray& A, std::vector& keys, int a, int b) +{ + while (b - a > 1) { --b; dualSwap(A, keys, a, b); ++a; } +} + +bool isAdjacent(std::vector& keys, int a, int b, int N) +{ + return (keys[a] + 1) % N == keys[b] || (keys[b] + 1) % N == keys[a]; +} + +int findAdjacent(std::vector& keys, int e, int a, int N) +{ + while (!isAdjacent(keys, a, e, N)) { ++a; } + return a; +} + +void AdjacencyPancakeSort(SortArray& A) +{ + int a = 0, N = static_cast(A.size()), b = N; + if (N == 2) + { + reverseArr(A, a, a + 1); + return; + } + std::vector keys(N); + + for (int j = a; j < b; ++j) + { + int c = 0; + for (int i = a; i < b; ++i) + { + if (i == j) { continue; } + if (A[i] < A[j] || (A[i] == A[j] && i < j)) { ++c; } + } + keys[j - a] = ArrayItem(c); + } + + while (true) + { + int i = a; + while (i < b - 1 && isAdjacent(keys, i, i + 1, N)) { ++i; } + if (i == b - 1) { break; } + if (i == a) + { + int j = findAdjacent(keys, a, a + 2, N); + if (!isAdjacent(keys, j - 1, j, N)) + { reversal(A, keys, a, j); } + else + { + int k = findAdjacent(keys, a, j + 1, N); + if (!isAdjacent(keys, k - 1, k, N)) + { reversal(A, keys, a, k); } + else + { + reversal(A, keys, a, j + 1); + reversal(A, keys, a, j); + reversal(A, keys, a, k + 1); + reversal(A, keys, a, a + k - j); + } + } + } + else + { + int j = findAdjacent(keys, a, i + 1, N); + if (!isAdjacent(keys, j - 1, j, N)) + { reversal(A, keys, a, j); } + else + { + int k = findAdjacent(keys, i, i + 2, N); + if (k + 1 < b && isAdjacent(keys, k + 1, k, N)) + { + reversal(A, keys, a, i + 1); + reversal(A, keys, a, k + 1); + } + else + { + reversal(A, keys, a, k + 1); + reversal(A, keys, a, a + k - i); + if (!isAdjacent(keys, k - 1, k, N)) + { + if (j < k) + { + reversal(A, keys, a, k + 1); + reversal(A, keys, a, i + k - j + 1); + } + else + { + reversal(A, keys, a, j + 1); + reversal(A, keys, a, a + j - k); + } + } + } + } + } + } + + int i = a; + while (keys[i] != 0 && keys[i] != N - 1) { ++i; } + if (keys[i] == 0) + { + if (i == a) { return; } + reversal(A, keys, a, b); + i = b - 2 - (i - a); + } + else if (i == a) + { + reversal(A, keys, a, b); + return; + } + ++i; + reversal(A, keys, a, i); + reversal(A, keys, a, b); + reversal(A, keys, a, b - (i - a)); +} + +// **************************************************************************** +// *** Bitonic Sort + +// from http://www.iti.fh-flensburg.de/lang/algorithmen/sortieren/bitonic/oddn.htm + +namespace BitonicSortNS { + +static const bool ASCENDING = true; // sorting direction + +static void compare(SortArray& A, int i, int j, bool dir) +{ + if (dir == (A[i] > A[j])) + A.swap(i, j); +} + +static void bitonicMerge(SortArray& A, int lo, int n, bool dir) +{ + if (n > 1) + { + int m = largestPowerOfTwoLessThan(n); + + for (int i = lo; i < lo + n - m; i++) + compare(A, i, i+m, dir); + + bitonicMerge(A, lo, m, dir); + bitonicMerge(A, lo + m, n - m, dir); + } +} + +static void bitonicSort(SortArray& A, int lo, int n, bool dir) +{ + if (n > 1) + { + int m = n / 2; + bitonicSort(A, lo, m, !dir); + bitonicSort(A, lo + m, n - m, dir); + bitonicMerge(A, lo, n, dir); + } +} + +} // namespace BitonicSortNS + +void BitonicSort(SortArray& A) +{ + BitonicSortNS::bitonicSort(A, 0, A.size(), BitonicSortNS::ASCENDING); +} + +// **************************************************************************** +// *** Bitonic Sort as "Parallel" Sorting Network + +// from http://www.iti.fh-flensburg.de/lang/algorithmen/sortieren/bitonic/oddn.htm + +// modified to first record the recursively generated swap sequence, and then +// sort it back into the order a parallel sorting network would perform the +// swaps in + +namespace BitonicSortNetworkNS { + +struct swappair_type +{ + // swapped positions + unsigned int i,j; + + // depth of recursions: sort / merge + unsigned int sort_depth, merge_depth; + + swappair_type(unsigned int _i, unsigned int _j, + unsigned int _sort_depth, unsigned int _merge_depth) + : i(_i), j(_j), + sort_depth(_sort_depth), merge_depth(_merge_depth) + { } + + // order relation for sorting swaps + bool operator < (const swappair_type& b) const + { + if (sort_depth != b.sort_depth) + return sort_depth > b.sort_depth; + + if (merge_depth != b.merge_depth) + return merge_depth < b.merge_depth; + + return i < b.i; + } +}; + +typedef std::vector sequence_type; +std::vector sequence; + +void replay(SortArray& A) +{ + for (sequence_type::const_iterator si = sequence.begin(); + si != sequence.end(); ++si) + { + if (A[si->i] > A[si->j]) + A.swap(si->i, si->j); + } +} + +static const bool ASCENDING = true; // sorting direction + +static void compare(SortArray& /* A */, unsigned int i, unsigned int j, bool dir, + unsigned int sort_depth, unsigned int merge_depth) +{ + // if (dir == (A[i] > A[j])) A.swap(i, j); + + if (dir) + sequence.push_back( swappair_type(i,j, sort_depth, merge_depth) ); + else + sequence.push_back( swappair_type(j,i, sort_depth, merge_depth) ); +} + +static void bitonicMerge(SortArray& A, unsigned int lo, unsigned int n, bool dir, + unsigned int sort_depth, unsigned int merge_depth) +{ + if (n > 1) + { + unsigned int m = largestPowerOfTwoLessThan(n); + + for (unsigned int i = lo; i < lo + n - m; i++) + compare(A, i, i + m, dir, sort_depth, merge_depth); + + bitonicMerge(A, lo, m, dir, sort_depth, merge_depth+1); + bitonicMerge(A, lo + m, n - m, dir, sort_depth, merge_depth+1); + } +} + +static void bitonicSort(SortArray& A, unsigned int lo, unsigned int n, bool dir, + unsigned int sort_depth) +{ + if (n > 1) + { + unsigned int m = n / 2; + bitonicSort(A, lo, m, !dir, sort_depth+1); + bitonicSort(A, lo + m, n - m, dir, sort_depth+1); + bitonicMerge(A, lo, n, dir, sort_depth, 0); + } +} + +void sort(SortArray& A) +{ + sequence.clear(); + bitonicSort(A, 0, A.size(), BitonicSortNS::ASCENDING, 0); + std::sort(sequence.begin(), sequence.end()); replay(A); sequence.clear(); } -} // namespace BatcherSortNetworkNS +} // namespace BitonicSortNS + +void BitonicSortNetwork(SortArray& A) +{ + BitonicSortNetworkNS::sort(A); +} + +// **************************************************************************** +// *** Batcher's Odd-Even Merge Sort as "Parallel" Sorting Network + +// from http://www.iti.fh-flensburg.de/lang/algorithmen/sortieren/networks/oemen.htm + +// modified to first record the recursively generated swap sequence, and then +// sort it back into the order a parallel sorting network would perform the +// swaps in + +namespace BatcherSortNetworkNS { + +struct swappair_type +{ + // swapped positions + unsigned int i,j; + + // depth of recursions: sort / merge + unsigned int sort_depth, merge_depth; + + swappair_type(unsigned int _i, unsigned int _j, + unsigned int _sort_depth, unsigned int _merge_depth) + : i(_i), j(_j), + sort_depth(_sort_depth), merge_depth(_merge_depth) + { } + + // order relation for sorting swaps + bool operator < (const swappair_type& b) const + { + if (sort_depth != b.sort_depth) + return sort_depth > b.sort_depth; + + if (merge_depth != b.merge_depth) + return merge_depth > b.merge_depth; + + return i < b.i; + } +}; + +typedef std::vector sequence_type; +std::vector sequence; + +void replay(SortArray& A) +{ + for (sequence_type::const_iterator si = sequence.begin(); + si != sequence.end(); ++si) + { + if (A[si->i] > A[si->j]) + A.swap(si->i, si->j); + } +} + +static void compare(SortArray& A, unsigned int i, unsigned int j, + unsigned int sort_depth, unsigned int merge_depth) +{ + // skip all swaps beyond end of array + ASSERT(i < j); + if (j >= A.size()) return; + + sequence.push_back( swappair_type(i,j, sort_depth, merge_depth) ); + + //if (A[i] > A[j]) A.swap(i, j); +} + +// lo is the starting position and n is the length of the piece to be merged, r +// is the distance of the elements to be compared +static void oddEvenMerge(SortArray& A, unsigned int lo, unsigned int n, unsigned int r, + unsigned int sort_depth, unsigned int merge_depth) +{ + unsigned int m = r * 2; + if (m < n) + { + // even subsequence + oddEvenMerge(A, lo, n, m, sort_depth, merge_depth+1); + // odd subsequence + oddEvenMerge(A, lo + r, n, m, sort_depth, merge_depth+1); + + for (unsigned int i = lo + r; i + r < lo + n; i += m) + compare(A, i, i + r, sort_depth, merge_depth); + } + else { + compare(A, lo, lo + r, sort_depth, merge_depth); + } +} + +// sorts a piece of length n of the array starting at position lo +static void oddEvenMergeSort(SortArray& A, unsigned int lo, unsigned int n, + unsigned int sort_depth) +{ + if (n > 1) + { + unsigned int m = n / 2; + oddEvenMergeSort(A, lo, m, sort_depth+1); + oddEvenMergeSort(A, lo + m, m, sort_depth+1); + oddEvenMerge(A, lo, n, 1, sort_depth, 0); + } +} + +void sort(SortArray& A) +{ + sequence.clear(); + + unsigned int n = largestPowerOfTwoLessThan(A.size()); + if (n != A.size()) n *= 2; + + oddEvenMergeSort(A, 0, n, 0); + std::sort(sequence.begin(), sequence.end()); + replay(A); + sequence.clear(); +} + +} // namespace BatcherSortNetworkNS + +void BatcherSortNetwork(SortArray& A) +{ + BatcherSortNetworkNS::sort(A); +} + +// **************************************************************************** +// *** Smooth Sort + +// from http://en.wikipediA.org/wiki/Smoothsort + +namespace SmoothSortNS { + +static const int LP[] = { + 1, 1, 3, 5, 9, 15, 25, 41, 67, 109, + 177, 287, 465, 753, 1219, 1973, 3193, 5167, 8361, 13529, 21891, + 35421, 57313, 92735, 150049, 242785, 392835, 635621, 1028457, + 1664079, 2692537, 4356617, 7049155, 11405773, 18454929, 29860703, + 48315633, 78176337, 126491971, 204668309, 331160281, 535828591, + 866988873 // the next number is > 31 bits. +}; + +static void sift(SortArray& A, int pshift, int head) +{ + // we do not use Floyd's improvements to the heapsort sift, because we + // are not doing what heapsort does - always moving nodes from near + // the bottom of the tree to the root. + + value_type val = A[head]; + + while (pshift > 1) + { + int rt = head - 1; + int lf = head - 1 - LP[pshift - 2]; + + if (val.cmp(A[lf]) >= 0 && val.cmp(A[rt]) >= 0) + break; + + if (A[lf].cmp(A[rt]) >= 0) { + A.set(head, A[lf]); + head = lf; + pshift -= 1; + } + else { + A.set(head, A[rt]); + head = rt; + pshift -= 2; + } + } + + A.set(head, val); +} + +static void trinkle(SortArray& A, int p, int pshift, int head, bool isTrusty) +{ + value_type val = A[head]; + + while (p != 1) + { + int stepson = head - LP[pshift]; + + if (A[stepson].cmp(val) <= 0) + break; // current node is greater than head. sift. + + // no need to check this if we know the current node is trusty, + // because we just checked the head (which is val, in the first + // iteration) + if (!isTrusty && pshift > 1) { + int rt = head - 1; + int lf = head - 1 - LP[pshift - 2]; + if (A[rt].cmp(A[stepson]) >= 0 || + A[lf].cmp(A[stepson]) >= 0) + break; + } + + A.set(head, A[stepson]); + + head = stepson; + //int trail = Integer.numberOfTrailingZeros(p & ~1); + int trail = __builtin_ctz(p & ~1); + p >>= trail; + pshift += trail; + isTrusty = false; + } + + if (!isTrusty) { + A.set(head, val); + sift(A, pshift, head); + } +} + +void sort(SortArray& A, int lo, int hi) +{ + int head = lo; // the offset of the first element of the prefix into m + + // These variables need a little explaining. If our string of heaps + // is of length 38, then the heaps will be of size 25+9+3+1, which are + // Leonardo numbers 6, 4, 2, 1. + // Turning this into a binary number, we get b01010110 = 0x56. We represent + // this number as a pair of numbers by right-shifting all the zeros and + // storing the mantissa and exponent as "p" and "pshift". + // This is handy, because the exponent is the index into L[] giving the + // size of the rightmost heap, and because we can instantly find out if + // the rightmost two heaps are consecutive Leonardo numbers by checking + // (p&3)==3 + + int p = 1; // the bitmap of the current standard concatenation >> pshift + int pshift = 1; + + while (head < hi) + { + if ((p & 3) == 3) { + // Add 1 by merging the first two blocks into a larger one. + // The next Leonardo number is one bigger. + sift(A, pshift, head); + p >>= 2; + pshift += 2; + } + else { + // adding a new block of length 1 + if (LP[pshift - 1] >= hi - head) { + // this block is its final size. + trinkle(A, p, pshift, head, false); + } else { + // this block will get merged. Just make it trusty. + sift(A, pshift, head); + } + + if (pshift == 1) { + // LP[1] is being used, so we add use LP[0] + p <<= 1; + pshift--; + } else { + // shift out to position 1, add LP[1] + p <<= (pshift - 1); + pshift = 1; + } + } + p |= 1; + head++; + } + + trinkle(A, p, pshift, head, false); + + while (pshift != 1 || p != 1) + { + if (pshift <= 1) { + // block of length 1. No fiddling needed + //int trail = Integer.numberOfTrailingZeros(p & ~1); + int trail = __builtin_ctz(p & ~1); + p >>= trail; + pshift += trail; + } + else { + p <<= 2; + p ^= 7; + pshift -= 2; + + // This block gets broken into three bits. The rightmost bit is a + // block of length 1. The left hand part is split into two, a block + // of length LP[pshift+1] and one of LP[pshift]. Both these two + // are appropriately heapified, but the root nodes are not + // necessarily in order. We therefore semitrinkle both of them + + trinkle(A, p >> 1, pshift + 1, head - LP[pshift] - 1, true); + trinkle(A, p, pshift, head - 1, true); + } + + head--; + } +} + +} // namespace SmoothSortNS + +void SmoothSort(SortArray& A) +{ + return SmoothSortNS::sort(A, 0, A.size()-1); +} + +// **************************************************************************** +// *** Stooge Sort + +void StoogeSort(SortArray& A, int i, int j) +{ + if (A[i] > A[j]) + { + A.swap(i, j); + } + + if (j - i + 1 >= 3) + { + int t = (j - i + 1) / 3; + + A.mark(i, 3); + A.mark(j, 3); + + StoogeSort(A, i, j-t); + StoogeSort(A, i+t, j); + StoogeSort(A, i, j-t); + + A.unmark(i); + A.unmark(j); + } +} + +void StoogeSort(SortArray& A) +{ + StoogeSort(A, 0, A.size()-1); +} + +void BadSort(SortArray& A) +{ + for (size_t i = 0; i < A.size(); i++) + { + size_t shortest = i; + for (size_t j = i; j < A.size(); j++) + { + bool isShortest = true; + for (size_t k = j + 1; k < A.size(); k++) + { + if (A[j] > A[k]) + { + isShortest = false; + break; + } + } + if (isShortest) + { + shortest = j; + break; + } + } + A.swap(i, shortest); + } +} + +// **************************************************************************** +// *** Slow Sort + +void SlowSort(SortArray& A, int i, int j) +{ + if (i >= j) return; + + int m = (i + j) / 2; + + SlowSort(A, i, m); + SlowSort(A, m+1, j); + + if (A[m] > A[j]) + A.swap(m, j); + + A.mark(j, 2); + + SlowSort(A, i, j-1); + + A.unmark(j); +} + +void SlowSort(SortArray& A) +{ + SlowSort(A, 0, A.size()-1); +} + +// **************************************************************************** +// *** Cycle Sort + +// Adapted from http://en.wikipedia.org/wiki/Cycle_sort + +void CycleSort(SortArray& vec_arr, ssize_t n) +{ + std::atomic cycleStart{ 0 }, rank{ 0 }; + vec_arr.watch(&cycleStart, 16); + + vec_arr.watch(&rank, 5); + + // Loop through the array to find cycles to rotate. + for (; cycleStart.load() < n - 1; ++cycleStart) + { + value_type& item = vec_arr.get_mutable(cycleStart.load()); + do { + // Find where to put the item. + rank.store(cycleStart.load()); + for (ssize_t i = cycleStart.load() + 1; i < n; ++i) + { + if (vec_arr[i] < item) { rank++; } + } + + // If the item is already there, this is a 1-cycle. + if (rank.load() == cycleStart.load()) { + vec_arr.mark(rank.load(), 2); + break; + } + + // Otherwise, put the item after any duplicates. + while (item == vec_arr[rank.load()]) { rank++; } + + // Put item into right place and colorize + counted_swap(vec_arr.get_mutable(rank.load()), item); + vec_arr.mark(rank.load(), 2); + + // Continue for rest of the cycle. + } while (rank.load() != cycleStart.load()); + } + + vec_arr.unwatch_all(); +} + +void CycleSort(SortArray& A) +{ + CycleSort(A, A.size()); +} + + +// **************************************************************************** +// *** Pairwise Sorting Network (Recursive and Iterative) +/* + Copyright (c) 2021 aphitorite + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), to deal + in the Software without restriction, including without limitation the rights + to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + The above copyright notice and this permission notice shall be included in all + copies or substantial portions of the Software. + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + SOFTWARE. +*/ + +void compSwap(SortArray& A, size_t a, size_t b) +{ + size_t n = A.size(); + if (b < n && A[a] > A[b]) { A.swap(a, b); } +} +void PairwiseMerge(SortArray& A, size_t a, size_t b) +{ + size_t m = (a + b) / 2, m1 = (a + m) / 2, g = m - m1; + for (size_t i = 0; m1 + i < m; ++i) + { + for (size_t j = m1, k = g; k > 0; k >>= 1, j -= k - (i & k)) + { + compSwap(A, j + i, j + i + k); + } + } + if (b - a > 4) { PairwiseMerge(A, m, b); } +} + +void PairwiseMergeSort(SortArray& A, size_t a, size_t b) +{ + size_t m = (a + b) / 2; + for (size_t i = a, j = m; i < m; ++i, ++j) + { + compSwap(A, i, j); + } + if (b - a > 2) + { + PairwiseMergeSort(A, a, m); + PairwiseMergeSort(A, m, b); + PairwiseMerge(A, a, b); + } +} + +void PairwiseSort(SortArray& A) +{ + size_t end = A.size(); + size_t n = 1; + for (; n < end; n <<= 1) {} + PairwiseMergeSort(A, 0, n); +} + +void PairwiseIterativeSort(SortArray& A) +{ + size_t end = A.size(), n = 1; + for (; n < end; n <<= 1) {} + for (size_t k = n >> 1; k > 0; k >>= 1) + { + for (size_t j = 0; j < end; j += k << 1) + { + for (size_t i = 0; i < k; ++i) + { + compSwap(A, j + i, j + i + k); + } + } + } + for (size_t k = 2; k < n; k <<= 1) + { + for (size_t j = k >> 1; j > 0; j >>= 1) + { + for (size_t i = 0; i < end; i += k << 1) + { + for (size_t m = j; m < ((k - j) << 1); m += j << 1) + { + for (size_t o = 0; o < j; ++o) + { + compSwap(A, i + m + o, i + m + j + o); + } + } + } + } + } +} + +// **************************************************************************** +// *** American Flag Sort +// Adapted from https://en.wikipedia.org/wiki/American_flag_sort +/* + Copyright 2017 Justin Wetherell -void BatcherSortNetwork(SortArray& A) + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + +size_t getDigit(size_t num, size_t divisor, size_t buckets) { return (num / divisor) % buckets; } + +int getMaxNumberOfDigits(SortArray& A, size_t len, int buckets) { - BatcherSortNetworkNS::sort(A); + int max = std::numeric_limits::min(); + int temp = 0; + for (size_t i = 0; i < len; ++i) + { + int ele = A[i].get(); + temp = static_cast(log(ele) / log(buckets)) + 1; + if (temp > max) { max = temp; } + } + return max; +} + +void sort(SortArray& A, size_t start, size_t len, size_t divisor) +{ + size_t buckets = 128; + std::vector count(buckets, 0); + std::vector offset(buckets, 0); + size_t digit = 0; + + for (size_t i = start; i < len; ++i) + { + int d = A[i].get(); + size_t l = d; + digit = getDigit(l, divisor, buckets); + count[digit] = count[digit] + 1; + } + int s = start; + offset[0] = s; + + for (size_t i = 1; i < buckets; ++i) + { + offset[i] = count[i - 1] + offset[i - 1]; + } + + for (size_t b = 0; b < buckets; ++b) + { + while (count[b] > 0) + { + size_t origin = offset[b], from = origin; + int num = A[from]; + do + { + size_t m = num; + digit = getDigit(m, divisor, buckets); + size_t to = offset[digit]; + + offset[digit] = offset[digit] + 1; + count[digit] = count[digit] - 1; + + int temp = A[to].get(); + A.set(to, ArrayItem(num)); + + num = temp; + from = to; + } + while (from != origin); + } + } + if (divisor > 1) + { + for (size_t i = 0; i < buckets; ++i) + { + size_t begin = 0; + if (i > 0) { begin = offset[i - 1]; } + else { begin = start; } + size_t last = offset[i]; + + if (last - begin > 1) + { + sort(A, begin, last, divisor / buckets); + } + } + } +} + +void AmericanFlagSort(SortArray& A) +{ + size_t len = A.size(); + int buckets = 128, max = 1; + int numberOfDigits = getMaxNumberOfDigits(A, len, buckets); // Max number of digits + + for (int i = 0; i < numberOfDigits - 1; ++i) { max *= buckets; } + + size_t m = max; + sort(A, 0, len, m); } + // **************************************************************************** -// *** Smooth Sort +// *** Strand Sort -// from http://en.wikipediA.org/wiki/Smoothsort +/* + MIT License -namespace SmoothSortNS { + Copyright (c) 2020-2021 aphitorite -static const int LP[] = { - 1, 1, 3, 5, 9, 15, 25, 41, 67, 109, - 177, 287, 465, 753, 1219, 1973, 3193, 5167, 8361, 13529, 21891, - 35421, 57313, 92735, 150049, 242785, 392835, 635621, 1028457, - 1664079, 2692537, 4356617, 7049155, 11405773, 18454929, 29860703, - 48315633, 78176337, 126491971, 204668309, 331160281, 535828591, - 866988873 // the next number is > 31 bits. -}; + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), to deal + in the Software without restriction, including without limitation the rights + to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: -static void sift(SortArray& A, int pshift, int head) + The above copyright notice and this permission notice shall be included in all + copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + SOFTWARE. +*/ + +void mergeTo(SortArray& A, std::vector& subList, size_t a, size_t m, size_t b) { - // we do not use Floyd's improvements to the heapsort sift, because we - // are not doing what heapsort does - always moving nodes from near - // the bottom of the tree to the root. + size_t i = 0, s = m - a; + while (i < s && m < b) + { + if (subList[i] < A[m]) + { + A.set(a++, subList[i++]); + } + else + { + A.set(a++, A[m++]); + } + } + while (i < s) + { + A.set(a++, subList[i++]); + } +} - value_type val = A[head]; +void StrandSort(SortArray& A) +{ + size_t n = A.size(), j = n, k = j; + std::vector subList(n); + while (j > 0) + { + subList[0] = A[0]; + --k; + for (size_t i = 0, p = 0, m = 1; m < j; ++m) + { + if (A[m] >= subList[i]) + { + subList[++i] = A[m]; + --k; + } + else + { + A.set(p++, A[m]); + } + } + mergeTo(A, subList, k, j, n); + j = k; + } +} - while (pshift > 1) + +// **************************************************************************** +// *** New Shuffle Merge Sort +/* + MIT License + + Copyright (c) 2021 EmeraldBlock + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), to deal + in the Software without restriction, including without limitation the rights + to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in all + copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + SOFTWARE. +*/ + +/* + * Implements https://www.sciencedirect.com/science/article/pii/S1877050910005478. + * + * The shuffle algorithm is at https://arxiv.org/abs/0805.1598. + * Note that the unshuffle algorithm is not the shuffle algorithm in reverse, + * but rather, it is a variation of the shuffle algorithm. + * + * See also a proof of the time complexity at https://arxiv.org/abs/1508.00292. + * The implementation is based on the pseudocode found in this. +*/ + +void rotateEqual(SortArray& A, size_t a, size_t b, size_t size) +{ + for (size_t i = 0; i < size; ++i) { - int rt = head - 1; - int lf = head - 1 - LP[pshift - 2]; + A.swap(a + i, b + i); + } +} - if (val.cmp(A[lf]) >= 0 && val.cmp(A[rt]) >= 0) +void rotateArray(SortArray& A, size_t mid, size_t a, size_t b) +{ + while (a > 0 && b > 0) + { + if (a > b) + { + rotateEqual(A, mid - b, mid, b); + mid -= b; + a -= b; + } + else + { + rotateEqual(A, mid - a, mid, a); + mid += a; + b -= a; + } + } +} + +void shuffleEasy(SortArray& A, size_t start, size_t size) +{ + for (size_t i = 1; i < size; i *= 3) + { + value_type val = A[start + i - 1]; + for (size_t j = i * 2 % size; j != i; j = j * 2 % size) + { + value_type nval = A[start + j - 1]; + A.set(start + j - 1, val); + val = nval; + } + A.set(start + i - 1, val); + } +} + +void shuffleArray(SortArray& A, size_t start, size_t end) +{ + while (end - start > 1) + { + size_t n = (end - start) / 2, l = 1; + while (l * 3 - 1 <= 2 * n) { l *= 3; } + size_t m = (l - 1) / 2; + rotateArray(A, start + n, n - m, m); + shuffleEasy(A, start, l); + start += l - 1; + } +} + +void rotateShuffledEqual(SortArray& A, size_t a, size_t b, size_t size) +{ + for (size_t i = 0; i < size; i += 2) + { + A.swap(a + i, b + i); + } +} + +void rotateShuffled(SortArray& A, size_t mid, size_t a, size_t b) +{ + while (a > 0 && b > 0) + { + if (a > b) + { + rotateShuffledEqual(A, mid - b, mid, b); + mid -= b; + a -= b; + } + else + { + rotateShuffledEqual(A, mid - a, mid, a); + mid += a; + b -= a; + } + } +} + +void rotateShuffledOuter(SortArray& A, size_t mid, size_t a, size_t b) +{ + if (a > b) + { + rotateShuffledEqual(A, mid - b, mid + 1, b); + mid -= b; + a -= b; + rotateShuffled(A, mid, a, b); + } + else + { + rotateShuffledEqual(A, mid - a, mid + 1, a); + mid += a + 1; + b -= a; + rotateShuffled(A, mid, a, b); + } +} + +void unshuffleEasy(SortArray& A, size_t start, size_t size) +{ + for (size_t i = 1; i < size; i *= 3) + { + size_t prev = i; + value_type val = A[start + i - 1]; + for (size_t j = i * 2 % size; j != i; j = j * 2 % size) { + A.set(start + prev - 1, A[start + j - 1]); + prev = j; + } + A.set(start + prev - 1, val); + } +} + +void unshuffle(SortArray& A, size_t start, size_t end) +{ + while (end - start > 1) + { + size_t n = (end - start) / 2, l = 1; + while (l * 3 - 1 <= 2 * n) { l *= 3; } + size_t m = (l - 1) / 2; + + rotateShuffledOuter(A, start + 2 * m, 2 * m, 2 * n - 2 * m); + unshuffleEasy(A, start, l); + start += l - 1; + } +} + +void mergeUp(SortArray& A, size_t start, size_t end, bool type) +{ + size_t i = start, j = i + 1; + while (j < end) + { + if (A[i] < A[j] || (!type && A[i] == A[j])) + { + ++i; + if (i == j) + { + ++j; + type = !type; + } + } + else if (end - j == 1) + { + rotateArray(A, j, j - i, 1); break; + } + else + { + size_t r = 0; + if (type) + { + while (j + 2 * r < end && A[j + 2 * r] <= A[i]) { ++r; } + } + else + { + while (j + 2 * r < end && A[j + 2 * r] < A[i]) { ++r; } + } + --j; + unshuffle(A, j, j + 2 * r); + rotateArray(A, j, j - i, r); + i += r + 1; + j += 2 * r + 1; + } + } +} - if (A[lf].cmp(A[rt]) >= 0) { - A.set(head, A[lf]); - head = lf; - pshift -= 1; +void mergeArray(SortArray& A, size_t start, size_t mid, size_t end) +{ + if (mid - start <= end - mid) + { + shuffleArray(A, start, end); + mergeUp(A, start, end, true); + } + else + { + shuffleArray(A, start + 1, end); + mergeUp(A, start, end, false); + } +} + +size_t ceilPowerOfTwo(size_t x) +{ + --x; + for (size_t i = 16; i > 0; i >>= 1) { x |= x >> i; } + return ++x; +} + +void sortLarge(SortArray& A, size_t len) +{ + for (size_t subarrayCount = ceilPowerOfTwo(len), wholeI = len / subarrayCount, fracI = len % subarrayCount; subarrayCount > 1; ) + { + for (size_t whole = 0, frac = 0; whole < len; ) + { + size_t start = whole; + whole += wholeI; + frac += fracI; + if (frac >= subarrayCount) + { + ++whole; + frac -= subarrayCount; + } + size_t mid = whole; + whole += wholeI; + frac += fracI; + if (frac >= subarrayCount) + { + ++whole; + frac -= subarrayCount; + } + mergeArray(A, start, mid, whole); } - else { - A.set(head, A[rt]); - head = rt; - pshift -= 2; + subarrayCount >>= 1; + wholeI <<= 1; + if (fracI >= subarrayCount) + { + ++wholeI; + fracI -= subarrayCount; + } + } +} + +void mergeSortArray(SortArray& A, size_t len) +{ + if (len < 1 << 15) + { + for (size_t subarrayCount = ceilPowerOfTwo(len); subarrayCount > 1; subarrayCount >>= 1) + { + for (size_t i = 0; i < subarrayCount; i += 2) + { + mergeArray(A, len * i / subarrayCount, len * (i + 1) / subarrayCount, len * (i + 2) / subarrayCount); + } + } + } + else + { + sortLarge(A, len); + } +} + +void NewShuffleMergeSort(SortArray& A) +{ + size_t len = A.size(); + mergeSortArray(A, len); +} + +// **************************************************************************** +// *** Andrey Astrelin's In-Place Merge Sort + +void sortVector(SortArray& A, size_t a, size_t b) +{ + while (b > 1) + { + size_t k = 0; + for (size_t i = 1; i < b; ++i) + { + if (A[a + k] > A[a + i]) { k = i; } + } + A.swap(a, a + k); + ++a; --b; + } +} + +void aswap(SortArray& A, size_t arr1, size_t arr2, size_t l) +{ + while (l-- > 0) + { + A.swap(arr1, arr2); + ++arr1; ++arr2; + } +} + +int backMerge(SortArray& A, size_t arr1, size_t l1, size_t arr2, size_t l2) +{ + size_t arr0 = arr2 + l1; + for (;;) + { + if (A[arr1] > A[arr2]) + { + A.swap(arr1, arr0); + --arr1; --arr0; + if (--l1 == 0) { return 0; } + } + else + { + A.swap(arr2, arr0); + --arr2; --arr0; + if (--l2 == 0) { break; } + } + } + size_t res = l1; + do + { + A.swap(arr1, arr0); + --arr1; --arr0; + } + while (--l1 != 0); + return res; +} + +void rMerge(SortArray& A, size_t a, size_t l, size_t r) +{ + for (size_t i = 0; i < l; i += r) + { + size_t q = i; + for (size_t j = i + r; j < l; j += r) + { + if (A[a + q] > A[a + j]) { q = j; } + } + if (q != i) { aswap(A, a + i, a + q, r); } + if (i != 0) + { + aswap(A, a + l, a + i, r); + backMerge(A, a + (l + r - 1), r, a + (i - 1), r); + } + } +} + +size_t rbnd(size_t len) +{ + len = len / 2; + size_t k = 0; + for (size_t i = 1; i < len; i *= 2) { ++k; } + len /= k; + for (k = 1; k <= len; k *= 2) {} + return k; +} + +void msort(SortArray& A, size_t a, size_t len) +{ + if (len < 12) { sortVector(A, a, len); return; } + size_t r = rbnd(len), lr = (len / r - 1) * r; + for (size_t p = 2; p <= lr; p += 2) + { + if (A[a + (p - 2)] > A[a + (p - 1)]) + { A.swap(a + (p - 2), a + (p - 1)); } + if ((p & 2) != 0) { continue; } + aswap(A, a + (p - 2), a + p, 2); + size_t m = len - p, q = 2; + for (;;) + { + size_t q0 = 2 * q; + if (q0 > m || (p & q0) != 0) { break; } + backMerge(A, a + (p - q - 1), q, a + (p + q - 1), q); + q = q0; + } + backMerge(A, a + (p + q - 1), q, a + (p - q - 1), q); + size_t q1 = q; + q *= 2; + while ((q & p) == 0) + { + q *= 2; + rMerge(A, a + (p - q), q, q1); } } - A.set(head, val); + size_t q1 = 0; + for (size_t q = r; q < lr; q *= 2) + { + if ((lr & q) != 0) + { + q1 += q; + if (q1 != q) + { + rMerge(A, a + (lr - q1), q1, r); + } + } + } + + size_t s = len - lr; + msort(A, a + lr, s); + aswap(A, a, a + lr, s); + s += backMerge(A, a + (s - 1), s, a + (lr - 1), lr - s); + msort(A, a, s); } -static void trinkle(SortArray& A, int p, int pshift, int head, bool isTrusty) +void AndreyMergeSort(SortArray& A) { - value_type val = A[head]; + msort(A, 0, A.size()); +} - while (p != 1) - { - int stepson = head - LP[pshift]; - if (A[stepson].cmp(val) <= 0) - break; // current node is greater than head. sift. +// **************************************************************************** +// *** Proportion Extend Merge Sort - // no need to check this if we know the current node is trusty, - // because we just checked the head (which is val, in the first - // iteration) - if (!isTrusty && pshift > 1) { - int rt = head - 1; - int lf = head - 1 - LP[pshift - 2]; - if (A[rt].cmp(A[stepson]) >= 0 || - A[lf].cmp(A[stepson]) >= 0) - break; - } +/* + MIT License - A.set(head, A[stepson]); + Copyright (c) 2023 aphitorite - head = stepson; - //int trail = Integer.numberOfTrailingZeros(p & ~1); - int trail = __builtin_ctz(p & ~1); - p >>= trail; - pshift += trail; - isTrusty = false; + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), to deal + in the Software without restriction, including without limitation the rights + to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in all + copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + SOFTWARE. +*/ + +void blockSwap(SortArray& A, size_t a, size_t b, size_t s) +{ + while (s-- > 0) + { + A.swap(a, b); + ++a; ++b; } +} - if (!isTrusty) { - A.set(head, val); - sift(A, pshift, head); +size_t partition(SortArray& A, size_t a, size_t b, size_t p) +{ + size_t i = a - 1, j = b; + while (true) + { + do { ++i; } while (i < j && A[i] < A[p]); + do { --j; } while (j >= i && A[j] > A[p]); + if (i < j) { A.swap(i, j); } + else { return i; } } } -void sort(SortArray& A, int lo, int hi) +void mergeFW(SortArray& A, size_t a, size_t m, size_t b, size_t p) { - int head = lo; // the offset of the first element of the prefix into m + size_t pLen = m - a, i = 0, j = m, k = a; + blockSwap(A, a, p, pLen); + while (i < pLen && j < b) + { + if (A[p + i] <= A[j]) { A.swap(k, p + i); ++k; ++i; } + else { A.swap(k, j); ++k; ++j; } + } + while (i < pLen) { A.swap(k, p + i); ++k; ++i; } +} - // These variables need a little explaining. If our string of heaps - // is of length 38, then the heaps will be of size 25+9+3+1, which are - // Leonardo numbers 6, 4, 2, 1. - // Turning this into a binary number, we get b01010110 = 0x56. We represent - // this number as a pair of numbers by right-shifting all the zeros and - // storing the mantissa and exponent as "p" and "pshift". - // This is handy, because the exponent is the index into L[] giving the - // size of the rightmost heap, and because we can instantly find out if - // the rightmost two heaps are consecutive Leonardo numbers by checking - // (p&3)==3 +void mergeBW(SortArray& A, size_t a, size_t m, size_t b, size_t p) +{ + size_t pLen = b - m; + int i = static_cast(pLen - 1), + j = static_cast(m - 1), + k = static_cast(b - 1), + z = static_cast(a); + blockSwap(A, m, p, pLen); + while (i >= 0 && j >= z) + { + if (A[p + i] >= A[j]) { A.swap(k, p + i); --k; --i; } + else { A.swap(k, j); --k; --j; } + } + while (i >= 0) { A.swap(k, p + i); --k; --i; } +} - int p = 1; // the bitmap of the current standard concatenation >> pshift - int pshift = 1; +void smartMerge(SortArray& A, size_t a, size_t m, size_t b, size_t p) +{ + if (m - a < b - m) { mergeFW(A, a, m, b, p); } + else { mergeBW(A, a, m, b, p); } +} - while (head < hi) +void mergeTo(SortArray& A, size_t a, size_t m, size_t b, size_t p) +{ + size_t i = a, j = m; + while (i < m && j < b) { - if ((p & 3) == 3) { - // Add 1 by merging the first two blocks into a larger one. - // The next Leonardo number is one bigger. - sift(A, pshift, head); - p >>= 2; - pshift += 2; - } - else { - // adding a new block of length 1 - if (LP[pshift - 1] >= hi - head) { - // this block is its final size. - trinkle(A, p, pshift, head, false); - } else { - // this block will get merged. Just make it trusty. - sift(A, pshift, head); - } - - if (pshift == 1) { - // LP[1] is being used, so we add use LP[0] - p <<= 1; - pshift--; - } else { - // shift out to position 1, add LP[1] - p <<= (pshift - 1); - pshift = 1; - } - } - p |= 1; - head++; + if (A[i] <= A[j]) { A.swap(p, i); ++p; ++i; } + else { A.swap(p, j); ++p; ++j; } } + while (i < m) { A.swap(p, i); ++p; ++i; } + while (j < b) { A.swap(p, j); ++p; ++j; } +} - trinkle(A, p, pshift, head, false); +void pingPongMerge(SortArray& A, size_t a, size_t m1, size_t m, size_t m2, size_t b, size_t p) +{ + size_t p1 = p + m - a, pEnd = p + b - a; + mergeTo(A, a, m1, m, p); + mergeTo(A, m, m2, b, p1); + mergeTo(A, p, p1, pEnd, a); +} - while (pshift != 1 || p != 1) +void mergeSort(SortArray& A, size_t a, size_t b, size_t p) +{ + const size_t min_insert = 8; + size_t n = b - a, j = n; + for (; (j + 3) / 4 >= min_insert; j = (j + 3) / 4) {} + for (size_t i = a; i < b; i += j) { - if (pshift <= 1) { - // block of length 1. No fiddling needed - //int trail = Integer.numberOfTrailingZeros(p & ~1); - int trail = __builtin_ctz(p & ~1); - p >>= trail; - pshift += trail; - } - else { - p <<= 2; - p ^= 7; - pshift -= 2; - - // This block gets broken into three bits. The rightmost bit is a - // block of length 1. The left hand part is split into two, a block - // of length LP[pshift+1] and one of LP[pshift]. Both these two - // are appropriately heapified, but the root nodes are not - // necessarily in order. We therefore semitrinkle both of them + BinaryInsertSort(A, i, std::min(b, i + j)); + } + for (size_t i; j < n; j *= 4) + { + for (i = a; i + 2 * j < b; i += 4 * j) + { + pingPongMerge(A, i, i + j, i + 2 * j, std::min(i + 3 * j, b), std::min(i + 4 * j, b), p); - trinkle(A, p >> 1, pshift + 1, head - LP[pshift] - 1, true); - trinkle(A, p, pshift, head - 1, true); } - - head--; + if (i + j < b) { mergeBW(A, i, i + j, b, p); } } } -} // namespace SmoothSortNS - -void SmoothSort(SortArray& A) +void smartMergeSort(SortArray& A, size_t a, size_t b, size_t p, size_t pb) { - return SmoothSortNS::sort(A, 0, A.size()-1); + if (b - a <= pb - p) { mergeSort(A, a, b, p); return; } + size_t m = (a + b) >> 1; + mergeSort(A, a, m, p); + mergeSort(A, m, b, p); + mergeFW(A, a, m, b, p); } -// **************************************************************************** -// *** Stooge Sort - -void StoogeSort(SortArray& A, int i, int j) +void peSort(SortArray& A, size_t a, size_t m, size_t b) { - if (A[i] > A[j]) + size_t n = b - a; + const size_t min_insert = 8; + if (n < 4 * min_insert) { BinaryInsertSort(A, a, b); return; } + if (m - a <= n / 3) { - A.swap(i, j); + size_t t = (n + 2) / 3; + smartMergeSort(A, m, b - t, b - t, b); + smartMerge(A, a, m, b - t, b - t); + m = b - t; } - - if (j - i + 1 >= 3) + size_t m1 = (a + m) >> 1, m2 = partition(A, m, b, m1); + size_t i = m, j = m2; + while (i > m1) { --i; --j; A.swap(i, j); } + m = m2 - (m - m1); + if (m - m1 < b - m2) { - int t = (j - i + 1) / 3; - - A.mark(i, 3); - A.mark(j, 3); - - StoogeSort(A, i, j-t); - StoogeSort(A, i+t, j); - StoogeSort(A, i, j-t); - - A.unmark(i); - A.unmark(j); + mergeSort(A, m1, m, m2); + smartMerge(A, a, m1, m, m2); + peSort(A, m + 1, m2, b); + } + else + { + mergeSort(A, m2, b, m1); + smartMerge(A, m + 1, m2, b, m1); + peSort(A, a, m1, m); } } -void StoogeSort(SortArray& A) +void ProportionMergeSort(SortArray& A) { - StoogeSort(A, 0, A.size()-1); + peSort(A, 0, 0, A.size()); } - + // **************************************************************************** -// *** Slow Sort +// *** Buffer Partition Merge Sort -void SlowSort(SortArray& A, int i, int j) +/* + * + MIT License + + Copyright (c) 2021 yuji, implemented by aphitorite + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), to deal + in the Software without restriction, including without limitation the rights + to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in all + copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + SOFTWARE. +* +*/ + +void shiftBW2(SortArray& A, int a, int m, int b) { - if (i >= j) return; - - int m = (i + j) / 2; - - SlowSort(A, i, m); - SlowSort(A, m+1, j); - - if (A[m] > A[j]) - A.swap(m, j); + while (m > a) + { + --b; --m; + A.swap(static_cast(b), static_cast(m)); + } +} - A.mark(j, 2); +void inPlaceMerge(SortArray& A, int a, int m, int b) +{ + int i = a, j = m, k = 0; + while (i < j && j < b) + { + if (A[i] > A[j]) + { + k = j; ++k; + while (k < b && A[i] > A[k]) { ++k; } + rotate(A, static_cast(i), static_cast(j), static_cast(k)); + i += k - j; + j = k; + } + else { ++i; } + } +} - SlowSort(A, i, j-1); +void medianOfThree(SortArray& A, int a, int b) +{ + int m = a + (b - 1 - a) / 2; + if (A[a] > A[m]) { A.swap(static_cast(a), static_cast(m)); } + if (A[m] > A[b - 1]) + { + A.swap(static_cast(m), static_cast(b - 1)); + if (A[a] > A[m]) { return; } + } + A.swap(static_cast(a), static_cast(m)); +} - A.unmark(j); +void medianofMedians(SortArray& A, int a, int b, int s) +{ + int end = b, start = a, i, j; + bool ad = true; + while (end - start > 1) + { + j = start; + for (i = start; i + 2 * s <= end; i += s) + { + InsertSort(A, static_cast(i), static_cast(i + s)); + A.swap(static_cast(j), static_cast(i + s / 2)); + ++j; + } + if (i < end) + { + InsertSort(A, static_cast(i), static_cast(end)); + int val = 0; + if (ad) { val = 1; } + A.swap(static_cast(j), static_cast(i + (end - val - i) / 2)); + if ((end - i) % 2 == 0) { ad = !ad; } + ++j; + } + end = j; + } } -void SlowSort(SortArray& A) +int partition(SortArray& A, int a, int b) { - SlowSort(A, 0, A.size()-1); + int i = a, j = b; + while (true) + { + do { ++i; } + while (i < j && A[i] > A[a]); + do { --j; } + while (j >= i && A[j] < A[a]); + if (i < j) { A.swap(static_cast(i), static_cast(j)); } + else { return j; } + } } -// **************************************************************************** -// *** Cycle Sort +int quickSelect(SortArray& A, int a, int b, int m) +{ + bool badPartition = false, mom = false; + int m1 = (m + b + 1) / 2; + while (true) + { + if (badPartition) + { + medianofMedians(A, a, b, 5); + mom = true; + } + else { medianOfThree(A, a, b); } + int p = partition(A, a, b); + A.swap(static_cast(a), static_cast(p)); + int l = std::max(1, p - a), r = std::max(1, b - (p + 1)); + badPartition = !mom && (l / r >= 16 || r / l >= 16); + if (p >= m && p < m1) { return p; } + else if (p < m) { a = p + 1; } + else { b = p; } + } +} -// Adapted from http://en.wikipedia.org/wiki/Cycle_sort +void mergeVector(SortArray& A, int a, int m, int b, int p) +{ + int i = a, j = m; + while (i < m && j < b) + { + if (A[i] <= A[j]) + { + A.swap(static_cast(p), static_cast(i)); + ++p; ++i; + } + else + { + A.swap(static_cast(p), static_cast(j)); + ++p; ++j; + } + } + while (i < m) + { + A.swap(static_cast(p), static_cast(i)); + ++p; ++i; + } + while (j < b) + { + A.swap(static_cast(p), static_cast(j)); + ++p; ++j; + } +} -void CycleSort(SortArray& array, ssize_t n) +int mergeFW2(SortArray& A, int p, int a, int m, int b) { - volatile ssize_t cycleStart = 0; - array.watch(&cycleStart, 16); + int i = a, j = m; + while (i < m && j < b) + { + if (A[i] <= A[j]) + { + A.swap(static_cast(p), static_cast(i)); + ++p; ++i; + } + else + { + A.swap(static_cast(p), static_cast(j)); + ++p; ++j; + } + } + if (i < m) { return i; } + else { return j; } +} - volatile ssize_t rank = 0; - array.watch(&rank, 3); +int getMinLevel(int n) +{ + while (n >= 32) { n = (n + 3) / 4; } + return n; +} - // Loop through the array to find cycles to rotate. - for (cycleStart = 0; cycleStart < n - 1; ++cycleStart) +void mergeSort2(SortArray& A, int a, int b, int p) +{ + int len = b - a; + if (len < 2) { return; } + int i, pos, j = getMinLevel(len); + for (i = a; i + j <= b; i += j) { - value_type& item = array.get_mutable(cycleStart); - - do { - // Find where to put the item. - rank = cycleStart; - for (ssize_t i = cycleStart + 1; i < n; ++i) + BinaryInsertSort(A, static_cast(i), static_cast(i + j)); + } + BinaryInsertSort(A, static_cast(i), static_cast(b)); + while (j < len) + { + pos = p; + for (i = a; i + 2 * j <= b; i += 2 * j, pos += 2 * j) + { mergeVector(A, i, i + j, i + 2 * j, pos); } + if (i + j < b) { mergeVector(A, i, i + j, b, pos); } + else + { + while (i < b) { - if (array[i] < item) - rank++; + A.swap(static_cast(i), static_cast(pos)); + ++i; ++pos; } - - // If the item is already there, this is a 1-cycle. - if (rank == cycleStart) { - array.mark(rank, 2); - break; + } + j *= 2; + pos = a; + for (i = p; i + 2 * j <= p + len; i += 2 * j, pos += 2 * j) + { mergeVector(A, i, i + j, i + 2 * j, pos); } + if (i + j < p + len) { mergeVector(A, i, i + j, p + len, pos); } + else + { + while (i < p + len) + { + A.swap(static_cast(i), static_cast(pos)); + ++i; ++pos; } - - // Otherwise, put the item after any duplicates. - while (item == array[rank]) - rank++; - - // Put item into right place and colorize - std::swap(array.get_mutable(rank), item); - array.mark(rank, 2); - - // Continue for rest of the cycle. } - while (rank != cycleStart); + j *= 2; } - - array.unwatch_all(); } -void CycleSort(SortArray& A) +void sortArray(SortArray& A, int a, int b) { - CycleSort(A, A.size()); + int minLvl = static_cast(sqrt(b - a)); + int m = (a + b + 1) / 2; + mergeSort2(A, m, b, a); + while (m - a > minLvl) + { + int m1 = (a + m + 1) / 2; + m1 = quickSelect(A, a, m, m1); + mergeSort2(A, m1, m, a); + int bSize = m1 - a; + int m2 = std::min(m1 + bSize, b); + m1 = mergeFW2(A, a, m1, m, m2); + while (m1 < m) + { + shiftBW2(A, m1, m, m2); + m1 = m2 - (m - m1); + a = m1 - bSize; + m = m2; + if (m == b) { break; } + m2 = std::min(m2 + bSize, b); + m1 = mergeFW2(A, a, m1, m, m2); + } + m = m1; + a = m1 - bSize; + } + BinaryInsertSort(A, static_cast(a), static_cast(m)); + inPlaceMerge(A, a, m, b); } -// **************************************************************************** +void BufferPartitionMergeSort(SortArray& A) +{ + sortArray(A, 0, A.size()); +} \ No newline at end of file diff --git a/src/SortAlgo.h b/src/SortAlgo.h index 3d5636243..dcc9bdd06 100644 --- a/src/SortAlgo.h +++ b/src/SortAlgo.h @@ -53,15 +53,27 @@ extern const struct AlgoEntry* g_algolist_end; // *** Sorting Algorithms void SelectionSort(class SortArray& a); +void DoubleSelectionSort(class SortArray& a); +void SandpaperSort(class SortArray& a); +void DoubleSandpaperSort(class SortArray& a); void InsertionSort(class SortArray& a); void BinaryInsertionSort(class SortArray& a); +void BinaryInsertSort(class SortArray& a, size_t start, size_t end); void MergeSort(class SortArray& a); void MergeSortIterative(class SortArray& a); +void PairwiseSort(class SortArray& a); +void PairwiseIterativeSort(class SortArray& a); +void WeaveMergeSort(class SortArray& a); +void StrandSort(class SortArray& a); +void NewShuffleMergeSort(class SortArray& a); +void AndreyMergeSort(class SortArray& a); +void ProportionMergeSort(class SortArray& a); +void BufferPartitionMergeSort(class SortArray& a); wxArrayString QuickSortPivotText(); -enum QuickSortPivotType { PIVOT_FIRST, PIVOT_LAST, PIVOT_MID, PIVOT_RANDOM, PIVOT_MEDIAN3 }; +enum QuickSortPivotType { PIVOT_FIRST, PIVOT_LAST, PIVOT_MID, PIVOT_RANDOM, PIVOT_MEDIAN3, PIVOT_MEDIAN3RANDOM, PIVOT_MEDIAN5, PIVOT_MEDIAN5RANDOM, PIVOT_MEDIAN7, PIVOT_MEDIAN7RANDOM, PIVOT_NINTHER, PIVOT_RANDOMNINTHER, PIVOT_MEDIAN9, PIVOT_MEDIAN9RANDOM, PIVOT_MEDIAN11, PIVOT_MEDIAN11RANDOM, PIVOT_MEDIAN15, PIVOT_MEDIAN21, PIVOT_THREENINTHER }; extern QuickSortPivotType g_quicksort_pivot; void QuickSortLR(class SortArray& a); @@ -69,12 +81,21 @@ void QuickSortLL(class SortArray& a); void QuickSortTernaryLR(class SortArray& a); void QuickSortTernaryLL(class SortArray& a); void QuickSortDualPivot(class SortArray& a); +void QuickLibrarySort(SortArray& a); void BubbleSort(class SortArray& a); +void OptimizedBubbleSort(class SortArray& a); void CocktailShakerSort(class SortArray& a); +void DualCocktailShakerSort(class SortArray& a); void CombSort(class SortArray& a); void GnomeSort(class SortArray& a); +void OptimizedGnomeSort(class SortArray& a); void OddEvenSort(class SortArray& a); +void TargetedBubbleSort(class SortArray& a); +void CircleSort(class SortArray& a); +void CircleSort2(class SortArray& a); +void IntroCircleSort(class SortArray& a); +void IntroIteCircleSort(class SortArray& a); void ShellSort(SortArray& a); void HeapSort(class SortArray& a); @@ -84,8 +105,12 @@ void BitonicSort(SortArray& a); void BitonicSortNetwork(SortArray& a); void BatcherSortNetwork(SortArray& a); +void InPlaceRadixSortLSD(class SortArray& a); void RadixSortLSD(class SortArray& a); void RadixSortMSD(class SortArray& a); +void RotateRadixSortLSD(class SortArray& a); +void RotateRadixSortMSD(class SortArray& a); +void AmericanFlagSort(class SortArray& a); void StlSort(class SortArray& a); void StlStableSort(class SortArray& a); @@ -93,11 +118,21 @@ void StlHeapSort(class SortArray& a); void TimSort(class SortArray& a); void WikiSort(class SortArray& a); +void GrailSort(class SortArray& a); +void AuxGrailSort(class SortArray& a); +void PDQSort(class SortArray& a); +void PDQSortBranchless(class SortArray& a); +void BadSort(class SortArray& a); void BogoSort(class SortArray& a); void BozoSort(class SortArray& a); void StoogeSort(class SortArray& a); void SlowSort(class SortArray& a); +void PancakeSort(class SortArray& a); +void OptimizedPancakeSort(class SortArray& a); +void AdjacencyPancakeSort(class SortArray& a); +void BeadSort(class SortArray& a); +void GravitySort(class SortArray& a); void CycleSort(class SortArray& a); @@ -106,80 +141,76 @@ void CycleSort(class SortArray& a); // iterator based on http://zotu.blogspot.de/2010/01/creating-random-access-iterator.html -class MyIterator : public std::iterator -{ +class MyIterator { protected: - SortArray* m_array; - size_t m_pos; + SortArray* m_array; + size_t m_pos; public: - typedef std::iterator base_type; - - typedef std::random_access_iterator_tag iterator_category; + using iterator_category = std::random_access_iterator_tag; + using value_type = ArrayItem; + using difference_type = std::ptrdiff_t; + using reference = ArrayItem&; + using pointer = ArrayItem*; - typedef base_type::value_type value_type; - typedef base_type::difference_type difference_type; - typedef base_type::reference reference; - typedef base_type::pointer pointer; - - MyIterator() : m_array(NULL), m_pos(0) {} + MyIterator() : m_array(nullptr), m_pos(0) {} MyIterator(SortArray* A, size_t p) : m_array(A), m_pos(p) {} MyIterator(const MyIterator& r) : m_array(r.m_array), m_pos(r.m_pos) {} - MyIterator& operator=(const MyIterator& r) - { m_array = r.m_array, m_pos = r.m_pos; return *this; } + MyIterator& operator=(const MyIterator& r) + { m_array = r.m_array; m_pos = r.m_pos; return *this; } - MyIterator& operator++() + MyIterator& operator++() { ++m_pos; return *this; } - MyIterator& operator--() + MyIterator& operator--() { --m_pos; return *this; } - MyIterator operator++(int) + MyIterator operator++(int) { return MyIterator(m_array, m_pos++); } - MyIterator operator--(int) + MyIterator operator--(int) { return MyIterator(m_array, m_pos--); } - MyIterator operator+(const difference_type& n) const + MyIterator operator+(difference_type n) const { return MyIterator(m_array, m_pos + n); } - MyIterator& operator+=(const difference_type& n) + MyIterator& operator+=(difference_type n) { m_pos += n; return *this; } - MyIterator operator-(const difference_type& n) const + MyIterator operator-(difference_type n) const { return MyIterator(m_array, m_pos - n); } - MyIterator& operator-=(const difference_type& n) + MyIterator& operator-=(difference_type n) { m_pos -= n; return *this; } - reference operator*() const + reference operator*() const { return m_array->get_mutable(m_pos); } - pointer operator->() const + pointer operator->() const { return &(m_array->get_mutable(m_pos)); } - reference operator[](const difference_type& n) const + reference operator[](difference_type n) const { return m_array->get_mutable(m_pos + n); } - bool operator==(const MyIterator& r) + bool operator==(const MyIterator& r) const { return (m_array == r.m_array) && (m_pos == r.m_pos); } - bool operator!=(const MyIterator& r) + bool operator!=(const MyIterator& r) const { return (m_array != r.m_array) || (m_pos != r.m_pos); } - bool operator<(const MyIterator& r) + bool operator<(const MyIterator& r) const { return (m_array == r.m_array ? (m_pos < r.m_pos) : (m_array < r.m_array)); } - bool operator>(const MyIterator& r) + bool operator>(const MyIterator& r) const { return (m_array == r.m_array ? (m_pos > r.m_pos) : (m_array > r.m_array)); } - bool operator<=(const MyIterator& r) + bool operator<=(const MyIterator& r) const { return (m_array == r.m_array ? (m_pos <= r.m_pos) : (m_array <= r.m_array)); } - bool operator>=(const MyIterator& r) + bool operator>=(const MyIterator& r) const { return (m_array == r.m_array ? (m_pos >= r.m_pos) : (m_array >= r.m_array)); } difference_type operator+(const MyIterator& r2) const @@ -189,5 +220,4 @@ class MyIterator : public std::iterator +#include +#include extern void SoundAccess(size_t i); @@ -35,6 +37,48 @@ size_t g_compare_count = 0; size_t g_access_count = 0; +size_t m_swaps = 0; + +static int scrambled_tail_intensity = 1; +static int scrambled_head_intensity = 1; + +static const std::array perm = { + 151, 160, 137, 91, 90, 15, 131, 13, 201, 95, 96, 53, 194, 233, 7, 225, + 140, 36, 103, 30, 69, 142, 8, 99, 37, 240, 21, 10, 23, 190, 6, 148, + 247, 120, 234, 75, 0, 26, 197, 62, 94, 252, 219, 203, 117, 35, 11, 32, + 57, 177, 33, 88, 237, 149, 56, 87, 174, 20, 125, 136, 171, 168, 68, 175, + 74, 165, 71, 134, 139, 48, 27, 166, 77, 146, 158, 231, 83, 111, 229, 122, + 60, 211, 133, 230, 220, 105, 92, 41, 55, 46, 245, 40, 244, 102, 143, 54, + 65, 25, 63, 161, 1, 216, 80, 73, 209, 76, 132, 187, 208, 89, 18, 169, + 200, 196, 135, 130, 116, 188, 159, 86, 164, 100, 109, 198, 173, 186, 3, 64, + 52, 217, 226, 250, 124, 123, 5, 202, 38, 147, 118, 126, 255, 82, 85, 212, + 207, 206, 59, 227, 47, 16, 58, 17, 182, 189, 28, 42, 223, 183, 170, 213, + 119, 248, 152, 2, 44, 154, 163, 70, 221, 153, 101, 155, 167, 43, 172, 9, + 129, 22, 39, 253, 19, 98, 108, 110, 79, 113, 224, 232, 178, 185, 112, 104, + 218, 246, 97, 228, 251, 34, 242, 193, 238, 210, 144, 12, 191, 179, 162, 241, + 81, 51, 145, 235, 249, 14, 239, 107, 49, 192, 214, 31, 181, 199, 106, 157, + 184, 84, 204, 176, 115, 121, 50, 45, 127, 4, 150, 254, 138, 236, 205, 93, + 222, 114, 67, 29, 24, 72, 243, 141, 128, 195, 78, 66, 215, 61, 156, 180, + 151, 160, 137, 91, 90, 15, 131, 13, 201, 95, 96, 53, 194, 233, 7, 225, + 140, 36, 103, 30, 69, 142, 8, 99, 37, 240, 21, 10, 23, 190, 6, 148, + 247, 120, 234, 75, 0, 26, 197, 62, 94, 252, 219, 203, 117, 35, 11, 32, + 57, 177, 33, 88, 237, 149, 56, 87, 174, 20, 125, 136, 171, 168, 68, 175, + 74, 165, 71, 134, 139, 48, 27, 166, 77, 146, 158, 231, 83, 111, 229, 122, + 60, 211, 133, 230, 220, 105, 92, 41, 55, 46, 245, 40, 244, 102, 143, 54, + 65, 25, 63, 161, 1, 216, 80, 73, 209, 76, 132, 187, 208, 89, 18, 169, + 200, 196, 135, 130, 116, 188, 159, 86, 164, 100, 109, 198, 173, 186, 3, 64, + 52, 217, 226, 250, 124, 123, 5, 202, 38, 147, 118, 126, 255, 82, 85, 212, + 207, 206, 59, 227, 47, 16, 58, 17, 182, 189, 28, 42, 223, 183, 170, 213, + 119, 248, 152, 2, 44, 154, 163, 70, 221, 153, 101, 155, 167, 43, 172, 9, + 129, 22, 39, 253, 19, 98, 108, 110, 79, 113, 224, 232, 178, 185, 112, 104, + 218, 246, 97, 228, 251, 34, 242, 193, 238, 210, 144, 12, 191, 179, 162, 241, + 81, 51, 145, 235, 249, 14, 239, 107, 49, 192, 214, 31, 181, 199, 106, 157, + 184, 84, 204, 176, 115, 121, 50, 45, 127, 4, 150, 254, 138, 236, 205, 93, + 222, 114, 67, 29, 24, 72, 243, 141, 128, 195, 78, 66, 215, 61, 156, 180 +}; + +static const std::array groupSizes = { 32, 31, 30, 29, 28, 27, 26, 25, 24, 23, 22, 21, 20, 19, 18, 17, 16, 15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3 }; + void ArrayItem::OnAccess(const ArrayItem& a) { SoundAccess(a.get_direct()); @@ -53,7 +97,7 @@ void ArrayItem::OnComparison(const ArrayItem& a, const ArrayItem& b) SortArray::SortArray() : m_calc_inversions(false), - m_delay(NULL) + m_delay(nullptr) { } @@ -98,6 +142,7 @@ void SortArray::FinishFill() g_access_count = 0; g_compare_count = 0; m_calc_inversions = true; + m_swaps = 0; RecalcInversions(); } @@ -107,84 +152,566 @@ void SortArray::FillInputlist(wxArrayString& list) list.Add(_("Random Shuffle")); list.Add(_("Ascending")); list.Add(_("Descending")); + list.Add(_("Near Sorted")); + list.Add(_("Scrambled Tail")); + list.Add(_("Scrambled Head")); list.Add(_("Shuffled Cubic")); list.Add(_("Shuffled Quintic")); list.Add(_("Shuffled n-2 Equal")); + list.Add(_("Pipe Organ")); + list.Add(_("Mirrored Organ")); + list.Add(_("Wave")); + list.Add(_("Sawtooth")); + list.Add(_("Reverse Sawtooth")); + list.Add(_("Many Similar")); + list.Add(_("Quicksort Killer")); + list.Add(_("Spike")); + list.Add(_("Ribbon")); + list.Add(_("Max Heapified")); + list.Add(_("Flipped Min Heapified")); + list.Add(_("Bell Curve")); + list.Add(_("Perlin Noise Curve")); + list.Add(_("Blancmange Curve")); } -void SortArray::FillData(unsigned int schema, size_t arraysize) +static void flippedminheapify(std::vector& m_array, int len, int root, int dist) { - if (arraysize == 0) arraysize = 1; - - ResetArray(arraysize); - - if (schema == 0) // Shuffle of [1,n] + while (root <= dist / 2) { - for (size_t i = 0; i < m_array.size(); ++i) - m_array[i] = ArrayItem(i+1); - - std::random_shuffle(m_array.begin(), m_array.end()); + int leaf = 2 * root; + if (leaf < dist && m_array[len - leaf] > m_array[len - leaf - 1]) { ++leaf; } + if (m_array[len - root] > m_array[len - leaf]) + { + ArrayItem temp = m_array[len - root]; + m_array[len - root] = m_array[len - leaf]; + m_array[len - leaf] = temp; + root = leaf; + } + else { break; } } - else if (schema == 1) // Ascending [1,n] +} + +static void heapify(std::vector& m_array, int n, int i) +{ + int largest = i, left = 2 * i + 1, right = 2 * i + 2; + if (left < n && m_array[left] > m_array[largest]) { largest = left; } + if (right < n && m_array[right] > m_array[largest]) { largest = right; } + if (largest != i) { - for (size_t i = 0; i < m_array.size(); ++i) - m_array[i] = ArrayItem(i+1); + ArrayItem temp = m_array[i]; + m_array[i] = m_array[largest]; + m_array[largest] = temp; + heapify(m_array, n, largest); } - else if (schema == 2) // Descending [1,n] +} + +static float gaussian(float x, float mean, float std_dev) +{ + float exponent = -((x - mean) * (x - mean)) / (2 * std_dev * std_dev); + return std::exp(exponent); +} + +static float fade(float t) { + return t * t * t * (t * (t * 6 - 15) + 10); +} + +static float mlerp(float t, float a, float b) { return a + t * (b - a); } + +static float gradient(int hash, float x) { return (hash & 1) == 0 ? x : -x; } + +static double triangleWave(double x) { return abs(x - (int)(x + 0.5)); } + +static double curve(int n, double x) { return triangleWave((1 << n) * x) / (1 << n); } + +static size_t groupCount(size_t size) +{ + if (size <= 1) { return 2; } + size_t divisor = 2, len = groupSizes.size(); + for (size_t i = 0; i < len; ++i) { - for (size_t i = 0; i < m_array.size(); ++i) - m_array[i] = ArrayItem(m_array.size() - i); + size_t cur = groupSizes[i]; + if (cur == size) { continue; } + if (size % cur == 0) { divisor = cur; break; } } - else if (schema == 3) // Cubic skew of [1,n] + return divisor; +} + +static double curveSum(int n, double x) { + double sum = 0; + while (n >= 0) { sum += curve(n, x); --n; } + return sum; +} + +static float returnPerlinNoise(float x) { + int X = static_cast(floor(x)) & 255; + x -= floor(x); + float u = fade(x); + return mlerp(u, gradient(perm[X], x), gradient(perm[X + 1], x - 1)) * 2; +} + +void SortArray::FillData(unsigned int schema, size_t arraysize) +{ + if (arraysize == 0) arraysize = 1; + { ResetArray(arraysize); } + std::random_device rd; + std::mt19937 g(rd()); + switch (schema) { - for (size_t i = 0; i < m_array.size(); ++i) + case 0: // Shuffle of [1,n] { - // normalize to [-1,+1] - double x = (2.0 * (double)i / m_array.size()) - 1.0; - // calculate x^3 - double v = x * x * x; - // normalize to array size - double w = (v + 1.0) / 2.0 * arraysize + 1; - // decrease resolution for more equal values - w /= 3.0; - m_array[i] = ArrayItem(w + 1); + for (size_t i = 0; i < m_array.size(); ++i) { m_array[i] = ArrayItem(i + 1); } + std::shuffle(m_array.begin(), m_array.end(), g); + break; } - - std::random_shuffle(m_array.begin(), m_array.end()); - } - else if (schema == 4) // Quintic skew of [1,n] - { - for (size_t i = 0; i < m_array.size(); ++i) + case 1: // Ascending [1,n] { - // normalize to [-1,+1] - double x = (2.0 * (double)i / m_array.size()) - 1.0; - // calculate x^5 - double v = x * x * x * x * x; - // normalize to array size - double w = (v + 1.0) / 2.0 * arraysize + 1; - // decrease resolution for more equal values - w /= 3.0; - m_array[i] = ArrayItem(w + 1); + for (size_t i = 0; i < m_array.size(); ++i) { m_array[i] = ArrayItem(i + 1); } + break; } - - std::random_shuffle(m_array.begin(), m_array.end()); - } - else if (schema == 5) // shuffled n-2 equal values in [1,n] - { - m_array[0] = ArrayItem(1); - for (size_t i = 1; i < m_array.size()-1; ++i) + case 2: // Descending { - m_array[i] = ArrayItem( arraysize / 2 + 1 ); + for (size_t i = 0; i < m_array.size(); ++i) { m_array[i] = ArrayItem(m_array.size() - i); } + break; } - m_array[m_array.size()-1] = ArrayItem(arraysize); - - std::random_shuffle(m_array.begin(), m_array.end()); - } - else // fallback - { - return FillData(0, arraysize); + case 3: // Near Sorted + { + for (size_t i = 0; i < m_array.size(); ++i) { m_array[i] = ArrayItem(i + 1); } + ArrayItem temp1 = m_array[0]; + m_array[0] = m_array[m_array.size() - 1]; + m_array[m_array.size() - 1] = temp1; + break; + } + case 4: // Scrambled Tail + { + std::vector::iterator it1 = m_array.begin(); + if (scrambled_tail_intensity > 3 || scrambled_tail_intensity <= 0) { scrambled_tail_intensity = 1; } + switch (scrambled_tail_intensity) + { + case 1: + { + size_t half = m_array.size() / 2; + for (size_t i = 0; i < m_array.size(); ++i) + { + m_array[i] = ArrayItem(i + 1); + if (i <= half + (half / 2)) { ++it1; } + } + break; + } + case 2: + { + for (size_t i = 0; i < m_array.size(); ++i) + { + m_array[i] = ArrayItem(i + 1); + if (i <= (m_array.size() / 2) - 1) { ++it1; } + } + break; + } + case 3: + { + for (size_t i = 0; i < m_array.size(); ++i) + { + m_array[i] = ArrayItem(i + 1); + if (i <= (m_array.size() / 4)) { ++it1; } + } + break; + } + } + ++scrambled_tail_intensity; + std::shuffle(it1, m_array.end(), g); + break; + } + case 5: // Scrambled Head + { + std::vector::iterator it = m_array.begin(); + if (scrambled_head_intensity > 3 || scrambled_head_intensity <= 0) { scrambled_head_intensity = 1; } + switch (scrambled_head_intensity) + { + case 1: + { + for (size_t i = 0; i < m_array.size(); ++i) + { + m_array[i] = ArrayItem(i + 1); + if (i <= (m_array.size() / 4)) { ++it; } + } + break; + } + case 2: + { + for (size_t i = 0; i < m_array.size(); ++i) + { + m_array[i] = ArrayItem(i + 1); + if (i <= (m_array.size() / 2) - 1) { ++it; } + } + break; + } + case 3: + { + size_t half = m_array.size() / 2; + for (size_t i = 0; i < m_array.size(); ++i) + { + m_array[i] = ArrayItem(i + 1); + if (i <= half + (half / 2)) { ++it; } + } + break; + } + } + ++scrambled_head_intensity; + std::shuffle(m_array.begin(), it, g); + break; + } + case 6: // Cubic skew of [1,n] + { + for (size_t i = 0; i < m_array.size(); ++i) + { + // normalize to [-1,+1] + double x = (2.0 * (double)i / m_array.size()) - 1.0; + // calculate x^3 + double v = x * x * x; + // normalize to array size + double w = (v + 1.0) / 2.0 * arraysize + 1; + // decrease resolution for more equal values + w /= 3.0; + m_array[i] = ArrayItem(w + 1); + } + std::shuffle(m_array.begin(), m_array.end(), g); + break; + } + case 7: // Quintic skew of [1,n] + { + for (size_t i = 0; i < m_array.size(); ++i) + { + // normalize to [-1,+1] + double x = (2.0 * (double)i / m_array.size()) - 1.0; + // calculate x^5 + double v = x * x * x * x * x; + // normalize to array size + double w = (v + 1.0) / 2.0 * arraysize + 1; + // decrease resolution for more equal values + w /= 3.0; + m_array[i] = ArrayItem(w + 1); + } + std::shuffle(m_array.begin(), m_array.end(), g); + break; + } + case 8: // shuffled n-2 equal values in [1,n] + { + m_array[0] = ArrayItem(1); + for (size_t i = 1; i < m_array.size() - 1; ++i) { m_array[i] = ArrayItem(arraysize / 2 + 1); } + m_array[m_array.size() - 1] = ArrayItem(arraysize); + std::shuffle(m_array.begin(), m_array.end(), g); + break; + } + case 9: // Pipe organ (1, 1, 2, 2, 1, 1) + { + size_t n = m_array.size(); + if (n % 2 == 0) + { + int val = 1; + for (size_t i = 0; i < n / 2; ++i) + { + m_array[i] = ArrayItem(val); ++val; + } + val = static_cast(n) / 2; + for (size_t i = n / 2; i <= n - 1; ++i) + { + m_array[i] = ArrayItem(val); --val; + } + } + else + { + int val = 1; + for (size_t i = 0; i <= n / 2; ++i) + { + m_array[i] = ArrayItem(val); ++val; + } + val = static_cast(n) / 2; + for (size_t i = (n / 2) + 1; i <= n - 1; ++i) + { + m_array[i] = ArrayItem(val); --val; + } + } + break; + } + case 10: // Mirrored organ (3, 2, 1, 1, 2, 3) + { + size_t n = m_array.size(); + if (n % 2 == 0) + { + int val = static_cast(n) / 2; + for (size_t i = 0; i < n / 2; ++i) + { + m_array[i] = ArrayItem(val); --val; + } + val = 1; + for (size_t i = n / 2; i <= n - 1; ++i) + { + m_array[i] = ArrayItem(val); ++val; + } + } + else + { + int val = static_cast(n) / 2; + for (size_t i = 0; i <= n / 2; ++i) + { + m_array[i] = ArrayItem(val); + if (val >= 2) { --val; } + } + val = 1; + for (size_t i = (n / 2) + 1; i <= n - 1; ++i) + { + m_array[i] = ArrayItem(val); ++val; + } + } + break; + } + case 11: // Wave + { + double n = static_cast(m_array.size()); + double pi = 3.14159265358979323846; + for (size_t i = 0; i < m_array.size(); ++i) + { + double x = i / n * 3 * pi * 5; + double sineVal = sin(x); + int val = std::round((sineVal + 1) * 100); + m_array[i] = ArrayItem(val + 1); + } + break; + } + case 12: // Sawtooth + { + size_t n = m_array.size(), teeth; + if (n % 5 == 0) { teeth = 5; } + else if (n % 4 == 0) { teeth = 4; } + else if (n % 3 == 0) { teeth = 3; } + else { teeth = 2; } + int max = n / teeth; + int count = 1; + for (size_t i = 0; i < n; ++i) + { + if (count > max) { count = 1; } + m_array[i] = ArrayItem(count); + ++count; + } + if (teeth == 2 && m_array[n - 1] == 1) + { + size_t m = n - 1; + while (m_array[m - 1] > m_array[m]) + { + ArrayItem temp5 = m_array[m - 1]; + m_array[m - 1] = m_array[m]; + m_array[m] = temp5; + --m; + } + } + break; + } + case 13: // Reverse Sawtooth + { + size_t n = m_array.size(), teeth; + if (n % 5 == 0) { teeth = 5; } + else if (n % 4 == 0) { teeth = 4; } + else if (n % 3 == 0) { teeth = 3; } + else { teeth = 2; } + int max = n / teeth; + int count = max; + for (size_t i = 0; i < n; ++i) + { + if (count <= 0) { count = max; } + m_array[i] = ArrayItem(count); + --count; + } + if (teeth == 2 && m_array[n - 1] == max) + { + size_t m = n - 1; + while (m_array[m - 1] < m_array[m]) + { + ArrayItem temp4 = m_array[m - 1]; + m_array[m - 1] = m_array[m]; + m_array[m] = temp4; + --m; + } + } + break; + } + case 14: // Many Similar + { + size_t size = m_array.size(), group_count = 0, divisor = groupCount(size); + bool isPrime = false, isDiv2Even = false; + // If the size is an odd number, and groupCount returns 2, then the array size is a prime number + if (divisor == 2 && size % 2 != 0) + { + --size; + isPrime = true; + divisor = groupCount(size); + } + // If the size is an even number, and the divisor returns 2, then decrement the size by 2 to pick a good group count + else if (divisor == 2 && size % 2 == 0) + { + size -= 2; + isDiv2Even = true; + divisor = groupCount(size); + } + group_count = size / divisor; + size_t repeat = 1, i = 0; + if (isPrime == true) + { + m_array[0] = ArrayItem(1); + size = m_array.size(); + ++i; + } + if (isDiv2Even == true) + { + m_array[0] = ArrayItem(1); + m_array[1] = ArrayItem(1); + i += 2; + size = m_array.size(); + } + int val = 1; + for (; i < size; ++i) + { + if (repeat > group_count) { ++val; repeat = 1; } + m_array[i] = ArrayItem(val); + ++repeat; + } + std::shuffle(m_array.begin(), m_array.end(), g); + break; + } + case 15: // Quicksort Killer + { + int currentLen = static_cast(m_array.size()); + for (int i = 0; i < currentLen; ++i) + { + m_array[i] = ArrayItem(i + 1); + } + for (int j = currentLen - currentLen % 2 - 2, i = j - 1; i >= 0; i -= 2, j--) + { + ArrayItem temp3 = m_array[i]; + m_array[i] = m_array[j]; + m_array[j] = temp3; + } + break; + } + case 16: // Spike + { + size_t n = m_array.size(); + int spike, val = 1; + if (n % 10 == 0) { spike = 5; } + else if (n % 8 == 0) { spike = 4; } + else if (n % 6 == 0) { spike = 3; } + else { spike = 2; } + int max = n / (spike * 2); + if (max == 1) { max = n / spike; } + for (size_t i = 0; i < n; ++i) + { + while (val <= max && i < n) + { + m_array[i] = ArrayItem(val); ++val; ++i; + } + if (n % 2 == 0) { val = max; } + else { val = max - 1; } + while (val > 0 && i < n) + { + m_array[i] = ArrayItem(val); --val; + if (val != 0) { ++i; } + } + val = 1; + } + size_t start, end; + start = end = m_array.size() - 1; + while (m_array[start - 1] < m_array[start]) + { + --start; + } + for (; start <= end; ++start) + { + ArrayItem key = m_array[start]; + size_t j = start; + while (j >= 1 && m_array[j - 1] <= key) + { + m_array[j] = m_array[j - 1]; + --j; + } + m_array[j] = key; + } + break; + } + case 17: // Ribbon + { + int min = 1; + int max = static_cast(m_array.size()); + for (size_t i = 0; i < m_array.size(); ++i) + { + if (i % 2 == 0) { m_array[i] = ArrayItem(min); } + else { m_array[i] = ArrayItem(max); } + ++min; --max; + } + break; + } + case 18: // Max Heapified + { + int n = m_array.size(); + for (int i = 0; i < n; ++i) { m_array[i] = ArrayItem(i + 1); } + std::shuffle(m_array.begin(), m_array.end(), g); + for (int i = n / 2 - 1; i >= 0; --i) { heapify(m_array, n, i); } + break; + } + case 19: // Flipped Min Heapified + { + int n = static_cast(m_array.size()); + for (int i = 0; i < n; ++i) { m_array[i] = ArrayItem(i + 1); } + std::shuffle(m_array.begin(), m_array.end(), g); + for (int i = n / 2; i >= 1; --i) + { + flippedminheapify(m_array, n, i, n); + } + break; + } + case 20: // Bell Curve + { + int length = static_cast(m_array.size()); + float mean = length / 2.0f; + float std_dev = length / 6.4f; + for (int i = 0; i < length; ++i) + { + float x = static_cast(i); + float gaussianValue = gaussian(x, mean, std_dev); + int item = static_cast(gaussianValue * 255.0f); + item = std::max(0, std::min(item, 255)); + m_array[i] = ArrayItem(item); + } + break; + } + case 21: // Perlin Noise Curve + { + int n = static_cast(m_array.size()); + for (int i = 0; i < n; ++i) { + int value = -(static_cast(returnPerlinNoise(static_cast(i) / n) * n)); + m_array[i] = ArrayItem(std::min(value, n - 1)); + } + for (int index = 0; index < n - 1; ++index) // Fix zero first element by expanding the left curve + { + if (m_array[index] >= m_array[index + 1]) { break; } + else { m_array[index] = m_array[index + 1]; } + } + break; + } + case 22: // Blancmange Curve + { + int len = static_cast(m_array.size()); + int floorLog = static_cast(log(len) / log(2)); + for (int i = 0; i < len; ++i) + { + int val = static_cast(len * curveSum(floorLog, static_cast(i) / len)); + m_array[i] = ArrayItem(val); + } + int half = len / 2; + // Fix zero first element by expanding the left portion + for (int i = 0; i < half; ++i) { m_array[i] = m_array[i + 1]; } + break; + } + default: + return FillData(0, arraysize); } - FinishFill(); } @@ -201,27 +728,21 @@ bool SortArray::CheckSorted() unmark_all(); // needed because iterator instrumentated algorithms may have changed the array RecalcInversions(); - - ArrayItem prev = get_nocount(0); mark(0); - bool is_sorted = true; for (size_t i = 1; i < size(); ++i) { - ArrayItem key = get_nocount(i); g_compare_count--; // dont count the following comparison - if (!(prev <= key)) { + if (get_nocount(i - 1) > get_nocount(i)) { wxLogError(_T("Result of sorting algorithm is incorrect!")); is_sorted = false; break; } mark(i); - prev = key; } unmark_all(); - return (m_is_sorted = is_sorted); } @@ -267,6 +788,42 @@ void SortArray::RecalcInversions() m_inversions = inversions; } +void SortArray::AddInversions(size_t pos) +{ + if (!m_calc_inversions) { + m_inversions = -1; + return; + } + + const ArrayItem& a = direct(pos); + unsigned int inverses = 0; + for (size_t i = 0; i < pos; ++i) + { + const ArrayItem& b = direct(i); + if (b.greater_direct(a)) { ++inverses; } + } + + m_inversions += inverses; +} + +void SortArray::RemoveInversions(size_t pos) +{ + if (!m_calc_inversions) { + m_inversions = -1; + return; + } + + const ArrayItem& a = direct(pos); + unsigned int inverses = 0; + for (size_t i = 0; i < pos; ++i) + { + const ArrayItem& b = direct(i); + if (b.greater_direct(a)) { ++inverses; } + } + + m_inversions -= inverses; +} + void SortArray::UpdateInversions(size_t i, size_t j) { if (!m_calc_inversions) { @@ -368,10 +925,11 @@ unsigned short SortArray::InWatchList(ssize_t idx) const { for (size_t i = 0; i < m_watch.size(); ++i) { - if (m_watch[i].first == NULL) continue; + if (m_watch[i].first == nullptr) continue; // compare watched value - if (*m_watch[i].first != idx) continue; + // std::atomic_ref(*m_watch[i].first).load() <-- C++20 + if (m_watch[i].first->load() != idx) continue; return m_watch[i].second; } @@ -380,8 +938,7 @@ unsigned short SortArray::InWatchList(ssize_t idx) const int SortArray::GetIndexColor(size_t idx) { - int clr; - + int clr, acl = InAccessList(idx); // select color if (idx == m_access1.index) { @@ -399,8 +956,9 @@ int SortArray::GetIndexColor(size_t idx) { clr = m_mark[idx]; } - else if ( (clr = InAccessList(idx)) >= 0 ) + else if ( acl >= 0 ) { + clr = acl; } else { diff --git a/src/SortArray.h b/src/SortArray.h index 7a1874d24..8d2a007fc 100644 --- a/src/SortArray.h +++ b/src/SortArray.h @@ -24,7 +24,8 @@ #ifndef SORT_ARRAY_HEADER #define SORT_ARRAY_HEADER -#include +#include +#include #include #include @@ -50,6 +51,139 @@ extern size_t g_compare_count; /// globally count the number of array access extern size_t g_access_count; +extern size_t m_swaps; + +// Custom counted swap function +template +constexpr +void counted_swap(T & a, T & b) { + std::swap(a, b); + ++m_swaps; +} + +// Custom counted iter_swap function +template +constexpr +void counted_iter_swap(Iterator a, Iterator b) { + std::iter_swap(a, b); + ++m_swaps; +} + +// Custom counted swap_ranges function +template +constexpr +ForwardIt2 counted_swap_ranges(ForwardIt1 first1, ForwardIt1 last1, ForwardIt2 first2) { + while (first1 != last1) { + counted_iter_swap(first1, first2); + ++first1; + ++first2; + } + return first2; +} + +template +constexpr +ForwardIt counted_rotate(ForwardIt first, ForwardIt middle, ForwardIt last) +{ + if (first == middle) { return last; } + if (middle == last) { return first; } + + ForwardIt write = first; + ForwardIt next_read = first; + + for (ForwardIt read = middle; read != last; ++write, ++read) + { + if (write == next_read) { next_read = read; } + counted_iter_swap(write, read); + } + + counted_rotate(write, next_read, last); + return write; +} + +template +constexpr +void counted_reverse(BidirIt first, BidirIt last) +{ + using iter_cat = typename std::iterator_traits::iterator_category; + if (std::is_base_of::value) + { + if (first == last) { return; } + for (--last; first < last; (void)++first, --last) + { counted_iter_swap(first, last); } + } + else + { + while (first != last && first != --last) + { counted_iter_swap(first++, last); } + } +} + +template > +void counted_make_heap(RandomIt first, RandomIt last, Compare comp = Compare()) +{ + if (last - first < 2) return; + + auto n = std::distance(first, last); + for (auto i = (n / 2) - 1; i >= 0; --i) + { + sift_down(first, last, i, comp); + } +} + +template +void sift_down(RandomIt first, RandomIt last, std::size_t index, Compare comp) +{ + size_t n = std::distance(first, last); + size_t current = index; + + while (true) + { + size_t left_child = 2 * current + 1; + size_t right_child = 2 * current + 2; + size_t largest = current; + + if (left_child < n && comp(first[largest], first[left_child])) + { largest = left_child; } + + if (right_child < n && comp(first[largest], first[right_child])) + { largest = right_child; } + + if (largest == current) { break; } + counted_swap(first[current], first[largest]); + current = largest; + } +} + +template > +void counted_sort_heap(RandomIt first, RandomIt last, Compare comp = Compare()) +{ + while (last - first > 1) + { + counted_iter_swap(first, last - 1); + sift_down(first, last - 1, 0, comp); + --last; + } +} + +template +ForwardIt counted_partition(ForwardIt first, ForwardIt last, UnaryPred p) +{ + first = std::find_if_not(first, last, p); + if (first == last) { return first; } + + for (auto i = std::next(first); i != last; ++i) + { + if (p(*i)) + { + counted_iter_swap(i, first); + ++first; + } + } + + return first; +} + // custom struct for array items, which allows detailed counting of comparisons. class ArrayItem { @@ -78,7 +212,16 @@ class ArrayItem const value_type& get_direct() const { return value; } - // *** comparisons + ArrayItem& operator= (const ArrayItem& other) + { + if (this != &other) + { + this->value = other.value; + } + return *this; + } + + operator int() const { return value; } bool operator== (const ArrayItem& v) const { OnComparison(*this,v); return (value == v.value); } @@ -171,7 +314,7 @@ class SortArray std::vector m_mark; /// custom watched index pointers in the array, set by algorithm - std::vector< std::pair > m_watch; + std::vector< std::pair*,unsigned char> > m_watch; /// flag for sorted array bool m_is_sorted; @@ -198,6 +341,9 @@ class SortArray /// turn on/off calculation of inversions void SetCalcInversions(bool on); + void AddInversions(size_t i); + void RemoveInversions(size_t i); + /// toggle boolean to calculate inversions void ToggleCalcInversions(); @@ -231,7 +377,7 @@ class SortArray void FinishFill(); /// save access to array, forwards to sound system - void SaveAccess(size_t i); + // void SaveAccess(size_t i); /// check if index matches one of the watched pointers short InAccessList(ssize_t idx); @@ -335,6 +481,7 @@ class SortArray void set(size_t i, const ArrayItem& v) { ASSERT(i < m_array.size()); + RemoveInversions(i); { wxMutexLocker lock(m_mutex); @@ -346,7 +493,7 @@ class SortArray m_array[i] = v; } - RecalcInversions(); + AddInversions(i); OnAccess(); } @@ -363,6 +510,7 @@ class SortArray m_access1 = i; m_access2 = j; + ++m_swaps; m_access_list.push_back(i); m_access_list.push_back(j); @@ -373,7 +521,7 @@ class SortArray OnAccess(); std::swap(m_array[i], m_array[j]); OnAccess(); - m_access2 = -1; + m_access2 = -1; } /// Touch an item of the array: set color till next frame is outputted. @@ -425,14 +573,14 @@ class SortArray m_access_list.clear(); } - /// Highly experimental method to _track_ array live indexes. For this, the - /// index must be marked volatile!. - void watch(volatile ssize_t* idxptr, unsigned char color = 2) + // Highly experimental method to _track_ array live indexes. + // The index must be created as std::atomic to ensure thread safety + void watch(std::atomic* idxptr, unsigned char color = 2) { wxMutexLocker lock(m_mutex); ASSERT(lock.IsOk()); - m_watch.push_back( std::make_pair(idxptr,color) ); + m_watch.push_back( std::make_pair(idxptr, color)); } /// Release all tracked live array indexes. diff --git a/src/SortSound.cpp b/src/SortSound.cpp index 00b3c8ec9..dec5fdbdc 100644 --- a/src/SortSound.cpp +++ b/src/SortSound.cpp @@ -232,7 +232,7 @@ void SoundReset() } /// sound generator callback run by SDL -void SoundCallback(void* udata, Uint8 *stream, int len) +void SoundCallback(void* udata, unsigned char* stream, int len) { if (!g_sound_on) { memset(stream, 0, len); diff --git a/src/SortTest.cpp b/src/SortTest.cpp index 1bc75ffb4..142cc1e96 100644 --- a/src/SortTest.cpp +++ b/src/SortTest.cpp @@ -53,10 +53,7 @@ bool SortTestApp::OnInit() } static size_t testsize[] = { - 10, 11, 12, 13, 14, 15, 16, 32, - 100, 101, 102, 103, 200, - 1024, 1337, 2048, - 0 + 10, 11, 12, 13, 14, 15, 16, 32, 100, 101, 102, 103, 200, 1024, 1337, 1600, 2048, 0 }; struct SortedCheck diff --git a/src/WMain.cpp b/src/WMain.cpp index 297d0a37a..81f831318 100644 --- a/src/WMain.cpp +++ b/src/WMain.cpp @@ -29,7 +29,7 @@ WMain::WMain(wxWindow* parent) : WMain_wxg(parent, wxID_ANY, wxEmptyString, wxDefaultPosition, wxDefaultSize, wxDEFAULT_FRAME_STYLE) { - m_thread = NULL; + m_thread = nullptr; g_sound_on = false; recordButton->Hide(); @@ -47,6 +47,7 @@ WMain::WMain(wxWindow* parent) // resize right split window splitter_0->SetSashPosition(GetSize().x - 280); + splitter_0->SetMinimumPaneSize(1); // insert list of algorithms into wxListBox for (const AlgoEntry* ae = g_algolist; ae != g_algolist_end; ++ae) @@ -88,7 +89,7 @@ WMain::WMain(wxWindow* parent) sdlaudiospec.userdata = sortview; // Open the audio device, forcing the desired format - if ( SDL_OpenAudio(&sdlaudiospec, NULL) < 0 ) { + if ( SDL_OpenAudio(&sdlaudiospec, nullptr) < 0 ) { wxLogError(_("Couldn't open audio: ") + wxString(SDL_GetError(), wxConvISO8859_1)); soundButton->Disable(); } @@ -116,6 +117,7 @@ BEGIN_EVENT_TABLE(WMain, WMain_wxg) EVT_TOGGLEBUTTON(ID_SOUND_BUTTON, WMain::OnSoundButton) EVT_BUTTON(ID_RANDOM_BUTTON, WMain::OnRandomButton) EVT_BUTTON(wxID_ABOUT, WMain::OnAboutButton) + EVT_SPLITTER_DCLICK(wxID_ANY, WMain::OnDClick) EVT_COMMAND_SCROLL(ID_SPEED_SLIDER, WMain::OnSpeedSliderChange) EVT_COMMAND_SCROLL(ID_SOUND_SUSTAIN_SLIDER, WMain::OnSoundSustainSliderChange) @@ -162,19 +164,33 @@ bool WMain::RunAlgorithm() } } +void WMain::OnDClick(wxSplitterEvent& event) { event.Veto(); } + void WMain::AbortAlgorithm() { if (!m_thread) return; - + m_thread_terminate = true; - if (m_thread->IsPaused()) m_thread->Resume(); + if (soundButton->GetValue() == true) + { + SoundReset(); + g_sound_on = false; + SDL_PauseAudio(1); + } + + if (m_thread->IsPaused()) { m_thread->Resume(); } sortview->SetStepwise(false); - m_thread->Wait(); + while (m_thread->IsAlive()) { wxMilliSleep(1); } g_algo_running = false; + if (soundButton->GetValue() == true) + { + g_sound_on = true; + SDL_PauseAudio(0); + } delete m_thread; - m_thread = NULL; + m_thread = nullptr; } void WMain::OnRunButton(wxCommandEvent &event) @@ -182,11 +198,9 @@ void WMain::OnRunButton(wxCommandEvent &event) // join finished thread if (m_thread && !m_thread->IsAlive()) { - m_thread->Wait(); g_algo_running = false; - delete m_thread; - m_thread = NULL; + m_thread = nullptr; } if (event.IsChecked()) @@ -221,7 +235,7 @@ void WMain::OnRunFinished(wxCommandEvent&) g_algo_running = false; delete m_thread; - m_thread = NULL; + m_thread = nullptr; } runButton->SetValue(false); @@ -317,19 +331,23 @@ void WMain::SetDelay(size_t pos) // different slider scale for Linux/GTK: (faster) #if __WXGTK__ || MSW_PERFORMANCECOUNTER + // 0.001 ms formula, tested on Windows + // Warning! The slider will never go past 0.005 ms with this formula + // g_delay = pow(base, pos / 15000.0 * log(2 * 1000.0 * 10.0) / log(base)) / 800.0; g_delay = pow(base, pos / 2000.0 * log(2 * 1000.0 * 10.0) / log(base)) / 10.0; #else // other systems probably have sucking real-time performance anyway g_delay = pow(base, pos / 2000.0 * log(2 * 1000.0) / log(base)); + #endif - if (pos == 0) g_delay = 0; + if (pos == 0) g_delay = 0.1; if (g_delay > 10) labelDelayValue->SetLabel(wxString::Format(_("%.0f ms"), g_delay)); else if (g_delay > 1) - labelDelayValue->SetLabel(wxString::Format(_("%.1f ms"), g_delay)); - else labelDelayValue->SetLabel(wxString::Format(_("%.2f ms"), g_delay)); + else + labelDelayValue->SetLabel(wxString::Format(_("%.3f ms"), g_delay)); } void WMain::OnSoundSustainSliderChange(wxScrollEvent &event) @@ -425,6 +443,9 @@ void WMain::RefreshTimer::Notify() long int compares = g_compare_count; wm.labelComparisonsValue->SetLabel(wxString::Format(_("%ld"), compares)); + long int swaps = m_swaps; + wm.labelSwapsCount->SetLabel(wxString::Format(_("%ld"), swaps)); + long int inversions = wm.sortview->m_array.GetInversions(); if (inversions >= 0) wm.labelInversionCount->SetLabel(wxString::Format(_("%ld"), inversions)); @@ -455,7 +476,7 @@ class MyApp : public wxApp srand((int)wxGetLocalTime()); - WMain* wmain = new WMain(NULL); + WMain* wmain = new WMain(nullptr); SetTopWindow(wmain); wmain->Show(); diff --git a/src/WMain.h b/src/WMain.h index b77d47f2b..f5c4be10d 100644 --- a/src/WMain.h +++ b/src/WMain.h @@ -33,7 +33,7 @@ // ---------------------------------------------------------------------------- // --- Global Constants and Variables -static const size_t g_framerate = 30; +static const size_t g_framerate = 45; // ---------------------------------------------------------------------------- @@ -85,10 +85,10 @@ class WMain : public WMain_wxg virtual void OnArraySizeSliderChange(wxScrollEvent &event); virtual void OnAlgoList(wxCommandEvent &event); virtual void OnAlgoListDClick(wxCommandEvent &event); + virtual void OnDClick(wxSplitterEvent& event); virtual void OnRunFinished(wxCommandEvent&); - - DECLARE_EVENT_TABLE(); + DECLARE_EVENT_TABLE() public: diff --git a/src/WSortView.cpp b/src/WSortView.cpp index b11542ba8..0a7277414 100644 --- a/src/WSortView.cpp +++ b/src/WSortView.cpp @@ -74,6 +74,8 @@ void mswMicroSleep(int microseconds) #endif // MSW_PERFORMANCECOUNTER +static double returnFrac(double n) { return n - static_cast(n); } + void WSortView::DoDelay(double delay) { // must be called by the algorithm thread @@ -81,7 +83,10 @@ void WSortView::DoDelay(double delay) ASSERT(wxThread::GetCurrentId() == wmain->m_thread->GetId()); if (wmain->m_thread_terminate) + { wmain->m_thread->Exit(); + return; + } // idle until main thread signals a condition while (m_stepwise) @@ -90,20 +95,71 @@ void WSortView::DoDelay(double delay) if (se == wxSEMA_NO_ERROR) break; // else timeout, recheck m_stepwise and loop - wmain->m_thread->TestDestroy(); - wmain->m_thread->Yield(); + wxMilliSleep(1); } - - wmain->m_thread->TestDestroy(); - -#if __WXGTK__ - wxMicroSleep(delay * 1000.0); -#elif MSW_PERFORMANCECOUNTER - mswMicroSleep(delay * 1000.0); -#else - // wxMSW does not have a high resolution timer, maybe others do? - wxMilliSleep(delay); -#endif + double secs = 0.0, microDelay = returnFrac(delay) * 1000.0; + delay = std::trunc(delay); + #if __WXGTK__ + // wxMicroSleep(delay * 1000.0); + while (secs < delay) + { + wxMilliSleep(1); + secs += 1.0; + if (wmain->m_thread_terminate) + { + wmain->m_thread->Exit(); + return; + } + } + secs = 0.0; + while (secs < microDelay) + { + wxMicroSleep(1); + secs += 1.0; + if (wmain->m_thread_terminate) + { + wmain->m_thread->Exit(); + return; + } + } + #elif MSW_PERFORMANCECOUNTER + // mswMicroSleep(delay * 1000.0); + while (secs < delay) + { + mswMicroSleep(1000); + secs += 1.0; + if (wmain->m_thread_terminate) + { + wmain->m_thread->Exit(); + return; + } + } + secs = 0.0; + while (secs < microDelay) + { + mswMicroSleep(1); + secs += 1.0; + if (wmain->m_thread_terminate) + { + wmain->m_thread->Exit(); + return; + } + } + #else + // wxMSW does not have a high resolution timer, maybe others do? + // wxMilliSleep(delay); + if (delay < 1.0) { delay = 1.0; } + while (secs < delay) + { + wxMilliSleep(1); + secs += 1.0; + if (wmain->m_thread_terminate) + { + wmain->m_thread->Exit(); + return; + } + } + #endif } void WSortView::OnAccess() @@ -158,7 +214,7 @@ void WSortView::paint(wxDC& dc, const wxSize& dcsize) size_t width = fwidth - 20; size_t height = fheight - 20; - dc.SetDeviceOrigin(10,10); + dc.SetDeviceOrigin(10, 10); // *** draw array element bars @@ -170,14 +226,18 @@ void WSortView::paint(wxDC& dc, const wxSize& dcsize) //double bstep = 1.5 * wbar; // 2nd variant: one pixel between bars - double wbar = (width - (size-1)) / (double)size; - if (width <= (size-1)) wbar = 0.0; - + size_t step = 1; + double wbar = (width - (size - 1)) / (double)size; double bstep = wbar + 1.0; + if (size > width) { + step = size / width; + wbar = wxMax(0.03, width / (double)size); // Scale down further if too many elements + bstep = wbar; + } // special case for bstep = 2 pixel -> draw 2 pixel bars instead of 1px // bar/1px gaps. - if ( fabs(wbar - 1.0) < 0.1 && fabs(bstep - 2.0) < 0.1 ) wbar = 2, bstep = 2; + if (fabs(wbar - 1.0) < 0.1 && fabs(bstep - 2.0) < 0.1) wbar = 2, bstep = 2; static const wxPen pens[] = { *wxWHITE_PEN, @@ -197,6 +257,16 @@ void WSortView::paint(wxDC& dc, const wxSize& dcsize) wxPen(wxColour(128,192,192)), // 14 dark cyan wxPen(wxColour(192,192,128)), // 15 dark yellow wxPen(wxColour(0,128,255)), // 16 blue/cyan mix + wxPen(wxColour(255,64,64)), // 17 bright red + wxPen(wxColour(64,255,64)), // 18 bright green + wxPen(wxColour(64,64,255)), // 19 bright blue + wxPen(wxColour(255,128,64)), // 20 coral + wxPen(wxColour(128,255,64)), // 21 lime green + wxPen(wxColour(64,255,255)), // 22 aqua + wxPen(wxColour(255,215,0)), // 23 gold + wxPen(wxColour(128,0,128)), // 24 purple + wxPen(wxColour(0,255,127)), // 25 spring green + wxPen(wxColour(255,69,0)), // 26 orange red }; static const wxBrush brushes[] = { @@ -217,39 +287,72 @@ void WSortView::paint(wxDC& dc, const wxSize& dcsize) wxBrush(wxColour(128,192,192)), // 14 dark cyan wxBrush(wxColour(192,192,128)), // 15 dark yellow wxBrush(wxColour(0,128,255)), // 16 blue/cyan mix + wxBrush(wxColour(255,64,64)), // 17 bright red + wxBrush(wxColour(64,255,64)), // 18 bright green + wxBrush(wxColour(64,64,255)), // 19 bright blue + wxBrush(wxColour(255,128,64)), // 20 coral + wxBrush(wxColour(128,255,64)), // 21 lime green + wxBrush(wxColour(64,255,255)), // 22 aqua + wxBrush(wxColour(255,215,0)), // 23 bright yellow + wxBrush(wxColour(128,0,128)), // 24 purple + wxBrush(wxColour(0,255,127)), // 25 spring green + wxBrush(wxColour(255,69,0)), // 26 orange red }; wxMutexLocker lock(m_array.m_mutex); ASSERT(lock.IsOk()); - - for (size_t i = 0; i < size; ++i) + const int numBrushes = sizeof(brushes) / sizeof(brushes[0]); + if (step > 1) { - int clr = m_array.GetIndexColor(i); - - ASSERT(clr < (int)(sizeof(brushes) / sizeof(brushes[0]))); - dc.SetPen( pens[clr] ); - dc.SetBrush( brushes[clr] ); - - dc.DrawRectangle(i*bstep, height, - wxMax(1, // draw at least 1 pixel - (wxCoord((i+1)*bstep) - wxCoord(i*bstep)) // integral gap to next bar - - (bstep - wbar) // space between bars - ), - -(double)height * m_array.direct(i).get_direct() / m_array.array_max()); + size_t i_step = 0; + const size_t last = size - 1; + for (size_t i = 0; i < size; ++i) + { + int clr = m_array.GetIndexColor(i); + if (i == i_step || i == last) + { + clr = clr % numBrushes; + dc.SetPen(pens[clr]); + dc.SetBrush(brushes[clr]); + dc.DrawRectangle(i * bstep, height, + wxMax(1, // draw at least 1 pixel + (wxCoord((i + 1) * bstep) - wxCoord(i * bstep)) // integral gap to next bar + - (bstep - wbar) // space between bars + ), + -(double)height * m_array.direct(i).get_direct() / m_array.array_max()); + i_step += step; + } + } } -} + else + { + for (size_t i = 0; i < size; ++i) + { + int clr = m_array.GetIndexColor(i); + ASSERT(clr < numBrushes); + dc.SetPen(pens[clr]); + dc.SetBrush(brushes[clr]); + dc.DrawRectangle(i * bstep, height, + wxMax(1, // draw at least 1 pixel + (wxCoord((i + 1) * bstep) - wxCoord(i * bstep)) // integral gap to next bar + - (bstep - wbar) // space between bars + ), + -(double)height * m_array.direct(i).get_direct() / m_array.array_max()); + } + } +}; BEGIN_EVENT_TABLE(WSortView, wxWindow) - EVT_PAINT (WSortView::OnPaint) - EVT_SIZE (WSortView::OnSize) + EVT_PAINT(WSortView::OnPaint) + EVT_SIZE(WSortView::OnSize) -END_EVENT_TABLE() +END_EVENT_TABLE(); // **************************************************************************** // *** Threading -SortAlgoThread::SortAlgoThread(WMain* wmain, class WSortView& sortview, size_t algo) +SortAlgoThread::SortAlgoThread(WMain* wmain, WSortView& sortview, size_t algo) : wxThread(wxTHREAD_JOINABLE), m_wmain(wmain), m_sortview(sortview), @@ -271,7 +374,7 @@ void* SortAlgoThread::Entry() wxCommandEvent evt(wxEVT_COMMAND_BUTTON_CLICKED, WMain::ID_RUN_FINISHED); m_wmain->GetEventHandler()->AddPendingEvent(evt); - return NULL; + return nullptr; } void SortAlgoThread::Exit() diff --git a/src/WSortView.h b/src/WSortView.h index da5f0f064..8d6105046 100644 --- a/src/WSortView.h +++ b/src/WSortView.h @@ -25,7 +25,6 @@ #define WSORTVIEW_H #include - #include "SortArray.h" // ---------------------------------------------------------------------------- @@ -37,7 +36,7 @@ extern bool g_sound_on; extern double g_sound_sustain; /// the SDL sound callback -void SoundCallback(void *udata, unsigned char *stream, int len); +void SoundCallback(void *udata, unsigned char* stream, int len); /// reset internal state of sound generator void SoundReset(); @@ -99,7 +98,7 @@ class WSortView : public wxPanel, public SortDelay void DoStepwise(); public: - DECLARE_EVENT_TABLE(); + DECLARE_EVENT_TABLE() }; // ---------------------------------------------------------------------------- diff --git a/src/algorithms/flansort.cpp b/src/algorithms/flansort.cpp new file mode 100644 index 000000000..a1962e491 --- /dev/null +++ b/src/algorithms/flansort.cpp @@ -0,0 +1,421 @@ +/* + * + MIT License + + Copyright (c) 2021-2024 aphitorite, edited by Flanlaina (a.k.a. Ayako-chan) + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), to deal + in the Software without restriction, including without limitation the rights + to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in all + copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + SOFTWARE. +* +*/ + +/** + ultimate sorting algorithm: + + unstable and probabilistic sorting algorithm performing an average of + n log n + ~4.4 n comparisons and O(n) moves (~12.1 n) in O(1) space + + makes the fewest comparisons among the sorts of its kind + achieves the ultimate goal of a n log n + O(n) comps in place O(n) moves sort + + implements a modified library sort where the original algorithm is proven to be O(n log n) + from http://www.cs.sunysb.edu/~bender/newpub/BenderFaMo06-librarysort.pdf + + @author aphitorite +*/ + +#include "../SortAlgo.h" +#include + +const int MIN_INSERT = 32; +const int G = 7; +const int R = 3; + +extern std::random_device rd1; +extern std::mt19937 gen1; + +void shiftBW(SortArray& A, int a, int m, int b) +{ + while (m > a) { --b; --m; A[b].get(); A.swap(b, m); } +} + +int randGapSearch(SortArray& A, int a, int b, int val) +{ + int s = G + 1, randCnt = 0; + while (a < b) + { + int m = a + (((b - a) / s) / 2) * s; + int ele = A[m].get(); + if (val < ele) { b = m; } + else if (val > ele) { a = m + s; } + else if (randCnt++ < 1) + { + std::uniform_int_distribution<> distr(0, ((b - a) / s) - 1); + m = a + distr(gen1) * s; + ele = A[m].get(); + if (val < ele) { b = m; } + else if (val > ele) { a = m + s; } + else { a = m; break; } + } + else { a = m; break; } + } + return a; +} + +int rightBinSearch(SortArray& A, int a, int b, int val, bool bw) +{ + int cmp = -1; + if (bw == true) { cmp = 1; } + while (a < b) + { + int m = a + (b - a) / 2; + int ele = A[m].get(), result = 0; + if (val < ele) { result = -1; } + else if (val > ele) { result = 1; } + + if (result == cmp) { b = m; } + else { a = m + 1; } + } + return a; +} + +void insertTo(SortArray& A, int tmp, int a, int b) +{ + while (a > b) + { + size_t z = static_cast(a); + A[z].get(); + A.set(z, A[z - 1]); + --a; + } + size_t z = static_cast(b); + A.set(z, ArrayItem(tmp)); + A[z].get(); +} + +void binaryInsert(SortArray& A, int a, int b) +{ + for (int i = a + 1; i < b; ++i) + { + int ele = A[i].get(); + insertTo(A, ele, i, rightBinSearch(A, a, i, ele, false)); + } +} + +void retrieve(SortArray& A, int b, int p, int pEnd, int bsv, bool bw) +{ + int j = b - 1, m; + for (int k = pEnd - (G + 1); k > p + G; ) + { + m = rightBinSearch(A, k - G, k, bsv, bw) - 1; + k -= G + 1; + while (m >= k) + { + size_t x = static_cast(j), y = static_cast(m); + A[x].get(); + A.swap(x, y); + --j; --m; + } + } + m = rightBinSearch(A, p, p + G, bsv, bw) - 1; + while (m >= p) + { + size_t x = static_cast(j), y = static_cast(m); + A[x].get(); + A.swap(x, y); + --j; --m; + } +} + +void rescatter(SortArray & A, int i, int p, int pEnd, int bsv, bool bw) +{ + int j = i - 2 * (G + 1), m; + for (int k = pEnd - (G + 1); k > p + G; ) + { + m = rightBinSearch(A, k - G, k, bsv, bw) - 1; + k -= G + 1; + while (m >= k) + { + size_t x = static_cast(j), y = static_cast(m); + A[x].get(); + A.swap(x, y); + j -= G + 1; --m; + } + } + m = rightBinSearch(A, p, p + G, bsv, bw) - 1; + while (m >= p) + { + size_t x = static_cast(j), y = static_cast(m); + A[x].get(); + A.swap(x, y); + j -= G + 1; --m; + } +} + +void rebalance(SortArray& A, int a, int b, int p, int pEnd, int pb, int bsv, bool bw) +{ + retrieve(A, b, p, pEnd, bsv, bw); + int gCnt = (pb - p) / (G + 1); + int gapElems = (b - a) - gCnt + 1; + int baseCnt = gapElems / gCnt; + int extra = gapElems - gCnt * baseCnt; + for (int k = p + G;; k += G + 1) + { + int val = 0; + if (extra > 0) { val = 1; } + int iter = baseCnt + val; + --extra; + for (int j = 0; j < iter; ++j) + { + size_t x = static_cast(a), y = static_cast(k - G + j); + A[x].get(); + A.swap(x, y); + ++a; + } + if (k < pb - (G + 1)) + { + size_t x = static_cast(a), y = static_cast(k); + A[x].get(); + A.swap(x, y); + ++a; + } + else { break; } + } +} + +void librarySort(SortArray& A, int a, int b, int p, int pb, int bsv, bool bw) +{ + int len = b - a; + if (len <= MIN_INSERT) + { + binaryInsert(A, a, b); + return; + } + int s = len; + while (s > MIN_INSERT) { s = (s - 1) / R + 1; } + int i = a + s, j = a + R * s, pEnd = p + (s + 1) * (G + 1) + G; + binaryInsert(A, a, i); + for (int k = 0; k < s; ++k) + { + size_t x = static_cast(a + k); + size_t y = static_cast(p + k * (G + 1) + G); + A[x].get(); + A.swap(x, y); + } + + while (i < b) + { + if (i == j) + { + s = i - a; + int pEndNew = p + (s + 1) * (G + 1) + G; + if (pEndNew > pb) + { + rebalance(A, a, i, p, pEnd, pb, bsv, bw); + pEnd = pb; + j = a; + } + else + { + rescatter(A, pEndNew, p, pEnd, bsv, bw); + pEnd = pEndNew; + j = a + (j - a) * R; + } + } + int ele = A[i].get(); + int bLoc = randGapSearch(A, p + G, pEnd - (G + 1), ele); + int loc = rightBinSearch(A, bLoc - G, bLoc, bsv, bw); + if (loc == bLoc) + { + int rotP = -1; + do { bLoc += G + 1; } + while (bLoc < pEnd && (rotP = rightBinSearch(A, bLoc - G, bLoc, bsv, bw)) == bLoc); + if (bLoc == pb) { rebalance(A, a, i, p, pEnd, pb, bsv, bw); } + else if (bLoc == pEnd) + { + int rotS = G / 2 + 1; + shiftBW(A, loc - rotS, bLoc - (G + 1), bLoc - (G + 1) + rotS); + pEnd += G + 1; + } + else + { + int rotS = bLoc - std::max(rotP, bLoc - (G + 1) / 2); + shiftBW(A, loc - rotS, bLoc - rotS, bLoc); + } + } + else + { + size_t k = static_cast(i), l = static_cast(loc); + int ele = A[k].get(); + A.set(k, A[l]); + ++i; + insertTo(A, ele, loc, rightBinSearch(A, bLoc - G, loc, ele, false)); + } + } + retrieve(A, b, p, pEnd, bsv, bw); +} + +int medianOfThree(SortArray& A, int a, int m, int b) +{ + if (A[m] > A[a]) + { + if (A[m] < A[b]) { return m; } + if (A[a] > A[b]) { return a; } + else { return b; } + } + else + { + if (A[m] > A[b]) { return m; } + if (A[a] < A[b]) { return a; } + else { return b; } + } +} + +int ninther(SortArray& A, int a, int b) +{ + int s = (b - a) / 9; + int a1 = medianOfThree(A, a, a + s, a + 2 * s); + int m1 = medianOfThree(A, a + 3 * s, a + 4 * s, a + 5 * s); + int b1 = medianOfThree(A, a + 6 * s, a + 7 * s, a + 8 * s); + return medianOfThree(A, a1, m1, b1); +} + +int medianOfThreeNinthers(SortArray& A, int a, int b) +{ + int s = (b - a) / 3; + int a1 = ninther(A, a, a + s); + int m1 = ninther(A, a + s, a + 2 * s); + int b1 = ninther(A, a + 2 * s, b); + return medianOfThree(A, a1, m1, b1); +} + +void quickLibrary(SortArray& A, int a, int b, int p, int pb, int minSize, int bsv, bool bw) +{ + if (b - a <= minSize) + { + librarySort(A, a, b, p, pb, bsv, bw); + return; + } + int piv = A[medianOfThreeNinthers(A, a, b)]; + int i = a - 1, j = b; + do + { + int val = A[0]; + do + { + ++i; + val = A[i].get(); + } + while (i < j && val < piv); + val = A[0]; + do + { + --j; + val = A[j].get(); + } + while (j >= i && val > piv); + if (i < j) + { + size_t x = static_cast(i), y = static_cast(j); + A.swap(x, y); + } + else { break; } + } + while (true); + quickLibrary(A, a, i, p, pb, minSize, bsv, bw); + quickLibrary(A, i, b, p, pb, minSize, bsv, bw); +} + +void QuickLibrarySort(SortArray& A) +{ + int length = static_cast(A.size()), a = 0, b = length; + while (b - a > MIN_INSERT) + { + int piv = A[medianOfThreeNinthers(A, a, b)]; + int i1 = a, i = a - 1, j = b, j1 = b; + for (;;) + { + ++i; + while (i < j) + { + int ele = A[i].get(); + if (ele == piv) + { + size_t x = static_cast(i1), y = static_cast(i); + A.swap(x, y); ++i1; + } + else if (ele > piv) { break; } + ++i; + } + --j; + while (j > i) + { + int ele = A[j].get(); + if (ele == piv) + { + --j1; + size_t x = static_cast(j1), y = static_cast(j); + A.swap(x, y); + } + else if (ele < piv) { break; } + --j; + } + if (i < j) + { + size_t x = static_cast(i), y = static_cast(j); + A.swap(x, y); + } + else + { + if (i1 == b) { return; } + else if (j < i) { ++j; } + while (i1 > a) + { + --i; --i1; + size_t x = static_cast(i), y = static_cast(i1); + A[x].get(); + A.swap(x, y); + } + while (j1 < b) + { + size_t x = static_cast(j), y = static_cast(j1); + A[x].get(); + A.swap(x, y); + ++j; ++j1; + } + break; + } + } + int left = i - a, right = b - j; + if (left <= right) + { + right -= (right + 1) % (G + 1) + (G + 1); + left = std::max(right / (G + 1) * R, MIN_INSERT); + quickLibrary(A, a, i, j, j + right, left, piv, false); + a = j; + } + else + { + left -= (left + 1) % (G + 1) + (G + 1); + right = std::max(left / (G + 1) * R, MIN_INSERT); + quickLibrary(A, j, b, a, a + left, right, piv, true); + b = i; + } + } + binaryInsert(A, a, b); +} diff --git a/src/algorithms/grailsort.cpp b/src/algorithms/grailsort.cpp new file mode 100644 index 000000000..4fd98a059 --- /dev/null +++ b/src/algorithms/grailsort.cpp @@ -0,0 +1,1338 @@ +/* + * MIT License + * + * Copyright (c) 2013 Andrey Astrelin + * Copyright (c) 2020-2021 The Holy Grail Sort Project + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + + /* + * The Holy Grail Sort Project + * Project Manager: Summer Dragonfly + * Project Contributors: 666666t + * Anonymous0726 + * aphitorite + * Control + * dani_dlg + * DeveloperSort + * EilrahcF + * Enver + * Gaming32 + * lovebuny + * Morwenn + * MP + * phoenixbound + * Spex_guy + * thatsOven + * _fluffyy + * + * Special thanks to "The Studio" Discord community! + */ + +#include "../SortAlgo.h" +#include +#include +#include +#include +#include + +namespace grailsort_detail +{ + // Credit to phoenixbound for this clever idea + enum struct Subarray + { + LEFT, + RIGHT + }; + + template + struct ThreeWayCompare + { + Compare compare; + + explicit ThreeWayCompare(Compare&& comp) : + compare(std::forward(comp)) + {} + + template + int operator()(T&& lhs, U&& rhs) + { + if (compare(lhs, rhs)) { + return -1; + } + if (compare(rhs, lhs)) { + return 1; + } + return 0; + } + }; + + struct GrailSort + { + int currBlockLen; + Subarray currBlockOrigin; + + template + static void BlockSwap(RandomAccessIterator array, int a, int b, int blockLen) { + counted_swap_ranges(array + a, array + (a + blockLen), array + b); + } + + // Swaps the order of two adjacent blocks whose lengths may or may not be equal. + // Variant of the Gries-Mills algorithm, which is basically recursive block swaps + template + static void Rotate(RandomAccessIterator array, int start, int leftLen, int rightLen) { + while (leftLen > 0 && rightLen > 0) { + if (leftLen <= rightLen) { + BlockSwap(array, start, start + leftLen, leftLen); + start += leftLen; + rightLen -= leftLen; + } + else { + BlockSwap(array, start + leftLen - rightLen, start + leftLen, rightLen); + leftLen -= rightLen; + } + } + } + + // Variant of Insertion Sort that utilizes swaps instead of overwrites. + // Also known as "Optimized Gnomesort". + template + static void InsertSort(RandomAccessIterator array, int start, int length, Compare comp) { + for (int item = 1; item < length; item++) { + int left = start + item - 1; + int right = start + item; + + while (left >= start && comp(array[left], array[right]) > 0) { + counted_iter_swap(array + left, array + right); + left--; + right--; + } + } + } + + template + static int BinarySearchLeft(RandomAccessIterator array, int start, int length, const T& target, Compare comp) { + int left = 0; + int right = length; + + while (left < right) { + // equivalent to (left + right) / 2 with added overflow protection + int middle = left + ((right - left) / 2); + + if (comp(array[start + middle], target) < 0) { + left = middle + 1; + } + else { + right = middle; + } + } + return left; + } + + // Credit to Anonymous0726 for debugging + template + static int BinarySearchRight(RandomAccessIterator array, int start, int length, const T& target, Compare comp) { + int left = 0; + int right = length; + + while (left < right) { + // equivalent to (left + right) / 2 with added overflow protection + int middle = left + ((right - left) / 2); + if (comp(array[start + middle], target) > 0) { + right = middle; + } + else { + left = middle + 1; + } + } + // OFF-BY-ONE BUG FIXED: used to be `return right - 1;` + return right; + } + + // cost: 2 * length + idealKeys^2 / 2 + template + static int CollectKeys(RandomAccessIterator array, int start, int length, int idealKeys, Compare comp) { + int keysFound = 1; // by itself, the first item in the array is our first unique key + int firstKey = 0; // the first item in the array is at the first position in the array + int currKey = 1; // the index used for finding potentially unique items ("keys") in the array + + while (currKey < length && keysFound < idealKeys) { + + // Find the location in the key-buffer where our current key can be inserted in sorted order. + // If the key at insertPos is equal to currKey, then currKey isn't unique and we move on. + int insertPos = BinarySearchLeft(array, start + firstKey, keysFound, array[start + currKey], comp); + + // The second part of this conditional does the equal check we were just talking about; however, + // if currKey is larger than everything in the key-buffer (meaning insertPos == keysFound), + // then that also tells us it wasn't *equal* to anything in the key-buffer. Magic! :) + if (insertPos == keysFound || comp(array[start + currKey], array[start + firstKey + insertPos]) != 0) { + + // First, rotate the key-buffer over to currKey's immediate left... + // (this helps save a TON of swaps/writes!!!) + Rotate(array, start + firstKey, keysFound, currKey - (firstKey + keysFound)); + + // Update the new position of firstKey... + firstKey = currKey - keysFound; + + // Then, "insertion sort" currKey to its spot in the key-buffer! + Rotate(array, start + firstKey + insertPos, keysFound - insertPos, 1); + + // One step closer to idealKeys. + keysFound++; + } + // Move on and test the next key... + currKey++; + } + + // Bring however many keys we found back to the beginning of our array, + // and return the number of keys collected. + Rotate(array, start, firstKey, keysFound); + return keysFound; + } + + template + static void PairwiseSwaps(RandomAccessIterator array, int start, int length, Compare comp) { + int index; + for (index = 1; index < length; index += 2) { + int left = start + index - 1; + int right = start + index; + + if (comp(array[left], array[right]) > 0) { + counted_swap(array[left - 2], array[right]); + counted_swap(array[right - 2], array[left]); + } + else { + counted_swap(array[left - 2], array[left]); + counted_swap(array[right - 2], array[right]); + } + } + + int left = start + index - 1; + if (left < start + length) { + counted_swap(array[left - 2], array[left]); + } + } + + template + static void PairwiseWrites(RandomAccessIterator array, int start, int length, Compare comp) { + int index; + for (index = 1; index < length; index += 2) { + int left = start + index - 1; + int right = start + index; + + if (comp(array[left], array[right]) > 0) { + array[left - 2] = std::move(array[right]); + array[right - 2] = std::move(array[left]); + } + else { + array[left - 2] = std::move(array[left]); + array[right - 2] = std::move(array[right]); + } + } + + int left = start + index - 1; + if (left < start + length) { + array[left - 2] = std::move(array[left]); + } + } + + // array[buffer .. start - 1] <=> "scrolling buffer" + // + // "scrolling buffer" + array[start, middle - 1] + array[middle, end - 1] + // --> array[buffer, buffer + end - 1] + "scrolling buffer" + template + static void MergeForwards(RandomAccessIterator array, int start, int leftLen, int rightLen, int bufferOffset, Compare comp) { + auto buffer = array + (start - bufferOffset); + auto left = array + start; + auto middle = array + (start + leftLen); + auto right = middle; + auto end = middle + rightLen; + + while (right != end) { + if (left == middle || comp(*left, *right) > 0) { + counted_iter_swap(buffer, right); + ++right; + } + else { + counted_iter_swap(buffer, left); + ++left; + } + ++buffer; + } + + while (left != middle) { + counted_iter_swap(buffer, left); + ++buffer; + ++left; + } + } + + // credit to 666666t for thorough bug-checking/fixing + template + static void MergeBackwards(RandomAccessIterator array, int start, int leftLen, int rightLen, int bufferOffset, Compare comp) { + // used to be '= start' + int end = start - 1; + // used to be '= start + leftLen - 1' + int left = end + leftLen; + int middle = left; + // OFF-BY-ONE BUG FIXED: used to be `int right = middle + rightLen - 1;` + int right = middle + rightLen; + // OFF-BY-ONE BUG FIXED: used to be `int buffer = right + bufferOffset - 1;` + int buffer = right + bufferOffset; + + // used to be 'left >= end' + while (left > end) { + if (right == middle || comp(array[left], array[right]) > 0) { + + counted_swap(array[buffer], array[left]); + left--; + } + else { + counted_swap(array[buffer], array[right]); + right--; + } + buffer--; + } + + if (right != buffer) { + while (right > middle) { + counted_swap(array[buffer], array[right]); + buffer--; + right--; + } + } + } + + // array[buffer .. start - 1] <=> "free space" + // + // "free space" + array[start, middle - 1] + array[middle, end - 1] + // --> array[buffer, buffer + end - 1] + "free space" + // + // FUNCTION RENAMED: More consistent with "out-of-place" being at the end + template + static void MergeOutOfPlace(RandomAccessIterator array, int start, int leftLen, int rightLen, int bufferOffset, Compare comp) { + int buffer = start - bufferOffset; + int left = start; + int middle = start + leftLen; + int right = middle; + int end = middle + rightLen; + + while (right < end) { + if (left == middle || comp(array[left], array[right]) > 0) { + array[buffer] = std::move(array[right]); + right++; + } + else { + array[buffer] = std::move(array[left]); + left++; + } + buffer++; + } + + if (buffer != left) { + while (left < middle) { + array[buffer] = std::move(array[left]); + buffer++; + left++; + } + } + } + + template + static void BuildInPlace(RandomAccessIterator array, int start, int length, int currentLen, int bufferLen, Compare comp) { + for (int mergeLen = currentLen; mergeLen < bufferLen; mergeLen *= 2) { + int fullMerge = 2 * mergeLen; + + int mergeIndex; + int mergeEnd = start + length - fullMerge; + int bufferOffset = mergeLen; + + for (mergeIndex = start; mergeIndex <= mergeEnd; mergeIndex += fullMerge) { + MergeForwards(array, mergeIndex, mergeLen, mergeLen, bufferOffset, comp); + } + + int leftOver = length - (mergeIndex - start); + + if (leftOver > mergeLen) { + MergeForwards(array, mergeIndex, mergeLen, leftOver - mergeLen, bufferOffset, comp); + } + else { + Rotate(array, mergeIndex - mergeLen, mergeLen, leftOver); + } + + start -= mergeLen; + } + + int fullMerge = 2 * bufferLen; + int lastBlock = length % fullMerge; + int lastOffset = start + length - lastBlock; + + if (lastBlock <= bufferLen) { + Rotate(array, lastOffset, lastBlock, bufferLen); + } + else { + MergeBackwards(array, lastOffset, bufferLen, lastBlock - bufferLen, bufferLen, comp); + } + + for (int mergeIndex = lastOffset - fullMerge; mergeIndex >= start; mergeIndex -= fullMerge) { + MergeBackwards(array, mergeIndex, bufferLen, bufferLen, bufferLen, comp); + } + } + + template + void BuildOutOfPlace(RandomAccessIterator array, int start, int length, int bufferLen, int extLen, + BufferIterator extBuffer, Compare comp) { + std::move(array + (start - extLen), array + start, extBuffer); + + PairwiseWrites(array, start, length, comp); + start -= 2; + + int mergeLen; + for (mergeLen = 2; mergeLen < extLen; mergeLen *= 2) { + int fullMerge = 2 * mergeLen; + + int mergeIndex; + int mergeEnd = start + length - fullMerge; + int bufferOffset = mergeLen; + + for (mergeIndex = start; mergeIndex <= mergeEnd; mergeIndex += fullMerge) { + MergeOutOfPlace(array, mergeIndex, mergeLen, mergeLen, bufferOffset, comp); + } + + int leftOver = length - (mergeIndex - start); + + if (leftOver > mergeLen) { + MergeOutOfPlace(array, mergeIndex, mergeLen, leftOver - mergeLen, bufferOffset, comp); + } + else { + // MINOR CHANGE: Used to be a loop; much clearer now + std::move(array + mergeIndex, array + (mergeIndex + leftOver), array + (mergeIndex - mergeLen)); + } + + start -= mergeLen; + } + + std::move(extBuffer, extBuffer + extLen, array + (start + length)); + BuildInPlace(array, start, length, mergeLen, bufferLen, comp); + } + + // build blocks of length 'bufferLen' + // input: [start - mergeLen, start - 1] elements are buffer + // output: first 'bufferLen' elements are buffer, blocks (2 * bufferLen) and last subblock sorted + template + void BuildBlocks(RandomAccessIterator array, int start, int length, int bufferLen, + BufferIterator extBuffer, int extBufferLen, Compare comp) { + if (extBufferLen != 0) { + int extLen; + + if (bufferLen < extBufferLen) { + extLen = bufferLen; + } + else { + // max power of 2 -- just in case + extLen = 1; + while ((extLen * 2) <= extBufferLen) { + extLen *= 2; + } + } + + BuildOutOfPlace(array, start, length, bufferLen, extLen, extBuffer, comp); + } + else { + PairwiseSwaps(array, start, length, comp); + BuildInPlace(array, start - 2, length, 2, bufferLen, comp); + } + } + + // Returns the final position of 'medianKey' + template + static int BlockSelectSort(RandomAccessIterator array, int firstKey, int start, int medianKey, int blockCount, int blockLen, Compare comp) { + for (int firstBlock = 0; firstBlock < blockCount; firstBlock++) { + int selectBlock = firstBlock; + + for (int currBlock = firstBlock + 1; currBlock < blockCount; currBlock++) { + int compare = comp(array[start + (currBlock * blockLen)], + array[start + (selectBlock * blockLen)]); + + if (compare < 0 || (compare == 0 && comp(array[firstKey + currBlock], + array[firstKey + selectBlock]) < 0)) { + selectBlock = currBlock; + } + } + + if (selectBlock != firstBlock) { + // Swap the left and right selected blocks... + BlockSwap(array, start + (firstBlock * blockLen), start + (selectBlock * blockLen), blockLen); + + // Swap the keys... + counted_swap(array[firstKey + firstBlock], array[firstKey + selectBlock]); + + // ...and follow the 'medianKey' if it was swapped + + // ORIGINAL LOC: if(midkey==u-1 || midkey==p) midkey^=(u-1)^p; + // MASSIVE, MASSIVE credit to lovebuny for figuring this one out! + if (medianKey == firstBlock) { + medianKey = selectBlock; + } + else if (medianKey == selectBlock) { + medianKey = firstBlock; + } + } + } + + return medianKey; + } + + // Swaps Grailsort's "scrolling buffer" from the right side of the array all the way back to 'start'. + // Costs O(n) swaps (amortized). + // + // OFF-BY-ONE BUG FIXED: used to be `int index = start + resetLen`; credit to 666666t for debugging + // RESTRUCTED, BETTER NAMES: 'resetLen' is now 'length' and 'bufferLen' is now 'bufferOffset' + template + static void InPlaceBufferReset(RandomAccessIterator array, int start, int length, int bufferOffset) { + int index = start + length - 1; + int buffer = index - bufferOffset; + ArrayItem pos = ArrayItem(index); + while (index >= start) { + pos.get(); + counted_swap(array[index], array[buffer]); + index--; buffer--; + pos = ArrayItem(index); + } + } + + // Shifts entire array over 'bufferOffset' spaces to move the out-of-place merging buffer back to + // the beginning of the array. + // Costs O(n) writes (amortized). + // + // OFF-BY-ONE BUG FIXED: used to be `int index = start + resetLen`; credit to 666666t for debugging + // RESTRUCTED, BETTER NAMES: 'resetLen' is now 'length' and 'bufferLen' is now 'bufferOffset' + template + static void OutOfPlaceBufferReset(RandomAccessIterator array, int start, int length, int bufferOffset) { + int index = start + length - 1; + int buffer = index - bufferOffset; + ArrayItem pos = ArrayItem(index); + while (index >= start) { + pos.get(); + array[index] = std::move(array[buffer]); + index--; buffer--; + pos = ArrayItem(index); + } + } + + // Rewinds Grailsort's "scrolling buffer" to the left any items belonging to the left subarray block + // left over by a "smart merge". This is used to continue an ongoing merge that has run out of buffer space. + // Costs O(sqrt n) swaps (amortized) in the *absolute* worst-case. + // + // NAMING IMPROVED: the left over items are in the middle of the merge while the buffer is at the end + // BETTER ORDER-OF-OPERATIONS, NAMING IMPROVED: the left over items (now called 'leftBlock') are in the + // middle of the merge while the buffer is at the end + template + static void InPlaceBufferRewind(RandomAccessIterator array, int start, int leftBlock, int buffer) { + while (leftBlock >= start) { + counted_swap(array[buffer], array[leftBlock]); + leftBlock--; + buffer--; + } + } + + // Rewinds Grailsort's out-of-place buffer to the left of any items belonging to the left subarray block + // left over by a "smart merge". This is used to continue an ongoing merge that has run out of buffer space. + // Costs O(sqrt n) writes (amortized) in the *absolute* worst-case. + // + // INCORRECT ORDER OF PARAMETERS BUG FIXED: `leftOvers` should be the middle, and `buffer` should be the end + // BETTER ORDER, INCORRECT ORDER OF PARAMETERS BUG FIXED: `leftOvers` (now called 'leftBlock') should be + // the middle, and `buffer` should be the end + template + static void OutOfPlaceBufferRewind(RandomAccessIterator array, int start, int leftBlock, int buffer) { + while (leftBlock >= start) { + array[buffer] = std::move(array[leftBlock]); + leftBlock--; + buffer--; + } + } + + template + static Subarray GetSubarray(RandomAccessIterator array, int currKey, int medianKey, Compare comp) { + if (comp(array[currKey], array[medianKey]) < 0) { + return Subarray::LEFT; + } + else { + return Subarray::RIGHT; + } + } + + // FUNCTION RE-RENAMED: last/final left blocks are used to calculate the length of the final merge + template + static int CountLastMergeBlocks(RandomAccessIterator array, int offset, int blockCount, int blockLen, Compare comp) { + int blocksToMerge = 0; + + int lastRightFrag = offset + (blockCount * blockLen); + int prevLeftBlock = lastRightFrag - blockLen; + + while (blocksToMerge < blockCount && comp(array[lastRightFrag], array[prevLeftBlock]) < 0) { + blocksToMerge++; + prevLeftBlock -= blockLen; + } + + return blocksToMerge; + } + + template + void SmartMerge(RandomAccessIterator array, int start, int leftLen, Subarray leftOrigin, int rightLen, int bufferOffset, Compare comp) { + auto buffer = array + (start - bufferOffset); + auto left = array + start; + auto middle = left + leftLen; + auto right = middle; + auto end = middle + rightLen; + + if (leftOrigin == Subarray::LEFT) { + while (left != middle && right != end) { + if (comp(*left, *right) <= 0) { + counted_iter_swap(buffer, left); + ++left; + } + else { + counted_iter_swap(buffer, right); + ++right; + } + ++buffer; + } + } + else { + while (left != middle && right != end) { + if (comp(*left, *right) < 0) { + counted_iter_swap(buffer, left); + ++left; + } + else { + counted_iter_swap(buffer, right); + ++right; + } + ++buffer; + } + } + + if (left < middle) { + this->currBlockLen = middle - left; + // UPDATED ARGUMENTS: 'middle' and 'end' now 'middle - 1' and 'end - 1' + InPlaceBufferRewind(array, left - array, (middle - array) - 1, (end - array) - 1); + } + else { + this->currBlockLen = end - right; + if (leftOrigin == Subarray::LEFT) { + this->currBlockOrigin = Subarray::RIGHT; + } + else { + this->currBlockOrigin = Subarray::LEFT; + } + } + } + + // MINOR CHANGE: better naming -- 'insertPos' is now 'mergeLen' -- and "middle" calculation simplified + template + void SmartLazyMerge(RandomAccessIterator array, int start, int leftLen, Subarray leftOrigin, int rightLen, Compare comp) { + int middle = start + leftLen; + + if (leftOrigin == Subarray::LEFT) { + if (comp(array[middle - 1], array[middle]) > 0) { + while (leftLen != 0) { + int mergeLen = BinarySearchLeft(array, middle, rightLen, array[start], comp); + + if (mergeLen != 0) { + Rotate(array, start, leftLen, mergeLen); + start += mergeLen; + rightLen -= mergeLen; + } + + middle += mergeLen; + + if (rightLen == 0) { + this->currBlockLen = leftLen; + return; + } + else { + do { + start++; + leftLen--; + } while (leftLen != 0 && comp(array[start], array[middle]) <= 0); + } + } + } + } + else { + if (comp(array[middle - 1], array[middle]) >= 0) { + while (leftLen != 0) { + int mergeLen = BinarySearchRight(array, start + leftLen, rightLen, array[start], comp); + + if (mergeLen != 0) { + Rotate(array, start, leftLen, mergeLen); + start += mergeLen; + rightLen -= mergeLen; + } + + middle += mergeLen; + + if (rightLen == 0) { + this->currBlockLen = leftLen; + return; + } + else { + do { + start++; + leftLen--; + } while (leftLen != 0 && comp(array[start], array[middle]) < 0); + } + } + } + } + + this->currBlockLen = rightLen; + if (leftOrigin == Subarray::LEFT) { + this->currBlockOrigin = Subarray::RIGHT; + } + else { + this->currBlockOrigin = Subarray::LEFT; + } + } + + // FUNCTION RENAMED: more consistent with other "out-of-place" merges + template + void SmartMergeOutOfPlace(RandomAccessIterator array, int start, int leftLen, Subarray leftOrigin, int rightLen, int bufferOffset, Compare comp) { + int buffer = start - bufferOffset; + int left = start; + int middle = start + leftLen; + int right = middle; + int end = middle + rightLen; + + if (leftOrigin == Subarray::LEFT) { + while (left < middle && right < end) { + if (comp(array[left], array[right]) <= 0) { + array[buffer] = std::move(array[left]); + left++; + } + else { + array[buffer] = std::move(array[right]); + right++; + } + buffer++; + } + } + else { + while (left < middle && right < end) { + if (comp(array[left], array[right]) < 0) { + array[buffer] = std::move(array[left]); + left++; + } + else { + array[buffer] = std::move(array[right]); + right++; + } + buffer++; + } + } + + if (left < middle) { + this->currBlockLen = middle - left; + // UPDATED ARGUMENTS: 'middle' and 'end' now 'middle - 1' and 'end - 1' + OutOfPlaceBufferRewind(array, left, middle - 1, end - 1); + } + else { + this->currBlockLen = end - right; + if (leftOrigin == Subarray::LEFT) { + this->currBlockOrigin = Subarray::RIGHT; + } + else { + this->currBlockOrigin = Subarray::LEFT; + } + } + } + + // Credit to Anonymous0726 for better variable names such as "nextBlock" + // Also minor change: removed unnecessary "currBlock = nextBlock" lines + template + void MergeBlocks(RandomAccessIterator array, int firstKey, int medianKey, int start, int blockCount, int blockLen, + int lastMergeBlocks, int lastLen, Compare comp) { + int buffer; + + int currBlock; + int nextBlock = start + blockLen; + + this->currBlockLen = blockLen; + this->currBlockOrigin = GetSubarray(array, firstKey, medianKey, comp); + + Subarray nextBlockOrigin; + for (int keyIndex = 1; keyIndex < blockCount; keyIndex++, nextBlock += blockLen) { + currBlock = nextBlock - this->currBlockLen; + nextBlockOrigin = GetSubarray(array, firstKey + keyIndex, medianKey, comp); + + if (nextBlockOrigin == this->currBlockOrigin) { + buffer = currBlock - blockLen; + + BlockSwap(array, buffer, currBlock, this->currBlockLen); + this->currBlockLen = blockLen; + } + else { + SmartMerge(array, currBlock, this->currBlockLen, this->currBlockOrigin, blockLen, blockLen, comp); + } + } + + currBlock = nextBlock - this->currBlockLen; + buffer = currBlock - blockLen; + + if (lastLen != 0) { + if (this->currBlockOrigin == Subarray::RIGHT) { + BlockSwap(array, buffer, currBlock, this->currBlockLen); + + currBlock = nextBlock; + this->currBlockLen = blockLen * lastMergeBlocks; + this->currBlockOrigin = Subarray::LEFT; + } + else { + this->currBlockLen += blockLen * lastMergeBlocks; + } + + MergeForwards(array, currBlock, this->currBlockLen, lastLen, blockLen, comp); + } + else { + BlockSwap(array, buffer, currBlock, this->currBlockLen); + } + } + + template + void LazyMergeBlocks(RandomAccessIterator array, int firstKey, int medianKey, int start, int blockCount, int blockLen, + int lastMergeBlocks, int lastLen, Compare comp) { + int currBlock; + int nextBlock = start + blockLen; + + this->currBlockLen = blockLen; + this->currBlockOrigin = GetSubarray(array, firstKey, medianKey, comp); + + Subarray nextBlockOrigin; + for (int keyIndex = 1; keyIndex < blockCount; keyIndex++, nextBlock += blockLen) { + currBlock = nextBlock - this->currBlockLen; + + nextBlockOrigin = GetSubarray(array, firstKey + keyIndex, medianKey, comp); + + if (nextBlockOrigin == this->currBlockOrigin) { + this->currBlockLen = blockLen; + } + else { + // These checks were included in the original code... but why??? + if (blockLen != 0 && this->currBlockLen != 0) { + SmartLazyMerge(array, currBlock, this->currBlockLen, this->currBlockOrigin, blockLen, comp); + } + } + } + + currBlock = nextBlock - this->currBlockLen; + + if (lastLen != 0) { + if (this->currBlockOrigin == Subarray::RIGHT) { + currBlock = nextBlock; + this->currBlockLen = blockLen * lastMergeBlocks; + this->currBlockOrigin = Subarray::LEFT; + } + else { + this->currBlockLen += blockLen * lastMergeBlocks; + } + + LazyMerge(array, currBlock, this->currBlockLen, lastLen, comp); + } + } + + template + void MergeBlocksOutOfPlace(RandomAccessIterator array, int firstKey, int medianKey, int start, int blockCount, int blockLen, + int lastMergeBlocks, int lastLen, Compare comp) { + int buffer; + int currBlock; + int nextBlock = start + blockLen; + + this->currBlockLen = blockLen; + this->currBlockOrigin = GetSubarray(array, firstKey, medianKey, comp); + + Subarray nextBlockOrigin; + for (int keyIndex = 1; keyIndex < blockCount; keyIndex++, nextBlock += blockLen) { + currBlock = nextBlock - this->currBlockLen; + nextBlockOrigin = GetSubarray(array, firstKey + keyIndex, medianKey, comp); + + if (nextBlockOrigin == this->currBlockOrigin) { + buffer = currBlock - blockLen; + std::move(array + currBlock, array + (currBlock + this->currBlockLen), array + buffer); + this->currBlockLen = blockLen; + } + else { + SmartMergeOutOfPlace(array, currBlock, this->currBlockLen, this->currBlockOrigin, blockLen, blockLen, comp); + } + } + + currBlock = nextBlock - this->currBlockLen; + buffer = currBlock - blockLen; + + if (lastLen != 0) { + if (this->currBlockOrigin == Subarray::RIGHT) { + std::move(array + currBlock, array + (currBlock + this->currBlockLen), array + buffer); + currBlock = nextBlock; + this->currBlockLen = blockLen * lastMergeBlocks; + this->currBlockOrigin = Subarray::LEFT; + } + else { + this->currBlockLen += blockLen * lastMergeBlocks; + } + + MergeOutOfPlace(array, currBlock, this->currBlockLen, lastLen, blockLen, comp); + } + else { + std::move(array + currBlock, array + (currBlock + this->currBlockLen), array + buffer); + } + } + + //TODO: Double-check "Merge Blocks" arguments + template + void CombineInPlace(RandomAccessIterator array, int firstKey, int start, int length, int subarrayLen, int blockLen, + int mergeCount, int lastSubarrays, bool buffer, Compare comp) { + int fullMerge = 2 * subarrayLen; + // SLIGHT OPTIMIZATION: 'blockCount' only needs to be calculated once for regular merges + int blockCount = fullMerge / blockLen; + + for (int mergeIndex = 0; mergeIndex < mergeCount; mergeIndex++) { + int offset = start + (mergeIndex * fullMerge); + + InsertSort(array, firstKey, blockCount, comp); + + // INCORRECT PARAMETER BUG FIXED: `block select sort` should be using `offset`, not `start` + int medianKey = subarrayLen / blockLen; + medianKey = BlockSelectSort(array, firstKey, offset, medianKey, blockCount, blockLen, comp); + + if (buffer) { + MergeBlocks(array, firstKey, firstKey + medianKey, offset, blockCount, blockLen, 0, 0, comp); + } + else { + LazyMergeBlocks(array, firstKey, firstKey + medianKey, offset, blockCount, blockLen, 0, 0, comp); + } + } + + // INCORRECT CONDITIONAL/PARAMETER BUG FIXED: Credit to 666666t for debugging. + if (lastSubarrays != 0) { + int offset = start + (mergeCount * fullMerge); + blockCount = lastSubarrays / blockLen; + + InsertSort(array, firstKey, blockCount + 1, comp); + + // INCORRECT PARAMETER BUG FIXED: `block select sort` should be using `offset`, not `start` + int medianKey = subarrayLen / blockLen; + medianKey = BlockSelectSort(array, firstKey, offset, medianKey, blockCount, blockLen, comp); + + // MISSING BOUNDS CHECK BUG FIXED: `lastFragment` *can* be 0 if the last two subarrays are evenly + // divided into blocks. This prevents Grailsort from going out-of-bounds. + int lastFragment = lastSubarrays - (blockCount * blockLen); + int lastMergeBlocks; + if (lastFragment != 0) { + lastMergeBlocks = CountLastMergeBlocks(array, offset, blockCount, blockLen, comp); + } + else { + lastMergeBlocks = 0; + } + + int smartMerges = blockCount - lastMergeBlocks; + + //TODO: Double-check if this micro-optimization works correctly like the original + if (smartMerges == 0) { + int leftLen = lastMergeBlocks * blockLen; + + // INCORRECT PARAMETER BUG FIXED: these merges should be using `offset`, not `start` + if (buffer) { + MergeForwards(array, offset, leftLen, lastFragment, blockLen, comp); + } + else { + LazyMerge(array, offset, leftLen, lastFragment, comp); + } + } + else { + if (buffer) { + MergeBlocks(array, firstKey, firstKey + medianKey, offset, + smartMerges, blockLen, lastMergeBlocks, lastFragment, comp); + } + else { + LazyMergeBlocks(array, firstKey, firstKey + medianKey, offset, + smartMerges, blockLen, lastMergeBlocks, lastFragment, comp); + } + } + } + + if (buffer) { + InPlaceBufferReset(array, start, length, blockLen); + } + } + + template + void CombineOutOfPlace(RandomAccessIterator array, int firstKey, int start, int length, int subarrayLen, int blockLen, + int mergeCount, int lastSubarrays, BufferIterator extBuffer, Compare comp) { + std::move(array + (start - blockLen), array + start, extBuffer); + + int fullMerge = 2 * subarrayLen; + // SLIGHT OPTIMIZATION: 'blockCount' only needs to be calculated once for regular merges + int blockCount = fullMerge / blockLen; + + for (int mergeIndex = 0; mergeIndex < mergeCount; mergeIndex++) { + int offset = start + (mergeIndex * fullMerge); + + InsertSort(array, firstKey, blockCount, comp); + + // INCORRECT PARAMETER BUG FIXED: `block select sort` should be using `offset`, not `start` + int medianKey = subarrayLen / blockLen; + medianKey = BlockSelectSort(array, firstKey, offset, medianKey, blockCount, blockLen, comp); + + MergeBlocksOutOfPlace(array, firstKey, firstKey + medianKey, offset, + blockCount, blockLen, 0, 0, comp); + } + + // INCORRECT CONDITIONAL/PARAMETER BUG FIXED: Credit to 666666t for debugging. + if (lastSubarrays != 0) { + int offset = start + (mergeCount * fullMerge); + blockCount = lastSubarrays / blockLen; + + InsertSort(array, firstKey, blockCount + 1, comp); + + // INCORRECT PARAMETER BUG FIXED: `block select sort` should be using `offset`, not `start` + int medianKey = subarrayLen / blockLen; + medianKey = BlockSelectSort(array, firstKey, offset, medianKey, blockCount, blockLen, comp); + + // MISSING BOUNDS CHECK BUG FIXED: `lastFragment` *can* be 0 if the last two subarrays are evenly + // divided into blocks. This prevents Grailsort from going out-of-bounds. + int lastFragment = lastSubarrays - (blockCount * blockLen); + int lastMergeBlocks; + if (lastFragment != 0) { + lastMergeBlocks = CountLastMergeBlocks(array, offset, blockCount, blockLen, comp); + } + else { + lastMergeBlocks = 0; + } + + int smartMerges = blockCount - lastMergeBlocks; + + if (smartMerges == 0) { + // MINOR CHANGE: renamed for consistency (used to be 'leftLength') + int leftLen = lastMergeBlocks * blockLen; + + // INCORRECT PARAMETER BUG FIXED: this merge should be using `offset`, not `start` + MergeOutOfPlace(array, offset, leftLen, lastFragment, blockLen, comp); + } + else { + MergeBlocksOutOfPlace(array, firstKey, firstKey + medianKey, offset, + smartMerges, blockLen, lastMergeBlocks, lastFragment, comp); + } + } + + OutOfPlaceBufferReset(array, start, length, blockLen); + std::move(extBuffer, extBuffer + blockLen, array + (start - blockLen)); + } + + // Keys are on the left side of array. Blocks of length 'subarrayLen' combined. We'll combine them in pairs + // 'subarrayLen' is a power of 2. (2 * subarrayLen / blockLen) keys are guaranteed + // + // IMPORTANT RENAMES: 'lastSubarray' is now 'lastSubarrays' because it includes the length of the last left + // subarray AND last right subarray (if there is a right subarray at all). + // + // *Please also check everything surrounding 'if(lastSubarrays != 0)' inside + // 'combine in-/out-of-place' methods for other renames!!* + template + void CombineBlocks(RandomAccessIterator array, int firstKey, int start, int length, int subarrayLen, int blockLen, + bool buffer, BufferIterator extBuffer, int extBufferLen, Compare comp) { + int fullMerge = 2 * subarrayLen; + int mergeCount = length / fullMerge; + int lastSubarrays = length - (fullMerge * mergeCount); + + if (lastSubarrays <= subarrayLen) { + length -= lastSubarrays; + lastSubarrays = 0; + } + + // INCOMPLETE CONDITIONAL BUG FIXED: In order to combine blocks out-of-place, we must check if a full-sized + // block fits into our external buffer. + if (buffer && blockLen <= extBufferLen) { + CombineOutOfPlace(array, firstKey, start, length, subarrayLen, blockLen, mergeCount, lastSubarrays, + extBuffer, comp); + } + else { + CombineInPlace(array, firstKey, start, length, subarrayLen, blockLen, + mergeCount, lastSubarrays, buffer, comp); + } + } + + // "Classic" in-place merge sort using binary searches and rotations + // + // cost: min(leftLen, rightLen)^2 + max(leftLen, rightLen) + // MINOR CHANGES: better naming -- 'insertPos' is now 'mergeLen' -- and "middle"/"end" calculations simplified + template + static void LazyMerge(RandomAccessIterator array, int start, int leftLen, int rightLen, Compare comp) { + if (leftLen < rightLen) { + int middle = start + leftLen; + + while (leftLen != 0) { + int mergeLen = BinarySearchLeft(array, middle, rightLen, array[start], comp); + + if (mergeLen != 0) { + Rotate(array, start, leftLen, mergeLen); + start += mergeLen; + rightLen -= mergeLen; + } + + middle += mergeLen; + + if (rightLen == 0) { + break; + } + else { + do { + start++; + leftLen--; + } while (leftLen != 0 && comp(array[start], array[middle]) <= 0); + } + } + } + // INDEXING BUG FIXED: Credit to Anonymous0726 for debugging. + else { + int end = start + leftLen + rightLen - 1; + + + while (rightLen != 0) { + int mergeLen = BinarySearchRight(array, start, leftLen, array[end], comp); + + if (mergeLen != leftLen) { + Rotate(array, start + mergeLen, leftLen - mergeLen, rightLen); + end -= leftLen - mergeLen; + leftLen = mergeLen; + } + + if (leftLen == 0) { + break; + } + else { + int middle = start + leftLen; + do { + rightLen--; + end--; + } while (rightLen != 0 && comp(array[middle - 1], array[end]) <= 0); + } + } + } + } + + template + static void LazyStableSort(RandomAccessIterator array, int start, int length, Compare comp) { + for (int index = 1; index < length; index += 2) { + int left = start + index - 1; + int right = start + index; + + if (comp(array[left], array[right]) > 0) { + counted_swap(array[left], array[right]); + } + } + for (int mergeLen = 2; mergeLen < length; mergeLen *= 2) { + int fullMerge = 2 * mergeLen; + + int mergeIndex; + int mergeEnd = length - fullMerge; + + for (mergeIndex = 0; mergeIndex <= mergeEnd; mergeIndex += fullMerge) { + LazyMerge(array, start + mergeIndex, mergeLen, mergeLen, comp); + } + + int leftOver = length - mergeIndex; + if (leftOver > mergeLen) { + LazyMerge(array, start + mergeIndex, mergeLen, leftOver - mergeLen, comp); + } + } + } + + // Calculates the minimum between numKeys and cbrt(2 * subarrayLen * keysFound). + // Math will be further explained later, but just like in CommonSort, this + // loop is rendered completely useless by the scrolling buffer optimization; + // minKeys will always equal numKeys. + // + // Code still here for preservation purposes. + /*static int CalcMinKeys(int numKeys, long halfSubarrKeys) { + int minKeys = 1; + while(minKeys < numKeys && halfSubarrKeys != 0) { + minKeys *= 2; + halfSubarrKeys /= 8; + } + return minKeys; + }*/ + + template + void CommonSort(RandomAccessIterator array, int start, int length, BufferIterator extBuf, int extBufLen, Compare comp) { + if (length < 16) { + InsertSort(array, start, length, comp); + return; + } + + BufferIterator extBuffer{}; + int extBufferLen = 0; + + int blockLen = 1; + + // find the smallest power of two greater than or equal to + // the square root of the input's length + while ((blockLen * blockLen) < length) { + blockLen *= 2; + } + + // '((a - 1) / b) + 1' is actually a clever and very efficient + // formula for the ceiling of (a / b) + // + // credit to Anonymous0726 for figuring this out! + int keyLen = ((length - 1) / blockLen) + 1; + + // Grailsort is hoping to find `2 * sqrt(n)` unique items + // throughout the array + int idealKeys = keyLen + blockLen; + + //TODO: Clean up `start +` offsets + int keysFound = CollectKeys(array, start, length, idealKeys, comp); + + bool idealBuffer; + if (keysFound < idealKeys) { + if (keysFound == 1) return; + if (keysFound < 4) { + // GRAILSORT STRATEGY 3 -- No block swaps or scrolling buffer; resort to Lazy Stable Sort + LazyStableSort(array, start, length, comp); + return; + } + else { + // GRAILSORT STRATEGY 2 -- Block swaps with small scrolling buffer and/or lazy merges + keyLen = blockLen; + blockLen = 0; + idealBuffer = false; + + while (keyLen > keysFound) { + keyLen /= 2; + } + } + } + else { + // GRAILSORT STRATEGY 1 -- Block swaps with scrolling buffer + idealBuffer = true; + } + + int bufferEnd = blockLen + keyLen; + int subarrayLen; + if (idealBuffer) { + subarrayLen = blockLen; + } + else { + subarrayLen = keyLen; + } + + if (idealBuffer && extBufLen != 0) { + // GRAILSORT + EXTRA SPACE + extBuffer = extBuf; + extBufferLen = extBufLen; + } + + BuildBlocks(array, start + bufferEnd, length - bufferEnd, subarrayLen, + extBuffer, extBufferLen, comp); + + while ((length - bufferEnd) > (2 * subarrayLen)) { + subarrayLen *= 2; + + int currBlockLen = blockLen; + bool scrollingBuffer = idealBuffer; + + // Huge credit to Anonymous0726, phoenixbound, and DeveloperSort for their tireless efforts + // towards deconstructing this math. + if (!idealBuffer) { + int keyBuffer = keyLen / 2; + // TODO: Rewrite explanation for this math + if (keyBuffer >= (2 * subarrayLen) / keyBuffer) { + currBlockLen = keyBuffer; + scrollingBuffer = true; + } + else { + // This is a very recent discovery, and the math will be spelled out later, but this + // "minKeys" calculation is *completely unnecessary*. "minKeys" would be less than + // "keyLen" iff keyBuffer >= (2 * subarrayLen) / keyBuffer... but this situation is + // already covered by our scrolling buffer optimization right above!! Consequently, + // "minKeys" will *always* be equal to "keyLen" when Grailsort resorts to smart lazy + // merges. Removing this loop is by itself a decent optimization, as well! + // + // Code still here for preservation purposes. + /*long halfSubarrKeys = ((long) subarrayLen * keysFound) / 2; + int minKeys = CalcMinKeys(keyLen, halfSubarrKeys);*/ + + currBlockLen = (2 * subarrayLen) / keyLen; + } + } + + // WRONG VARIABLE BUG FIXED: 4th argument should be `length - bufferEnd`, was `length - bufferLen` before. + // Credit to 666666t and Anonymous0726 for debugging. + CombineBlocks(array, start, start + bufferEnd, length - bufferEnd, + subarrayLen, currBlockLen, scrollingBuffer, + extBuffer, extBufferLen, comp); + } + + InsertSort(array, start, bufferEnd, comp); + LazyMerge(array, start, bufferEnd, length - bufferEnd, comp); + } + }; +} + +template< + typename RandomAccessIterator, + typename Compare = std::less::value_type> +> +void grailsort(RandomAccessIterator first, RandomAccessIterator last, Compare comp = {}) +{ + using value_type = typename std::iterator_traits::value_type; + + grailsort_detail::GrailSort gsort; + gsort.CommonSort(first, 0, last - first, + (value_type*)nullptr, 0, + grailsort_detail::ThreeWayCompare(std::move(comp))); +} + +template< + typename RandomAccessIterator1, + typename RandomAccessIterator2, + typename Compare = std::less::value_type> +> +void grailsort(RandomAccessIterator1 first, RandomAccessIterator1 last, + RandomAccessIterator2 buff_first, RandomAccessIterator2 buff_last, + Compare comp = {}) +{ + grailsort_detail::GrailSort gsort; + gsort.CommonSort(first, 0, last - first, + buff_first, buff_last - buff_first, + grailsort_detail::ThreeWayCompare(std::move(comp))); +} + + +void GrailSort(SortArray& A) +{ + grailsort(MyIterator(&A, 0), MyIterator(&A, A.size())); +} + +void AuxGrailSort(SortArray& A) +{ + std::vector space_buf(512, ArrayItem(0)); + grailsort(MyIterator(&A, 0), MyIterator(&A, A.size()), space_buf.begin(), space_buf.end()); +} +// **************************************************************************** diff --git a/src/algorithms/pdqsort.cpp b/src/algorithms/pdqsort.cpp new file mode 100644 index 000000000..fe2a8825f --- /dev/null +++ b/src/algorithms/pdqsort.cpp @@ -0,0 +1,542 @@ +/* + pdqsort.h - Pattern-defeating quicksort. + + Copyright (c) 2021 Orson Peters + + This software is provided 'as-is', without any express or implied warranty. In no event will the + authors be held liable for any damages arising from the use of this software. + + Permission is granted to anyone to use this software for any purpose, including commercial + applications, and to alter it and redistribute it freely, subject to the following restrictions: + + 1. The origin of this software must not be misrepresented; you must not claim that you wrote the + original software. If you use this software in a product, an acknowledgment in the product + documentation would be appreciated but is not required. + + 2. Altered source versions must be plainly marked as such, and must not be misrepresented as + being the original software. + + 3. This notice may not be removed or altered from any source distribution. +*/ + + +#ifndef PDQSORT_H +#define PDQSORT_H + +#include "../SortAlgo.h" +#include +#include +#include +#include +#include + +#if __cplusplus >= 201103L + #include + #include + #define PDQSORT_PREFER_MOVE(x) std::move(x) +#else + #define PDQSORT_PREFER_MOVE(x) (x) +#endif + + +namespace pdqsort_detail { + enum { + // Partitions below this size are sorted using insertion sort. + insertion_sort_threshold = 24, + + // Partitions above this size use Tukey's ninther to select the pivot. + ninther_threshold = 128, + + // When we detect an already sorted partition, attempt an insertion sort that allows this + // amount of element moves before giving up. + partial_insertion_sort_limit = 8, + + // Must be multiple of 8 due to loop unrolling, and < 256 to fit in unsigned char. + block_size = 64, + + // Cacheline size, assumes power of two. + cacheline_size = 64 + + }; + +#if __cplusplus >= 201103L + template struct is_default_compare : std::false_type { }; + template struct is_default_compare> : std::true_type { }; + template struct is_default_compare> : std::true_type { }; +#endif + + // Returns floor(log2(n)), assumes n > 0. + template + inline int log2(T n) { + int log = 0; + while (n >>= 1) ++log; + return log; + } + + // Sorts [begin, end) using insertion sort with the given comparison function. + template + inline void insertion_sort(Iter begin, Iter end, Compare comp) { + typedef typename std::iterator_traits::value_type T; + if (begin == end) return; + + for (Iter cur = begin + 1; cur != end; ++cur) { + Iter sift = cur; + Iter sift_1 = cur - 1; + + // Compare first so we can avoid 2 moves for an element already positioned correctly. + if (comp(*sift, *sift_1)) { + T tmp = PDQSORT_PREFER_MOVE(*sift); + + do { *sift-- = PDQSORT_PREFER_MOVE(*sift_1); } + while (sift != begin && comp(tmp, *--sift_1)); + + *sift = PDQSORT_PREFER_MOVE(tmp); + } + } + } + + // Sorts [begin, end) using insertion sort with the given comparison function. Assumes + // *(begin - 1) is an element smaller than or equal to any element in [begin, end). + template + inline void unguarded_insertion_sort(Iter begin, Iter end, Compare comp) { + typedef typename std::iterator_traits::value_type T; + if (begin == end) return; + + for (Iter cur = begin + 1; cur != end; ++cur) { + Iter sift = cur; + Iter sift_1 = cur - 1; + + // Compare first so we can avoid 2 moves for an element already positioned correctly. + if (comp(*sift, *sift_1)) { + T tmp = PDQSORT_PREFER_MOVE(*sift); + + do { *sift-- = PDQSORT_PREFER_MOVE(*sift_1); } + while (comp(tmp, *--sift_1)); + + *sift = PDQSORT_PREFER_MOVE(tmp); + } + } + } + + // Attempts to use insertion sort on [begin, end). Will return false if more than + // partial_insertion_sort_limit elements were moved, and abort sorting. Otherwise it will + // successfully sort and return true. + template + inline bool partial_insertion_sort(Iter begin, Iter end, Compare comp) { + typedef typename std::iterator_traits::value_type T; + if (begin == end) return true; + + std::size_t limit = 0; + for (Iter cur = begin + 1; cur != end; ++cur) { + Iter sift = cur; + Iter sift_1 = cur - 1; + + // Compare first so we can avoid 2 moves for an element already positioned correctly. + if (comp(*sift, *sift_1)) { + T tmp = PDQSORT_PREFER_MOVE(*sift); + + do { *sift-- = PDQSORT_PREFER_MOVE(*sift_1); } + while (sift != begin && comp(tmp, *--sift_1)); + + *sift = PDQSORT_PREFER_MOVE(tmp); + limit += cur - sift; + } + + if (limit > partial_insertion_sort_limit) return false; + } + + return true; + } + + template + inline void sort2(Iter a, Iter b, Compare comp) { + if (comp(*b, *a)) counted_iter_swap(a, b); + } + + // Sorts the elements *a, *b and *c using comparison function comp. + template + inline void sort3(Iter a, Iter b, Iter c, Compare comp) { + sort2(a, b, comp); + sort2(b, c, comp); + sort2(a, b, comp); + } + + template + inline T* align_cacheline(T* p) { +#if defined(UINTPTR_MAX) && __cplusplus >= 201103L + std::uintptr_t ip = reinterpret_cast(p); +#else + std::size_t ip = reinterpret_cast(p); +#endif + ip = (ip + cacheline_size - 1) & -cacheline_size; + return reinterpret_cast(ip); + } + + template + inline void swap_offsets(Iter first, Iter last, + unsigned char* offsets_l, unsigned char* offsets_r, + size_t num, bool use_swaps) { + typedef typename std::iterator_traits::value_type T; + if (use_swaps) { + // This case is needed for the descending distribution, where we need + // to have proper swapping for pdqsort to remain O(n). + for (size_t i = 0; i < num; ++i) { + counted_iter_swap(first + offsets_l[i], last - offsets_r[i]); + } + } else if (num > 0) { + Iter l = first + offsets_l[0]; Iter r = last - offsets_r[0]; + T tmp(PDQSORT_PREFER_MOVE(*l)); *l = PDQSORT_PREFER_MOVE(*r); + for (size_t i = 1; i < num; ++i) { + l = first + offsets_l[i]; *r = PDQSORT_PREFER_MOVE(*l); + r = last - offsets_r[i]; *l = PDQSORT_PREFER_MOVE(*r); + } + *r = PDQSORT_PREFER_MOVE(tmp); + } + } + + // Partitions [begin, end) around pivot *begin using comparison function comp. Elements equal + // to the pivot are put in the right-hand partition. Returns the position of the pivot after + // partitioning and whether the passed sequence already was correctly partitioned. Assumes the + // pivot is a median of at least 3 elements and that [begin, end) is at least + // insertion_sort_threshold long. Uses branchless partitioning. + template + inline std::pair partition_right_branchless(Iter begin, Iter end, Compare comp) { + typedef typename std::iterator_traits::value_type T; + + // Move pivot into local for speed. + T pivot(PDQSORT_PREFER_MOVE(*begin)); + Iter first = begin; + Iter last = end; + + // Find the first element greater than or equal than the pivot (the median of 3 guarantees + // this exists). + while (comp(*++first, pivot)); + + // Find the first element strictly smaller than the pivot. We have to guard this search if + // there was no element before *first. + if (first - 1 == begin) while (first < last && !comp(*--last, pivot)); + else while ( !comp(*--last, pivot)); + + // If the first pair of elements that should be swapped to partition are the same element, + // the passed in sequence already was correctly partitioned. + bool already_partitioned = first >= last; + if (!already_partitioned) { + counted_iter_swap(first, last); + ++first; + + // The following branchless partitioning is derived from "BlockQuicksort: How Branch + // Mispredictions don’t affect Quicksort" by Stefan Edelkamp and Armin Weiss, but + // heavily micro-optimized. + unsigned char offsets_l_storage[block_size + cacheline_size]; + unsigned char offsets_r_storage[block_size + cacheline_size]; + unsigned char* offsets_l = align_cacheline(offsets_l_storage); + unsigned char* offsets_r = align_cacheline(offsets_r_storage); + + Iter offsets_l_base = first; + Iter offsets_r_base = last; + size_t num_l, num_r, start_l, start_r; + num_l = num_r = start_l = start_r = 0; + + while (first < last) { + // Fill up offset blocks with elements that are on the wrong side. + // First we determine how much elements are considered for each offset block. + size_t num_unknown = last - first; + size_t left_split = num_l == 0 ? (num_r == 0 ? num_unknown / 2 : num_unknown) : 0; + size_t right_split = num_r == 0 ? (num_unknown - left_split) : 0; + + // Fill the offset blocks. + if (left_split >= block_size) { + for (size_t i = 0; i < block_size;) { + offsets_l[num_l] = i++; num_l += !comp(*first, pivot); ++first; + offsets_l[num_l] = i++; num_l += !comp(*first, pivot); ++first; + offsets_l[num_l] = i++; num_l += !comp(*first, pivot); ++first; + offsets_l[num_l] = i++; num_l += !comp(*first, pivot); ++first; + offsets_l[num_l] = i++; num_l += !comp(*first, pivot); ++first; + offsets_l[num_l] = i++; num_l += !comp(*first, pivot); ++first; + offsets_l[num_l] = i++; num_l += !comp(*first, pivot); ++first; + offsets_l[num_l] = i++; num_l += !comp(*first, pivot); ++first; + } + } else { + for (size_t i = 0; i < left_split;) { + offsets_l[num_l] = i++; num_l += !comp(*first, pivot); ++first; + } + } + + if (right_split >= block_size) { + for (size_t i = 0; i < block_size;) { + offsets_r[num_r] = ++i; num_r += comp(*--last, pivot); + offsets_r[num_r] = ++i; num_r += comp(*--last, pivot); + offsets_r[num_r] = ++i; num_r += comp(*--last, pivot); + offsets_r[num_r] = ++i; num_r += comp(*--last, pivot); + offsets_r[num_r] = ++i; num_r += comp(*--last, pivot); + offsets_r[num_r] = ++i; num_r += comp(*--last, pivot); + offsets_r[num_r] = ++i; num_r += comp(*--last, pivot); + offsets_r[num_r] = ++i; num_r += comp(*--last, pivot); + } + } else { + for (size_t i = 0; i < right_split;) { + offsets_r[num_r] = ++i; num_r += comp(*--last, pivot); + } + } + + // Swap elements and update block sizes and first/last boundaries. + size_t num = std::min(num_l, num_r); + swap_offsets(offsets_l_base, offsets_r_base, + offsets_l + start_l, offsets_r + start_r, + num, num_l == num_r); + num_l -= num; num_r -= num; + start_l += num; start_r += num; + + if (num_l == 0) { + start_l = 0; + offsets_l_base = first; + } + + if (num_r == 0) { + start_r = 0; + offsets_r_base = last; + } + } + + // We have now fully identified [first, last)'s proper position. Swap the last elements. + if (num_l) { + offsets_l += start_l; + while (num_l--) counted_iter_swap(offsets_l_base + offsets_l[num_l], --last); + first = last; + } + if (num_r) { + offsets_r += start_r; + while (num_r--) counted_iter_swap(offsets_r_base - offsets_r[num_r], first), ++first; + last = first; + } + } + + // Put the pivot in the right place. + Iter pivot_pos = first - 1; + *begin = PDQSORT_PREFER_MOVE(*pivot_pos); + *pivot_pos = PDQSORT_PREFER_MOVE(pivot); + + return std::make_pair(pivot_pos, already_partitioned); + } + + + + // Partitions [begin, end) around pivot *begin using comparison function comp. Elements equal + // to the pivot are put in the right-hand partition. Returns the position of the pivot after + // partitioning and whether the passed sequence already was correctly partitioned. Assumes the + // pivot is a median of at least 3 elements and that [begin, end) is at least + // insertion_sort_threshold long. + template + inline std::pair partition_right(Iter begin, Iter end, Compare comp) { + typedef typename std::iterator_traits::value_type T; + + // Move pivot into local for speed. + T pivot(PDQSORT_PREFER_MOVE(*begin)); + + Iter first = begin; + Iter last = end; + + // Find the first element greater than or equal than the pivot (the median of 3 guarantees + // this exists). + while (comp(*++first, pivot)); + + // Find the first element strictly smaller than the pivot. We have to guard this search if + // there was no element before *first. + if (first - 1 == begin) while (first < last && !comp(*--last, pivot)); + else while ( !comp(*--last, pivot)); + + // If the first pair of elements that should be swapped to partition are the same element, + // the passed in sequence already was correctly partitioned. + bool already_partitioned = first >= last; + + // Keep swapping pairs of elements that are on the wrong side of the pivot. Previously + // swapped pairs guard the searches, which is why the first iteration is special-cased + // above. + while (first < last) { + counted_iter_swap(first, last); + while (comp(*++first, pivot)); + while (!comp(*--last, pivot)); + } + + // Put the pivot in the right place. + Iter pivot_pos = first - 1; + *begin = PDQSORT_PREFER_MOVE(*pivot_pos); + *pivot_pos = PDQSORT_PREFER_MOVE(pivot); + + return std::make_pair(pivot_pos, already_partitioned); + } + + // Similar function to the one above, except elements equal to the pivot are put to the left of + // the pivot and it doesn't check or return if the passed sequence already was partitioned. + // Since this is rarely used (the many equal case), and in that case pdqsort already has O(n) + // performance, no block quicksort is applied here for simplicity. + template + inline Iter partition_left(Iter begin, Iter end, Compare comp) { + typedef typename std::iterator_traits::value_type T; + + T pivot(PDQSORT_PREFER_MOVE(*begin)); + Iter first = begin; + Iter last = end; + + while (comp(pivot, *--last)); + + if (last + 1 == end) while (first < last && !comp(pivot, *++first)); + else while ( !comp(pivot, *++first)); + + while (first < last) { + counted_iter_swap(first, last); + while (comp(pivot, *--last)); + while (!comp(pivot, *++first)); + } + + Iter pivot_pos = last; + *begin = PDQSORT_PREFER_MOVE(*pivot_pos); + *pivot_pos = PDQSORT_PREFER_MOVE(pivot); + + return pivot_pos; + } + + + template + inline void pdqsort_loop(Iter begin, Iter end, Compare comp, int bad_allowed, bool leftmost = true) { + typedef typename std::iterator_traits::difference_type diff_t; + + // Use a while loop for tail recursion elimination. + while (true) { + diff_t size = end - begin; + + // Insertion sort is faster for small arrays. + if (size < insertion_sort_threshold) { + if (leftmost) insertion_sort(begin, end, comp); + else unguarded_insertion_sort(begin, end, comp); + return; + } + + // Choose pivot as median of 3 or pseudomedian of 9. + diff_t s2 = size / 2; + if (size > ninther_threshold) { + sort3(begin, begin + s2, end - 1, comp); + sort3(begin + 1, begin + (s2 - 1), end - 2, comp); + sort3(begin + 2, begin + (s2 + 1), end - 3, comp); + sort3(begin + (s2 - 1), begin + s2, begin + (s2 + 1), comp); + counted_iter_swap(begin, begin + s2); + } else sort3(begin + s2, begin, end - 1, comp); + + // If *(begin - 1) is the end of the right partition of a previous partition operation + // there is no element in [begin, end) that is smaller than *(begin - 1). Then if our + // pivot compares equal to *(begin - 1) we change strategy, putting equal elements in + // the left partition, greater elements in the right partition. We do not have to + // recurse on the left partition, since it's sorted (all equal). + if (!leftmost && !comp(*(begin - 1), *begin)) { + begin = partition_left(begin, end, comp) + 1; + continue; + } + + // Partition and get results. + std::pair part_result = + Branchless ? partition_right_branchless(begin, end, comp) + : partition_right(begin, end, comp); + Iter pivot_pos = part_result.first; + bool already_partitioned = part_result.second; + + // Check for a highly unbalanced partition. + diff_t l_size = pivot_pos - begin; + diff_t r_size = end - (pivot_pos + 1); + bool highly_unbalanced = l_size < size / 8 || r_size < size / 8; + + // If we got a highly unbalanced partition we shuffle elements to break many patterns. + if (highly_unbalanced) { + // If we had too many bad partitions, switch to heapsort to guarantee O(n log n). + if (--bad_allowed == 0) { + counted_make_heap(begin, end); + counted_sort_heap(begin, end); + return; + } + + if (l_size >= insertion_sort_threshold) { + counted_iter_swap(begin, begin + l_size / 4); + counted_iter_swap(pivot_pos - 1, pivot_pos - l_size / 4); + + if (l_size > ninther_threshold) { + counted_iter_swap(begin + 1, begin + (l_size / 4 + 1)); + counted_iter_swap(begin + 2, begin + (l_size / 4 + 2)); + counted_iter_swap(pivot_pos - 2, pivot_pos - (l_size / 4 + 1)); + counted_iter_swap(pivot_pos - 3, pivot_pos - (l_size / 4 + 2)); + } + } + + if (r_size >= insertion_sort_threshold) { + counted_iter_swap(pivot_pos + 1, pivot_pos + (1 + r_size / 4)); + counted_iter_swap(end - 1, end - r_size / 4); + + if (r_size > ninther_threshold) { + counted_iter_swap(pivot_pos + 2, pivot_pos + (2 + r_size / 4)); + counted_iter_swap(pivot_pos + 3, pivot_pos + (3 + r_size / 4)); + counted_iter_swap(end - 2, end - (1 + r_size / 4)); + counted_iter_swap(end - 3, end - (2 + r_size / 4)); + } + } + } else { + // If we were decently balanced and we tried to sort an already partitioned + // sequence try to use insertion sort. + if (already_partitioned && partial_insertion_sort(begin, pivot_pos, comp) + && partial_insertion_sort(pivot_pos + 1, end, comp)) return; + } + + // Sort the left partition first using recursion and do tail recursion elimination for + // the right-hand partition. + pdqsort_loop(begin, pivot_pos, comp, bad_allowed, leftmost); + begin = pivot_pos + 1; + leftmost = false; + } + } +} + + +template +inline void pdqsort(Iter begin, Iter end, Compare comp) { + if (begin == end) return; + +#if __cplusplus >= 201103L + pdqsort_detail::pdqsort_loop::type>::value && + std::is_arithmetic::value_type>::value>( + begin, end, comp, pdqsort_detail::log2(end - begin)); +#else + pdqsort_detail::pdqsort_loop( + begin, end, comp, pdqsort_detail::log2(end - begin)); +#endif +} + +template +inline void pdqsort(Iter begin, Iter end) { + typedef typename std::iterator_traits::value_type T; + pdqsort(begin, end, std::less()); +} + +template +inline void pdqsort_branchless(Iter begin, Iter end, Compare comp) { + if (begin == end) return; + pdqsort_detail::pdqsort_loop( + begin, end, comp, pdqsort_detail::log2(end - begin)); +} + +template +inline void pdqsort_branchless(Iter begin, Iter end) { + typedef typename std::iterator_traits::value_type T; + pdqsort_branchless(begin, end, std::less()); +} + +void PDQSort(SortArray& A) +{ + pdqsort(MyIterator(&A, 0), MyIterator(&A, A.size())); +} + +void PDQSortBranchless(SortArray& A) +{ + pdqsort_branchless(MyIterator(&A, 0), MyIterator(&A, A.size())); +} + +#undef PDQSORT_PREFER_MOVE + +#endif diff --git a/src/algorithms/timsort.cpp b/src/algorithms/timsort.cpp index 215a05cbb..eb7de6561 100644 --- a/src/algorithms/timsort.cpp +++ b/src/algorithms/timsort.cpp @@ -200,7 +200,7 @@ class TimSort while(runHi < hi && compare.lt(*runHi, *(runHi - 1))) { ++runHi; } - std::reverse(lo, runHi); + counted_reverse(lo, runHi); } else { // ascending while(runHi < hi && compare.ge(*runHi, *(runHi - 1))) { diff --git a/src/algorithms/wikisort.cpp b/src/algorithms/wikisort.cpp index 78a10b477..038df4418 100644 --- a/src/algorithms/wikisort.cpp +++ b/src/algorithms/wikisort.cpp @@ -73,14 +73,14 @@ size_t FloorPowerOfTwo (const size_t value) { template void InsertionSort(Iterator begin, Iterator end, const Comparison compare) { for (Iterator it = begin; it != end; ++it) { - std::rotate(std::upper_bound(begin, it, *it, compare), it, it+1); + counted_rotate(std::upper_bound(begin, it, *it, compare), it, it+1); } } // swap a series of values in the array template void BlockSwap(Iterator start1, Iterator start2, const size_t block_size) { - std::swap_ranges(start1, start1 + block_size, start2); + counted_swap_ranges(start1, start1 + block_size, start2); } // rotate the values in an array ([0 1 2 3] becomes [1 2 3 0] if we rotate by 1) @@ -117,7 +117,7 @@ void Rotate(Iterator begin, Iterator end, const ssize_t amount, } } - std::rotate(begin, split, end); + counted_rotate(begin, split, end); } // standard merge operation using an internal or external buffer @@ -161,12 +161,12 @@ void Merge(const RangeI& buffer, const RangeI& A, const Rang if (B.length() > 0 && A.length() > 0) { while (true) { if (!compare(*B_index, *A_index)) { - std::swap(*insert_index, *A_index); + counted_swap(*insert_index, *A_index); A_index++; insert_index++; if (A_index == A_last) break; } else { - std::swap(*insert_index, *B_index); + counted_swap(*insert_index, *B_index); B_index++; insert_index++; if (B_index == B_last) break; @@ -174,7 +174,7 @@ void Merge(const RangeI& buffer, const RangeI& A, const Rang } } - std::swap_ranges(A_index, A_last, insert_index); + counted_swap_ranges(A_index, A_last, insert_index); } } @@ -438,7 +438,7 @@ void Sort(Iterator first, Iterator last, const Comparison compare) // swap the second value of each A block with the value in buffer1 for (Iterator index = buffer1.start, indexA = firstA.end + 1; indexA < blockA.end; index++, indexA += block_size) - std::swap(*index, *indexA); + counted_swap(*index, *indexA); // start rolling the A blocks through the B blocks! // whenever we leave an A block behind, we'll need to merge the previous A block with any B blocks that follow it, so track that information as well @@ -453,7 +453,7 @@ void Sort(Iterator first, Iterator last, const Comparison compare) if (lastA.length() <= cache_size) std::copy(lastA.start, lastA.end, cache); else - std::swap_ranges(lastA.start, lastA.end, buffer2.start); + counted_swap_ranges(lastA.start, lastA.end, buffer2.start); while (true) { // if there's a previous B block and the first value of the minimum A block is <= the last value of the previous B block, @@ -468,7 +468,7 @@ void Sort(Iterator first, Iterator last, const Comparison compare) // we need to swap the second item of the previous A block back with its original value, which is stored in buffer1 // since the firstA block did not have its value swapped out, we need to make sure the previous A block is not unevenly sized - std::swap(*(blockA.start + 1), *(indexA++)); + counted_swap(*(blockA.start + 1), *(indexA++)); // locally merge the previous A block with the B values that follow it, using the buffer as swap space Merge(buffer2, lastA, Range(lastA.end, B_split), compare, cache, cache_size); diff --git a/src/wxg/WAbout_wxg.cpp b/src/wxg/WAbout_wxg.cpp index e581bbc47..588aa2284 100644 --- a/src/wxg/WAbout_wxg.cpp +++ b/src/wxg/WAbout_wxg.cpp @@ -34,7 +34,11 @@ void WAbout_wxg::set_properties() { // begin wxGlade: WAbout_wxg::set_properties SetTitle(_("About")); - labelTitle->SetFont(wxFont(14, wxDEFAULT, wxNORMAL, wxNORMAL, 0, wxT(""))); + #if wxCHECK_VERSION(3, 1, 6) + labelTitle->SetFont(wxFont(14, wxFONTFAMILY_DEFAULT, wxFONTSTYLE_NORMAL, wxFONTWEIGHT_NORMAL)); + #else + labelTitle->SetFont(wxFont(14, wxDEFAULT, wxNORMAL, wxNORMAL, 0, wxT(""))); + #endif // end wxGlade } diff --git a/src/wxg/WMain_wxg.cpp b/src/wxg/WMain_wxg.cpp index b2f4bdba5..c9cf11bbf 100644 --- a/src/wxg/WMain_wxg.cpp +++ b/src/wxg/WMain_wxg.cpp @@ -42,9 +42,10 @@ WMain_wxg::WMain_wxg(wxWindow* parent, int id, const wxString& title, const wxPo labelAccessCount = new wxClickText(splitter_0_pane_2, wxID_ANY, wxEmptyString); labelInversionCount = new wxClickText(splitter_0_pane_2, ID_INVERSION_LABEL, wxEmptyString); labelRunsCount = new wxClickText(splitter_0_pane_2, wxID_ANY, wxEmptyString); + labelSwapsCount = new wxClickText(splitter_0_pane_2, wxID_ANY, wxEmptyString); const wxString *inputTypeChoice_choices = NULL; inputTypeChoice = new wxChoice(splitter_0_pane_2, wxID_ANY, wxDefaultPosition, wxDefaultSize, 0, inputTypeChoice_choices, 0); - arraySizeSlider = new wxSlider(splitter_0_pane_2, ID_ARRAY_SIZE_SLIDER, 0, 1, 2048); + arraySizeSlider = new wxSlider(splitter_0_pane_2, ID_ARRAY_SIZE_SLIDER, 0, 1, 16384); labelArraySizeValue = new wxStaticText(splitter_0_pane_2, wxID_ANY, _("1024"), wxDefaultPosition, wxDefaultSize, wxALIGN_CENTRE|wxST_NO_AUTORESIZE); const wxString *algoList_choices = NULL; algoList = new wxListBox(splitter_0_pane_2, ID_ALGO_LIST, wxDefaultPosition, wxDefaultSize, 0, algoList_choices, wxLB_SINGLE); @@ -65,9 +66,11 @@ void WMain_wxg::set_properties() SetTitle(_("The Sound of Sorting - http://panthema.net/2013/sound-of-sorting")); sortview->SetMinSize(wxSize(640, 480)); speedSlider->SetToolTip(_("Changes the animation speed by setting the delay for each array access.")); + arraySizeSlider->SetToolTip(_("Resize the options panel if the visualized array doesn't seem to update.")); soundSustainSlider->SetToolTip(_("Changes the duration of each access sound as a multiple of the delay.")); labelInversionCount->SetToolTip(_("Current number of inversions. Click to enable or disable.")); labelRunsCount->SetToolTip(_("Current number of runs.")); + labelSwapsCount->SetToolTip(_("Current number of swaps.")); panelQuickSortPivot->Hide(); splitter_0->SetSashGravity(1.0); // end wxGlade @@ -87,7 +90,7 @@ void WMain_wxg::do_layout() sizer_3_staticbox->Lower(); wxStaticBoxSizer* sizer_3 = new wxStaticBoxSizer(sizer_3_staticbox, wxVERTICAL); wxBoxSizer* sizerAnimationControls = new wxBoxSizer(wxVERTICAL); - wxFlexGridSizer* grid_sizer_3 = new wxFlexGridSizer(7, 2, 0, 0); + wxFlexGridSizer* grid_sizer_3 = new wxFlexGridSizer(8, 2, 0, 0); wxBoxSizer* sizerSoundSustain = new wxBoxSizer(wxHORIZONTAL); wxGridSizer* grid_sizer_2 = new wxGridSizer(3, 2, 0, 0); wxBoxSizer* sizer_1 = new wxBoxSizer(wxHORIZONTAL); @@ -124,6 +127,11 @@ void WMain_wxg::do_layout() wxStaticText* labelRuns = new wxStaticText(splitter_0_pane_2, wxID_ANY, _("Runs: ")); grid_sizer_3->Add(labelRuns, 0, wxTOP|wxBOTTOM|wxALIGN_CENTER_VERTICAL, 4); grid_sizer_3->Add(labelRunsCount, 0, wxTOP|wxBOTTOM|wxEXPAND|wxALIGN_CENTER_VERTICAL, 4); + + wxStaticText* labelSwaps = new wxStaticText(splitter_0_pane_2, wxID_ANY, _("Swaps: ")); + grid_sizer_3->Add(labelSwaps, 0, wxTOP | wxBOTTOM | wxALIGN_CENTER_VERTICAL, 4); + grid_sizer_3->Add(labelSwapsCount, 0, wxTOP | wxBOTTOM | wxEXPAND | wxALIGN_CENTER_VERTICAL, 4); + grid_sizer_3->AddGrowableCol(1); sizerAnimationControls->Add(grid_sizer_3, 1, wxEXPAND, 0); sizer_3->Add(sizerAnimationControls, 1, wxEXPAND, 0); @@ -135,6 +143,7 @@ void WMain_wxg::do_layout() grid_sizer_1->Add(labelArraySize, 0, wxTOP|wxBOTTOM|wxALIGN_CENTER_VERTICAL, 4); sizer_6->Add(arraySizeSlider, 1, wxTOP|wxBOTTOM|wxALIGN_CENTER_VERTICAL, 4); sizer_6->Add(labelArraySizeValue, 0, wxLEFT|wxTOP|wxBOTTOM|wxALIGN_CENTER_VERTICAL|wxFIXED_MINSIZE, 4); + labelArraySizeValue->SetMinSize(wxSize(39, -1)); grid_sizer_1->Add(sizer_6, 1, wxEXPAND|wxALIGN_CENTER_VERTICAL, 0); grid_sizer_1->AddGrowableCol(1); sizerInputAlgorithm->Add(grid_sizer_1, 0, wxEXPAND, 0); diff --git a/src/wxg/WMain_wxg.h b/src/wxg/WMain_wxg.h index 7d433842d..cf58d0dd7 100644 --- a/src/wxg/WMain_wxg.h +++ b/src/wxg/WMain_wxg.h @@ -79,6 +79,7 @@ class WMain_wxg: public wxFrame { wxClickText* labelAccessCount; wxClickText* labelInversionCount; wxClickText* labelRunsCount; + wxClickText* labelSwapsCount; wxChoice* inputTypeChoice; wxSlider* arraySizeSlider; wxStaticText* labelArraySizeValue;